fix: user avatar mime change from gif to avif
This commit is contained in:
parent
ffdb767518
commit
6e0f2985b9
20 changed files with 219 additions and 104 deletions
|
@ -183,6 +183,8 @@ version = "1.0.0"
|
||||||
"mod_panel:label.create_warning" = "Create warning"
|
"mod_panel:label.create_warning" = "Create warning"
|
||||||
"mod_panel:label.associations" = "Associations"
|
"mod_panel:label.associations" = "Associations"
|
||||||
"mod_panel:label.invited_by" = "Invited by"
|
"mod_panel:label.invited_by" = "Invited by"
|
||||||
|
"mod_panel:label.send_debug_payload" = "Send debug payload"
|
||||||
|
"mod_panel:action.send" = "Send"
|
||||||
|
|
||||||
"requests:label.requests" = "Requests"
|
"requests:label.requests" = "Requests"
|
||||||
"requests:label.community_join_request" = "Community join request"
|
"requests:label.community_join_request" = "Community join request"
|
||||||
|
|
|
@ -90,7 +90,7 @@
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
.then((res) => res.json())
|
.then((res) => res.json())
|
||||||
.then((res) => {
|
.then(async (res) => {
|
||||||
trigger(\"atto::toast\", [
|
trigger(\"atto::toast\", [
|
||||||
res.ok ? \"success\" : \"error\",
|
res.ok ? \"success\" : \"error\",
|
||||||
res.message,
|
res.message,
|
||||||
|
@ -98,7 +98,7 @@
|
||||||
|
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
// update tokens
|
// update tokens
|
||||||
const new_tokens = ns(\"me\").LOGIN_ACCOUNT_TOKENS;
|
const new_tokens = (await ns(\"me\")).LOGIN_ACCOUNT_TOKENS;
|
||||||
new_tokens[e.target.username.value] = res.message;
|
new_tokens[e.target.username.value] = res.message;
|
||||||
trigger(\"me::set_login_account_tokens\", [new_tokens]);
|
trigger(\"me::set_login_account_tokens\", [new_tokens]);
|
||||||
|
|
||||||
|
|
|
@ -107,7 +107,7 @@
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
.then((res) => res.json())
|
.then((res) => res.json())
|
||||||
.then((res) => {
|
.then(async (res) => {
|
||||||
trigger(\"atto::toast\", [
|
trigger(\"atto::toast\", [
|
||||||
res.ok ? \"success\" : \"error\",
|
res.ok ? \"success\" : \"error\",
|
||||||
res.message,
|
res.message,
|
||||||
|
@ -115,7 +115,7 @@
|
||||||
|
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
// update tokens
|
// update tokens
|
||||||
const new_tokens = ns(\"me\").LOGIN_ACCOUNT_TOKENS;
|
const new_tokens = (await ns(\"me\")).LOGIN_ACCOUNT_TOKENS;
|
||||||
new_tokens[e.target.username.value] = res.message;
|
new_tokens[e.target.username.value] = res.message;
|
||||||
trigger(\"me::set_login_account_tokens\", [new_tokens]);
|
trigger(\"me::set_login_account_tokens\", [new_tokens]);
|
||||||
|
|
||||||
|
|
|
@ -56,8 +56,8 @@
|
||||||
|
|
||||||
; random js
|
; random js
|
||||||
(text "<script data-turbo-permanent=\"true\" id=\"init-script\">
|
(text "<script data-turbo-permanent=\"true\" id=\"init-script\">
|
||||||
document.documentElement.addEventListener(\"turbo:load\", () => {
|
document.documentElement.addEventListener(\"turbo:load\", async () => {
|
||||||
const atto = ns(\"atto\");
|
const atto = await ns(\"atto\");
|
||||||
|
|
||||||
if (!atto) {
|
if (!atto) {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
|
|
|
@ -572,9 +572,9 @@
|
||||||
(text "{%- endif %}"))
|
(text "{%- endif %}"))
|
||||||
|
|
||||||
(script
|
(script
|
||||||
(text "setTimeout(() => {
|
(text "setTimeout(async () => {
|
||||||
const element = document.getElementById(\"membership_info\");
|
const element = document.getElementById(\"membership_info\");
|
||||||
const ui = ns(\"ui\");
|
const ui = await ns(\"ui\");
|
||||||
|
|
||||||
const uid = new URLSearchParams(window.location.search).get(\"uid\");
|
const uid = new URLSearchParams(window.location.search).get(\"uid\");
|
||||||
if (uid) {
|
if (uid) {
|
||||||
|
@ -665,7 +665,7 @@
|
||||||
`/api/v1/communities/{{ community.id }}/memberships/${e.target.uid.value}`,
|
`/api/v1/communities/{{ community.id }}/memberships/${e.target.uid.value}`,
|
||||||
)
|
)
|
||||||
.then((res) => res.json())
|
.then((res) => res.json())
|
||||||
.then((res) => {
|
.then(async (res) => {
|
||||||
trigger(\"atto::toast\", [
|
trigger(\"atto::toast\", [
|
||||||
res.ok ? \"success\" : \"error\",
|
res.ok ? \"success\" : \"error\",
|
||||||
res.message,
|
res.message,
|
||||||
|
@ -676,7 +676,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// permissions manager
|
// permissions manager
|
||||||
const get_permissions_html = trigger(
|
const get_permissions_html = await trigger(
|
||||||
\"ui::generate_permissions_ui\",
|
\"ui::generate_permissions_ui\",
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
@ -750,8 +750,8 @@
|
||||||
(text "{{ community.context|json_encode()|safe }}"))
|
(text "{{ community.context|json_encode()|safe }}"))
|
||||||
|
|
||||||
(script
|
(script
|
||||||
(text "setTimeout(() => {
|
(text "setTimeout(async () => {
|
||||||
const ui = ns(\"ui\");
|
const ui = await ns(\"ui\");
|
||||||
const settings = JSON.parse(
|
const settings = JSON.parse(
|
||||||
document.getElementById(\"settings_json\").innerHTML,
|
document.getElementById(\"settings_json\").innerHTML,
|
||||||
);
|
);
|
||||||
|
|
|
@ -761,8 +761,8 @@
|
||||||
(text "{{ text \"communities:action.remove_drawing\" }}"))
|
(text "{{ text \"communities:action.remove_drawing\" }}"))
|
||||||
|
|
||||||
(script
|
(script
|
||||||
(text "globalThis.attach_drawing = () => {
|
(text "globalThis.attach_drawing = async () => {
|
||||||
globalThis.gerald = trigger(\"carp::new\", [document.querySelector(\"[ui_ident=carp_canvas_field]\")]);
|
globalThis.gerald = await trigger(\"carp::new\", [document.querySelector(\"[ui_ident=carp_canvas_field]\")]);
|
||||||
globalThis.gerald.create_canvas();
|
globalThis.gerald.create_canvas();
|
||||||
|
|
||||||
document.querySelector(\"[ui_ident=add_drawing]\").classList.add(\"hidden\");
|
document.querySelector(\"[ui_ident=add_drawing]\").classList.add(\"hidden\");
|
||||||
|
|
|
@ -68,8 +68,8 @@
|
||||||
(text "Unban"))
|
(text "Unban"))
|
||||||
(text "{%- endif %}")))
|
(text "{%- endif %}")))
|
||||||
(script
|
(script
|
||||||
(text "setTimeout(() => {
|
(text "setTimeout(async () => {
|
||||||
const ui = ns(\"ui\");
|
const ui = await ns(\"ui\");
|
||||||
const element = document.getElementById(\"mod_options\");
|
const element = document.getElementById(\"mod_options\");
|
||||||
|
|
||||||
async function profile_request(do_confirm, path, body) {
|
async function profile_request(do_confirm, path, body) {
|
||||||
|
@ -216,6 +216,52 @@
|
||||||
("class" "card lowered flex flex-wrap gap-2")
|
("class" "card lowered flex flex-wrap gap-2")
|
||||||
(text "{{ components::user_plate(user=invite[0], show_menu=false) }}")))
|
(text "{{ components::user_plate(user=invite[0], show_menu=false) }}")))
|
||||||
(text "{%- endif %}")
|
(text "{%- endif %}")
|
||||||
|
(div
|
||||||
|
("class" "card-nest w-full")
|
||||||
|
(div
|
||||||
|
("class" "card small flex items-center justify-between gap-2")
|
||||||
|
(div
|
||||||
|
("class" "flex items-center gap-2")
|
||||||
|
(text "{{ icon \"square-function\" }}")
|
||||||
|
(span
|
||||||
|
(text "{{ text \"mod_panel:label.send_debug_payload\" }}"))))
|
||||||
|
(form
|
||||||
|
("class" "card flex flex-col gap-2")
|
||||||
|
("onsubmit" "send_debug_payload(event)")
|
||||||
|
(div
|
||||||
|
("class" "flex flex-col gap-1")
|
||||||
|
(label
|
||||||
|
("for" "title")
|
||||||
|
(text "{{ text \"communities:label.content\" }}"))
|
||||||
|
(textarea
|
||||||
|
("type" "text")
|
||||||
|
("name" "content")
|
||||||
|
("id" "content")
|
||||||
|
("placeholder" "javascript content")
|
||||||
|
("required" "")
|
||||||
|
("minlength" "2")))
|
||||||
|
(button
|
||||||
|
("class" "primary")
|
||||||
|
(text "{{ text \"mod_panel:action.send\" }}"))))
|
||||||
|
(script
|
||||||
|
(text "globalThis.send_debug_payload = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (
|
||||||
|
!(await trigger(\"atto::confirm\", [
|
||||||
|
\"Are you sure you would like to do this? This will only work if the user is online.\",
|
||||||
|
]))
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await trigger(
|
||||||
|
\"streams::send_packet_to\",
|
||||||
|
[\"{{ profile.id }}\", \"notifs\", { Forward: \"Javascript\" }, { js: e.target.content.value }]
|
||||||
|
);
|
||||||
|
|
||||||
|
trigger(\"atto::toast\", [res.ok ? \"success\" : \"error\", res.message]);
|
||||||
|
}"))
|
||||||
(div
|
(div
|
||||||
("class" "card-nest w-full")
|
("class" "card-nest w-full")
|
||||||
(div
|
(div
|
||||||
|
@ -235,8 +281,8 @@
|
||||||
("class" "card lowered flex flex-col gap-2")
|
("class" "card lowered flex flex-col gap-2")
|
||||||
("id" "permission_builder")))
|
("id" "permission_builder")))
|
||||||
(script
|
(script
|
||||||
(text "setTimeout(() => {
|
(text "setTimeout(async () => {
|
||||||
const get_permissions_html = trigger(
|
const get_permissions_html = await trigger(
|
||||||
\"ui::generate_permissions_ui\",
|
\"ui::generate_permissions_ui\",
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
|
|
@ -145,8 +145,8 @@
|
||||||
(span
|
(span
|
||||||
(text "{{ text \"general:action.save\" }}")))
|
(text "{{ text \"general:action.save\" }}")))
|
||||||
(script
|
(script
|
||||||
(text "setTimeout(() => {
|
(text "setTimeout(async () => {
|
||||||
const ui = ns(\"ui\");
|
const ui = await ns(\"ui\");
|
||||||
const element = document.getElementById(\"post_context\");
|
const element = document.getElementById(\"post_context\");
|
||||||
const settings = JSON.parse(\"{{ post_context_serde|safe }}\");
|
const settings = JSON.parse(\"{{ post_context_serde|safe }}\");
|
||||||
|
|
||||||
|
|
|
@ -1035,8 +1035,8 @@
|
||||||
("id" "settings_json")
|
("id" "settings_json")
|
||||||
(text "{{ profile.settings|json_encode()|remove_script_tags|safe }}"))
|
(text "{{ profile.settings|json_encode()|remove_script_tags|safe }}"))
|
||||||
(script
|
(script
|
||||||
(text "setTimeout(() => {
|
(text "setTimeout(async () => {
|
||||||
const ui = ns(\"ui\");
|
const ui = await ns(\"ui\");
|
||||||
const settings = JSON.parse(
|
const settings = JSON.parse(
|
||||||
document.getElementById(\"settings_json\").innerHTML,
|
document.getElementById(\"settings_json\").innerHTML,
|
||||||
);
|
);
|
||||||
|
|
|
@ -21,10 +21,9 @@
|
||||||
{%- endif %}")
|
{%- endif %}")
|
||||||
|
|
||||||
(text "<script>
|
(text "<script>
|
||||||
globalThis.ns_verbose = false;
|
|
||||||
globalThis.ns_config = {
|
globalThis.ns_config = {
|
||||||
root: \"/js/\",
|
root: \"/js/\",
|
||||||
verbose: globalThis.ns_verbose,
|
verbose: false,
|
||||||
version: \"tetratto-{{ random_cache_breaker }}\",
|
version: \"tetratto-{{ random_cache_breaker }}\",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -117,7 +117,7 @@ media_theme_pref();
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
self.define("clean_date_codes", ({ $ }) => {
|
self.define("clean_date_codes", async ({ $ }) => {
|
||||||
for (const element of Array.from(document.querySelectorAll(".date"))) {
|
for (const element of Array.from(document.querySelectorAll(".date"))) {
|
||||||
if (element.getAttribute("data-unix")) {
|
if (element.getAttribute("data-unix")) {
|
||||||
// this allows us to run the function twice on the same page
|
// this allows us to run the function twice on the same page
|
||||||
|
@ -134,7 +134,7 @@ media_theme_pref();
|
||||||
|
|
||||||
element.setAttribute("title", then.toLocaleString());
|
element.setAttribute("title", then.toLocaleString());
|
||||||
|
|
||||||
let pretty = $.rel_date(then) || "";
|
let pretty = (await $.rel_date(then)) || "";
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(screen.width < 900 && pretty !== undefined) |
|
(screen.width < 900 && pretty !== undefined) |
|
||||||
|
@ -619,7 +619,6 @@ media_theme_pref();
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
// done scrolling, no more pages (http error)
|
// done scrolling, no more pages (http error)
|
||||||
wrapper.removeEventListener("scroll", event);
|
wrapper.removeEventListener("scroll", event);
|
||||||
|
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -652,7 +651,7 @@ media_theme_pref();
|
||||||
);
|
);
|
||||||
|
|
||||||
self.define("hooks::check_reactions", async ({ $ }) => {
|
self.define("hooks::check_reactions", async ({ $ }) => {
|
||||||
const observer = $.offload_work_to_client_when_in_view(
|
const observer = await $.offload_work_to_client_when_in_view(
|
||||||
async (element) => {
|
async (element) => {
|
||||||
const like = element.querySelector(
|
const like = element.querySelector(
|
||||||
'[hook_element="reaction.like"]',
|
'[hook_element="reaction.like"]',
|
||||||
|
@ -1292,7 +1291,7 @@ ${option.input_element_type === "textarea" ? `${option.value}</textarea>` : ""}
|
||||||
}, 150);
|
}, 150);
|
||||||
|
|
||||||
// run hooks
|
// run hooks
|
||||||
const atto = ns("atto");
|
const atto = await ns("atto");
|
||||||
|
|
||||||
atto.clean_date_codes();
|
atto.clean_date_codes();
|
||||||
atto.clean_poll_date_codes();
|
atto.clean_poll_date_codes();
|
||||||
|
|
|
@ -16,19 +16,32 @@ function regns_log(level, ...args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Query an existing namespace
|
/// Query an existing namespace
|
||||||
globalThis.ns = (ns) => {
|
globalThis.ns = async (ns) => {
|
||||||
regns_log("info", "namespace query:", ns);
|
regns_log("info", "namespace query:", ns);
|
||||||
|
|
||||||
// get namespace from app base
|
// get namespace from app base
|
||||||
const res = globalThis._app_base.ns_store[`$${ns}`];
|
let res = globalThis._app_base.ns_store[`$${ns}`];
|
||||||
|
let tries = 0;
|
||||||
|
|
||||||
if (!res) {
|
while (!res) {
|
||||||
|
if (tries >= 5) {
|
||||||
return console.error(
|
return console.error(
|
||||||
`namespace "${ns}" does not exist, please use one of the following:`,
|
`namespace "${ns}" does not exist, please use one of the following:`,
|
||||||
Object.keys(globalThis._app_base.ns_store),
|
Object.keys(globalThis._app_base.ns_store),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tries += 1;
|
||||||
|
res = globalThis._app_base.ns_store[`$${ns}`];
|
||||||
|
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve();
|
||||||
|
}, 500);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
regns_log("info", `found ns "${ns}" after ${tries} tries`);
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -51,12 +64,12 @@ globalThis.reg_ns = (ns, deps) => {
|
||||||
_ident: ns,
|
_ident: ns,
|
||||||
_deps: deps || [],
|
_deps: deps || [],
|
||||||
/// Pull dependencies (other namespaces) as listed in the given `deps` argument
|
/// Pull dependencies (other namespaces) as listed in the given `deps` argument
|
||||||
_get_deps: () => {
|
_get_deps: async () => {
|
||||||
const self = globalThis._app_base.ns_store[`$${ns}`];
|
const self = globalThis._app_base.ns_store[`$${ns}`];
|
||||||
const deps = {};
|
const deps = {};
|
||||||
|
|
||||||
for (const dep of self._deps) {
|
for (const dep of self._deps) {
|
||||||
const res = globalThis.ns(dep);
|
const res = await globalThis.ns(dep);
|
||||||
|
|
||||||
if (!res) {
|
if (!res) {
|
||||||
regns_log("warn", "failed to pull dependency:", dep);
|
regns_log("warn", "failed to pull dependency:", dep);
|
||||||
|
@ -72,16 +85,15 @@ globalThis.reg_ns = (ns, deps) => {
|
||||||
/// Store the real versions of functions
|
/// Store the real versions of functions
|
||||||
_fn_store: {},
|
_fn_store: {},
|
||||||
/// Call a function in a namespace and load namespace dependencies
|
/// Call a function in a namespace and load namespace dependencies
|
||||||
define: (name, func, types) => {
|
define: async (name, func, types) => {
|
||||||
const self = globalThis.ns(ns);
|
const self = await globalThis.ns(ns);
|
||||||
self._fn_store[name] = func; // store real function
|
self._fn_store[name] = func; // store real function
|
||||||
self[name] = function (...args) {
|
self[name] = async (...args) => {
|
||||||
regns_log("info", "namespace call:", ns, name);
|
regns_log("info", "namespace call:", ns, name);
|
||||||
|
|
||||||
// js doesn't provide type checking, we do
|
// js doesn't provide type checking, we do
|
||||||
if (types) {
|
if (types) {
|
||||||
for (const i in args) {
|
for (const i in args) {
|
||||||
// biome-ignore lint: this is incorrect, you do not need a string literal to use typeof
|
|
||||||
if (types[i] && typeof args[i] !== types[i]) {
|
if (types[i] && typeof args[i] !== types[i]) {
|
||||||
return console.error(
|
return console.error(
|
||||||
"argument does not pass type check:",
|
"argument does not pass type check:",
|
||||||
|
@ -94,7 +106,7 @@ globalThis.reg_ns = (ns, deps) => {
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
// we MUST return here, otherwise nothing will work in workers
|
// we MUST return here, otherwise nothing will work in workers
|
||||||
return self._fn_store[name](self._get_deps(), ...args); // call with deps and arguments
|
return self._fn_store[name](await self._get_deps(), ...args); // call with deps and arguments
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -104,11 +116,11 @@ globalThis.reg_ns = (ns, deps) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Call a namespace function quickly
|
/// Call a namespace function quickly
|
||||||
globalThis.trigger = (id, args) => {
|
globalThis.trigger = async (id, args) => {
|
||||||
// get namespace
|
// get namespace
|
||||||
const s = id.split("::");
|
const s = id.split("::");
|
||||||
const [namespace, func] = [s[0], s.slice(1, s.length).join("::")];
|
const [namespace, func] = [s[0], s.slice(1, s.length).join("::")];
|
||||||
const self = ns(namespace);
|
const self = await ns(namespace);
|
||||||
|
|
||||||
if (!self) {
|
if (!self) {
|
||||||
return console.error("namespace does not exist:", namespace);
|
return console.error("namespace does not exist:", namespace);
|
||||||
|
|
|
@ -52,6 +52,17 @@
|
||||||
if (data.method.Forward === "Key") {
|
if (data.method.Forward === "Key") {
|
||||||
$.STREAMS[stream].id = data.data;
|
$.STREAMS[stream].id = data.data;
|
||||||
return console.info(`${stream} ${data.data}`);
|
return console.info(`${stream} ${data.data}`);
|
||||||
|
} else if (data.method.Forward === "Javascript") {
|
||||||
|
const s = document.createElement("script");
|
||||||
|
s.setAttribute("type", "module");
|
||||||
|
s.setAttribute("data-received", Date.now().toString());
|
||||||
|
|
||||||
|
s.text = JSON.parse(data.data).js;
|
||||||
|
document.body.appendChild(s).parentNode.removeChild(s);
|
||||||
|
|
||||||
|
return console.info(
|
||||||
|
`${stream} received Forward(PacketType::Javascript) payload of ${data.data.length} bytes`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $.sock(stream).events.message(data);
|
return $.sock(stream).events.message(data);
|
||||||
|
@ -72,8 +83,8 @@
|
||||||
socket.socket.close();
|
socket.socket.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
self.define("event", ({ $ }, stream, event, handler) => {
|
self.define("event", async ({ $ }, stream, event, handler) => {
|
||||||
const socket = $.sock(stream);
|
const socket = await $.sock(stream);
|
||||||
|
|
||||||
if (!socket) {
|
if (!socket) {
|
||||||
console.warn("no such stream to add event to");
|
console.warn("no such stream to add event to");
|
||||||
|
@ -84,7 +95,7 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
self.define("send_packet", async ({ $ }, stream, method, data) => {
|
self.define("send_packet", async ({ $ }, stream, method, data) => {
|
||||||
await (
|
return await (
|
||||||
await fetch(`/api/v1/auth/user/${$.USER}/_connect/${stream}/send`, {
|
await fetch(`/api/v1/auth/user/${$.USER}/_connect/${stream}/send`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -97,4 +108,19 @@
|
||||||
})
|
})
|
||||||
).json();
|
).json();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
self.define("send_packet_to", async (_, user, stream, method, data) => {
|
||||||
|
return await (
|
||||||
|
await fetch(`/api/v1/auth/user/${user}/_connect/${stream}/send`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
method,
|
||||||
|
data: JSON.stringify(data),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
).json();
|
||||||
|
});
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -219,7 +219,7 @@ pub async fn upload_avatar_request(
|
||||||
std::fs::write(&path, img.0).unwrap();
|
std::fs::write(&path, img.0).unwrap();
|
||||||
|
|
||||||
// update user settings
|
// update user settings
|
||||||
auth_user.settings.avatar_mime = mime.to_string();
|
auth_user.settings.avatar_mime = "image/gif".to_string();
|
||||||
if let Err(e) = data
|
if let Err(e) = data
|
||||||
.update_user_settings(auth_user.id, auth_user.settings)
|
.update_user_settings(auth_user.id, auth_user.settings)
|
||||||
.await
|
.await
|
||||||
|
@ -240,25 +240,8 @@ pub async fn upload_avatar_request(
|
||||||
return Json(Error::FileTooLarge.into());
|
return Json(Error::FileTooLarge.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// upload image
|
|
||||||
let mut bytes = Vec::new();
|
|
||||||
|
|
||||||
for byte in img.0 {
|
|
||||||
bytes.push(byte);
|
|
||||||
}
|
|
||||||
|
|
||||||
match save_buffer(
|
|
||||||
&path,
|
|
||||||
bytes,
|
|
||||||
if mime == "image/gif" {
|
|
||||||
image::ImageFormat::Gif
|
|
||||||
} else {
|
|
||||||
image::ImageFormat::Avif
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
Ok(_) => {
|
|
||||||
// update user settings
|
// update user settings
|
||||||
auth_user.settings.avatar_mime = mime.to_string();
|
auth_user.settings.avatar_mime = "image/avif".to_string();
|
||||||
if let Err(e) = data
|
if let Err(e) = data
|
||||||
.update_user_settings(auth_user.id, auth_user.settings)
|
.update_user_settings(auth_user.id, auth_user.settings)
|
||||||
.await
|
.await
|
||||||
|
@ -266,13 +249,19 @@ pub async fn upload_avatar_request(
|
||||||
return Json(e.into());
|
return Json(e.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ...
|
// upload image
|
||||||
Json(ApiReturn {
|
let mut bytes = Vec::new();
|
||||||
|
|
||||||
|
for byte in img.0 {
|
||||||
|
bytes.push(byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
match save_buffer(&path, bytes, image::ImageFormat::Avif) {
|
||||||
|
Ok(_) => Json(ApiReturn {
|
||||||
ok: true,
|
ok: true,
|
||||||
message: "Avatar uploaded. It might take a bit to update".to_string(),
|
message: "Avatar uploaded. It might take a bit to update".to_string(),
|
||||||
payload: (),
|
payload: (),
|
||||||
})
|
}),
|
||||||
}
|
|
||||||
Err(e) => Json(Error::MiscError(e.to_string()).into()),
|
Err(e) => Json(Error::MiscError(e.to_string()).into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -331,7 +320,7 @@ pub async fn upload_banner_request(
|
||||||
std::fs::write(&path, img.0).unwrap();
|
std::fs::write(&path, img.0).unwrap();
|
||||||
|
|
||||||
// update user settings
|
// update user settings
|
||||||
auth_user.settings.banner_mime = mime.to_string();
|
auth_user.settings.banner_mime = "image/gif".to_string();
|
||||||
if let Err(e) = data
|
if let Err(e) = data
|
||||||
.update_user_settings(auth_user.id, auth_user.settings)
|
.update_user_settings(auth_user.id, auth_user.settings)
|
||||||
.await
|
.await
|
||||||
|
@ -352,25 +341,8 @@ pub async fn upload_banner_request(
|
||||||
return Json(Error::FileTooLarge.into());
|
return Json(Error::FileTooLarge.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// upload image
|
|
||||||
let mut bytes = Vec::new();
|
|
||||||
|
|
||||||
for byte in img.0 {
|
|
||||||
bytes.push(byte);
|
|
||||||
}
|
|
||||||
|
|
||||||
match save_buffer(
|
|
||||||
&path,
|
|
||||||
bytes,
|
|
||||||
if mime == "image/gif" {
|
|
||||||
image::ImageFormat::Gif
|
|
||||||
} else {
|
|
||||||
image::ImageFormat::Avif
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
Ok(_) => {
|
|
||||||
// update user settings
|
// update user settings
|
||||||
auth_user.settings.banner_mime = mime.to_string();
|
auth_user.settings.avatar_mime = "image/avif".to_string();
|
||||||
if let Err(e) = data
|
if let Err(e) = data
|
||||||
.update_user_settings(auth_user.id, auth_user.settings)
|
.update_user_settings(auth_user.id, auth_user.settings)
|
||||||
.await
|
.await
|
||||||
|
@ -378,13 +350,19 @@ pub async fn upload_banner_request(
|
||||||
return Json(e.into());
|
return Json(e.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ...
|
// upload image
|
||||||
Json(ApiReturn {
|
let mut bytes = Vec::new();
|
||||||
|
|
||||||
|
for byte in img.0 {
|
||||||
|
bytes.push(byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
match save_buffer(&path, bytes, image::ImageFormat::Avif) {
|
||||||
|
Ok(_) => Json(ApiReturn {
|
||||||
ok: true,
|
ok: true,
|
||||||
message: "Banner uploaded. It might take a bit to update".to_string(),
|
message: "Banner uploaded. It might take a bit to update".to_string(),
|
||||||
payload: (),
|
payload: (),
|
||||||
})
|
}),
|
||||||
}
|
|
||||||
Err(e) => Json(Error::MiscError(e.to_string()).into()),
|
Err(e) => Json(Error::MiscError(e.to_string()).into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -708,7 +708,7 @@ pub async fn post_to_socket_request(
|
||||||
None => return Json(Error::NotAllowed.into()),
|
None => return Json(Error::NotAllowed.into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
if user.id.to_string() != user_id {
|
if user.id.to_string() != user_id && !user.permissions.check(FinePermission::MANAGE_USERS) {
|
||||||
return Json(Error::NotAllowed.into());
|
return Json(Error::NotAllowed.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
47
crates/app/src/routes/pages/links.rs
Normal file
47
crates/app/src/routes/pages/links.rs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
use axum::{
|
||||||
|
extract::Path,
|
||||||
|
response::{Html, IntoResponse},
|
||||||
|
Extension,
|
||||||
|
};
|
||||||
|
use axum_extra::extract::CookieJar;
|
||||||
|
use tetratto_core::model::{permissions::FinePermission, Error};
|
||||||
|
use crate::{get_user_from_token, State};
|
||||||
|
use super::render_error;
|
||||||
|
|
||||||
|
/// `/links/{id}`
|
||||||
|
pub async fn navigate_request(
|
||||||
|
jar: CookieJar,
|
||||||
|
Path(id): Path<usize>,
|
||||||
|
Extension(data): Extension<State>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
let data = data.read().await;
|
||||||
|
let user = match get_user_from_token!(jar, data.0) {
|
||||||
|
Some(ua) => ua,
|
||||||
|
None => {
|
||||||
|
return Err(Html(
|
||||||
|
render_error(Error::NotAllowed, &jar, &data, &None).await,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let link = match data.0.get_link_by_id(id).await {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => return Err(Html(render_error(e, &jar, &data, &Some(user)).await)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let owner = match data.0.get_user_by_id(link.owner).await {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => return Err(Html(render_error(e, &jar, &data, &Some(user)).await)),
|
||||||
|
};
|
||||||
|
|
||||||
|
if owner.permissions.check(FinePermission::SUPPORTER) {
|
||||||
|
if let Err(e) = data.0.incr_link_clicks(link.id).await {
|
||||||
|
return Err(Html(render_error(e, &jar, &data, &Some(user)).await));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Html(format!(
|
||||||
|
"<!doctype html /><html><head><meta http-equiv=\"refresh\" content=\"0; url={}\" /></head><body>Navigating...</body></html>",
|
||||||
|
link.href
|
||||||
|
)))
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ pub mod communities;
|
||||||
pub mod developer;
|
pub mod developer;
|
||||||
pub mod forge;
|
pub mod forge;
|
||||||
pub mod journals;
|
pub mod journals;
|
||||||
|
pub mod links;
|
||||||
pub mod misc;
|
pub mod misc;
|
||||||
pub mod mod_panel;
|
pub mod mod_panel;
|
||||||
pub mod profile;
|
pub mod profile;
|
||||||
|
@ -137,6 +138,8 @@ pub fn routes() -> Router {
|
||||||
.route("/journals/{journal}/{note}", get(journals::app_request))
|
.route("/journals/{journal}/{note}", get(journals::app_request))
|
||||||
.route("/@{owner}/{journal}", get(journals::index_view_request))
|
.route("/@{owner}/{journal}", get(journals::index_view_request))
|
||||||
.route("/@{owner}/{journal}/{note}", get(journals::view_request))
|
.route("/@{owner}/{journal}/{note}", get(journals::view_request))
|
||||||
|
// links
|
||||||
|
.route("/links/{id}", get(links::navigate_request))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn render_error(
|
pub async fn render_error(
|
||||||
|
|
|
@ -350,6 +350,7 @@ fn default_banned_usernames() -> Vec<String> {
|
||||||
"stack".to_string(),
|
"stack".to_string(),
|
||||||
"search".to_string(),
|
"search".to_string(),
|
||||||
"journals".to_string(),
|
"journals".to_string(),
|
||||||
|
"links".to_string(),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -141,6 +141,6 @@ impl DataManager {
|
||||||
auto_method!(update_link_label(&str) -> "UPDATE links SET label = $1 WHERE id = $2" --cache-key-tmpl="atto.link:{}");
|
auto_method!(update_link_label(&str) -> "UPDATE links SET label = $1 WHERE id = $2" --cache-key-tmpl="atto.link:{}");
|
||||||
auto_method!(update_link_href(&str) -> "UPDATE links SET href = $1 WHERE id = $2" --cache-key-tmpl="atto.link:{}");
|
auto_method!(update_link_href(&str) -> "UPDATE links SET href = $1 WHERE id = $2" --cache-key-tmpl="atto.link:{}");
|
||||||
auto_method!(update_link_upload_id(i64) -> "UPDATE links SET upload_id = $1 WHERE id = $2" --cache-key-tmpl="atto.link:{}");
|
auto_method!(update_link_upload_id(i64) -> "UPDATE links SET upload_id = $1 WHERE id = $2" --cache-key-tmpl="atto.link:{}");
|
||||||
auto_method!(update_link_clicks(i32) -> "UPDATE links SET clicks = $1 WHERE id = $2" --cache-key-tmpl="atto.link:{}");
|
|
||||||
auto_method!(update_link_position(i32) -> "UPDATE links SET position = $1 WHERE id = $2" --cache-key-tmpl="atto.link:{}");
|
auto_method!(update_link_position(i32) -> "UPDATE links SET position = $1 WHERE id = $2" --cache-key-tmpl="atto.link:{}");
|
||||||
|
auto_method!(incr_link_clicks() -> "UPDATE links SET clicks = $1 WHERE id = $2" --cache-key-tmpl="atto.link:{}" --incr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@ pub enum PacketType {
|
||||||
Crud(CrudMessageType),
|
Crud(CrudMessageType),
|
||||||
/// A text key which identifies the socket.
|
/// A text key which identifies the socket.
|
||||||
Key,
|
Key,
|
||||||
|
/// JavaScript text.
|
||||||
|
Javascript,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, PartialEq, Eq)]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue