diff --git a/backend/src/ap.rs b/backend/src/ap.rs index 2cd3927..8b1b07a 100644 --- a/backend/src/ap.rs +++ b/backend/src/ap.rs @@ -24,8 +24,12 @@ pub mod tag; pub mod undo; pub fn generate_object_id() -> Result { - Url::parse(&format!("https://{}/object/{}", CONFIG.domain, Ulid::new(),)) - .context_internal_server_error("failed to construct object URL") + Url::parse(&format!( + "https://{}/object/{}", + CONFIG.public_domain, + Ulid::new(), + )) + .context_internal_server_error("failed to construct object URL") } #[derive(Debug, Clone, Deserialize, Serialize)] diff --git a/backend/src/ap/follow.rs b/backend/src/ap/follow.rs index 027a49b..932ca66 100644 --- a/backend/src/ap/follow.rs +++ b/backend/src/ap/follow.rs @@ -179,8 +179,11 @@ impl FollowReject { object: Follow { ty: Default::default(), id: Some( - Url::parse(&format!("https://{}/follower/{}", CONFIG.domain, user_id)) - .context_internal_server_error("failed to construct URL")?, + Url::parse(&format!( + "https://{}/follower/{}", + CONFIG.public_domain, user_id + )) + .context_internal_server_error("failed to construct URL")?, ), actor: user_uri, object: LocalPerson::id(), diff --git a/backend/src/ap/person.rs b/backend/src/ap/person.rs index ecb1e60..c9a55bc 100644 --- a/backend/src/ap/person.rs +++ b/backend/src/ap/person.rs @@ -138,7 +138,7 @@ impl LocalPerson { pub fn id() -> Url { static ID: Lazy = Lazy::new(|| { - Url::parse(&format!("https://{}/person", CONFIG.domain)) + Url::parse(&format!("https://{}/person", CONFIG.public_domain)) .expect("failed to construct ID URL") }); ID.clone() @@ -146,7 +146,7 @@ impl LocalPerson { pub fn inbox() -> Url { static INBOX: Lazy = Lazy::new(|| { - Url::parse(&format!("https://{}/inbox", CONFIG.domain)) + Url::parse(&format!("https://{}/inbox", CONFIG.public_domain)) .expect("failed to construct inbox URL") }); INBOX.clone() diff --git a/backend/src/config.rs b/backend/src/config.rs index 33b4d0f..c75ef2c 100644 --- a/backend/src/config.rs +++ b/backend/src/config.rs @@ -13,6 +13,10 @@ fn default_debug() -> bool { false } +fn default_public_domain() -> String { + "localhost".to_string() +} + fn default_listen_addr() -> String { "0.0.0.0:3000".to_string() } @@ -65,10 +69,11 @@ pub struct Config { #[serde(default = "default_debug")] pub debug: bool, - /// Domain of the instance. + /// Public domain of the instance. /// DO NOT CHANGE! /// e.g. `example.com` - pub domain: String, + #[serde(default = "default_public_domain")] + pub public_domain: String, #[serde(default = "default_listen_addr")] pub listen_addr: String, diff --git a/backend/src/entity_impl/emoji.rs b/backend/src/entity_impl/emoji.rs index a400e78..1a42e4f 100644 --- a/backend/src/entity_impl/emoji.rs +++ b/backend/src/entity_impl/emoji.rs @@ -16,12 +16,15 @@ use crate::{ impl emoji::Model { pub fn ap_id(&self) -> Result { - Url::parse(&format!("https://{}/emoji/{}", CONFIG.domain, self.name)) - .context_internal_server_error("failed to construct follow URL ID") + Url::parse(&format!( + "https://{}/emoji/{}", + CONFIG.public_domain, self.name + )) + .context_internal_server_error("failed to construct follow URL ID") } pub fn parse_ap_id(url: &str) -> Option { - url.strip_prefix(&format!("https://{}/emoji/", CONFIG.domain)) + url.strip_prefix(&format!("https://{}/emoji/", CONFIG.public_domain)) .map(str::to_string) } } diff --git a/backend/src/entity_impl/follow.rs b/backend/src/entity_impl/follow.rs index bdf43ad..9fcb8f2 100644 --- a/backend/src/entity_impl/follow.rs +++ b/backend/src/entity_impl/follow.rs @@ -24,12 +24,12 @@ impl follow::Model { } pub fn ap_id_from_id(id: Ulid) -> Result { - Url::parse(&format!("https://{}/follow/{}", CONFIG.domain, id)) + Url::parse(&format!("https://{}/follow/{}", CONFIG.public_domain, id)) .context_internal_server_error("failed to construct follow URL ID") } pub fn parse_ap_id(url: &str) -> Option { - url.strip_prefix(&format!("https://{}/follow/", CONFIG.domain)) + url.strip_prefix(&format!("https://{}/follow/", CONFIG.public_domain)) .and_then(|id| Ulid::from_string(id).ok()) } } diff --git a/backend/src/entity_impl/post.rs b/backend/src/entity_impl/post.rs index a86135f..a0b3406 100644 --- a/backend/src/entity_impl/post.rs +++ b/backend/src/entity_impl/post.rs @@ -41,7 +41,7 @@ fn calculate_visibility(to: &[Url], cc: &[Url]) -> sea_orm_active_enums::Visibil impl post::Model { pub fn ap_id_from_id(id: Ulid) -> Result { - Url::parse(&format!("https://{}/note/{}", CONFIG.domain, id)) + Url::parse(&format!("https://{}/note/{}", CONFIG.public_domain, id)) .context_internal_server_error("failed to construct follow URL ID") } diff --git a/backend/src/entity_impl/reaction.rs b/backend/src/entity_impl/reaction.rs index 02090af..0f6f7d7 100644 --- a/backend/src/entity_impl/reaction.rs +++ b/backend/src/entity_impl/reaction.rs @@ -28,7 +28,7 @@ use crate::{ impl reaction::Model { pub fn ap_id_from_id(id: Ulid) -> Result { - Url::parse(&format!("https://{}/like/{}", CONFIG.domain, id)) + Url::parse(&format!("https://{}/like/{}", CONFIG.public_domain, id)) .context_internal_server_error("failed to construct follow URL ID") } diff --git a/backend/src/handler/well_known.rs b/backend/src/handler/well_known.rs index 3b844d1..d449417 100644 --- a/backend/src/handler/well_known.rs +++ b/backend/src/handler/well_known.rs @@ -59,7 +59,7 @@ async fn get_nodeinfo() -> Result> { links: vec![NodeInfoWellKnownLinks { rel: Url::parse("http://nodeinfo.diaspora.software/ns/schema/2.0") .context_internal_server_error("failed to construct URL")?, - href: Url::parse(&format!("https://{}/nodeinfo/2.0", CONFIG.domain,)) + href: Url::parse(&format!("https://{}/nodeinfo/2.0", CONFIG.public_domain,)) .context_internal_server_error("failed to construct URL")?, }], })) diff --git a/backend/src/main.rs b/backend/src/main.rs index d72489b..3d22776 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -64,6 +64,7 @@ async fn main() -> anyhow::Result<()> { if crate::config::CONFIG.debug { tracing::warn!("Enabling debug mode... DO NOT USE IN PRODUCTION!"); } + tracing::info!("Using domain `{}`", crate::config::CONFIG.public_domain); let db = Database::connect(crate::config::CONFIG.database_url.as_str()) .await @@ -78,7 +79,7 @@ async fn main() -> anyhow::Result<()> { .await .context("failed to construct app state")?; let federation_config = FederationConfig::builder() - .domain(&crate::config::CONFIG.domain) + .domain(&crate::config::CONFIG.public_domain) .app_data(state.clone()) .debug(crate::config::CONFIG.debug) .build() diff --git a/backend/src/object_store.rs b/backend/src/object_store.rs index 8fd8a88..a885d84 100644 --- a/backend/src/object_store.rs +++ b/backend/src/object_store.rs @@ -75,7 +75,7 @@ impl ObjectStore { .to_string_lossy() .to_string(), sea_orm_active_enums::ObjectStoreType::LocalFileSystem, - Url::parse(&format!("https://{}/file/{}", CONFIG.domain, key)) + Url::parse(&format!("https://{}/file/{}", CONFIG.public_domain, key)) .context_internal_server_error("failed to construct public URL")?, ), }) diff --git a/frontend/src/components/RightNav.tsx b/frontend/src/components/RightNav.tsx index 856f539..181eb50 100644 --- a/frontend/src/components/RightNav.tsx +++ b/frontend/src/components/RightNav.tsx @@ -1,5 +1,6 @@ import { useIsAuthed } from "../queries/auth"; import { useSetting } from "../queries/setting"; +import RightNavGallery from "./RightNavGallery"; import RightNavSetting from "./RightNavSetting"; import RightNavUpload from "./RightNavUpload"; @@ -14,9 +15,12 @@ export default function RightNav() {
{setting && }
-
+
+
+ +
)}
diff --git a/frontend/src/components/RightNavGallery.tsx b/frontend/src/components/RightNavGallery.tsx new file mode 100644 index 0000000..8e92f25 --- /dev/null +++ b/frontend/src/components/RightNavGallery.tsx @@ -0,0 +1,79 @@ +import { PhotoIcon, PlusIcon, TrashIcon } from "@heroicons/react/24/outline"; +import { Fragment, useRef } from "react"; + +import { useLocalFileDeleteMutation, useLocalFiles } from "../queries/file"; + +export default function RightNavGallery() { + const modalRef = useRef(null); + + const { data, fetchNextPage, hasNextPage, isFetchingNextPage, remove } = + useLocalFiles(); + const { mutate: deleteFile, isLoading: isDeleteLoading } = + useLocalFileDeleteMutation(() => { + remove(); + }); + + return ( + <> + + +
+

Gallery

+
+ {(data?.pages ?? []).map((page, i) => ( + + {page.map((file) => ( +
+ {file.alt + +
+ ))} +
+ ))} +
+
+ {isFetchingNextPage ? ( + + ) : ( + hasNextPage && ( + + ) + )} +
+
+
+ +
+
+ + ); +} diff --git a/frontend/src/components/RightNavSetting.tsx b/frontend/src/components/RightNavSetting.tsx index 05d28d7..ccd68fd 100644 --- a/frontend/src/components/RightNavSetting.tsx +++ b/frontend/src/components/RightNavSetting.tsx @@ -40,6 +40,7 @@ export default function RightNavSetting({
+

Settings

(null); - const { register, handleSubmit } = useForm(); + const { register, handleSubmit, reset } = useForm(); const { mutate: upload, isLoading, error, - } = useFileUploadMutation(() => { + } = useLocalFileUploadMutation(() => { + reset(); modalRef.current?.close(); }); @@ -44,6 +45,7 @@ export default function RightNavUpload() {
+

Upload

void, -): MutationRet { +): MutationRet { + const queryClient = useQueryClient(); const [accessKey] = useContext(AccessKeyContext); return useMutation( @@ -40,6 +55,36 @@ export function useFileUploadMutation( }, { onSuccess: () => { + queryClient.invalidateQueries(FILES_KEY); + onSuccess(); + }, + }, + ); +} + +export function useLocalFileDeleteMutation( + onSuccess: () => void, +): MutationRet> { + const queryClient = useQueryClient(); + const [accessKey] = useContext(AccessKeyContext); + + return useMutation( + async (id) => { + const headers: HeadersInit = {}; + if (accessKey.length > 0) { + headers["authorization"] = `Bearer ${accessKey}`; + } + const resp = await fetch(`/api/file/${id}`, { + method: "DELETE", + headers, + }); + if (!resp.ok) { + await throwError(resp); + } + }, + { + onSuccess: () => { + queryClient.invalidateQueries(FILES_KEY); onSuccess(); }, }, diff --git a/frontend/src/queries/index.ts b/frontend/src/queries/index.ts index 91d5f0b..da64eaa 100644 --- a/frontend/src/queries/index.ts +++ b/frontend/src/queries/index.ts @@ -7,7 +7,7 @@ import { useMutation, useInfiniteQuery, UseInfiniteQueryResult, - QueryOptions, + UseQueryOptions, } from "react-query"; import z from "zod"; @@ -24,7 +24,7 @@ export function useJsonQuery( key: string[], url: string, params?: URLSearchParams, - options?: QueryOptions, + options?: UseQueryOptions, ): UseJsonQueryResult { const [accessKey] = useContext(AccessKeyContext);