Federation Refactoring & Architecture Improvements (#930)
* initial commit * add lists * update permissions * fix waypoint create * needs_full_sync for activitypub trails * fixes build issues * fix list get * further CSRF protection * update API docs * adds rate limiter * fix tiptap mentions * a bit more cleanup of main.go * Fix migration order * fixes reviewed notes * require context for activitpub server calls * improve hashing for identifier * fix copy paste error * fix sync trail/list issues * fix summit log/comment duplicates * adaptions after trail merge * fix dockerignore --------- Co-authored-by: Christian Beutel <> Co-authored-by: slothful-vassal <89943360+slothful-vassal@users.noreply.github.com>
This commit is contained in:
@@ -43,7 +43,7 @@
|
||||
error = "",
|
||||
placeholder = "",
|
||||
extraClasses = "",
|
||||
searchListPosition = "absolute"
|
||||
searchListPosition = "absolute",
|
||||
}: Props = $props();
|
||||
|
||||
const fontSizes: SelectItem[] = [
|
||||
@@ -87,7 +87,9 @@
|
||||
return [
|
||||
"a",
|
||||
mergeAttributes(
|
||||
{ href: `/profile/@${options.HTMLAttributes["data-label"]}` },
|
||||
{
|
||||
href: `/profile/@${options.HTMLAttributes["data-label"]}`,
|
||||
},
|
||||
options.HTMLAttributes,
|
||||
),
|
||||
options.renderText?.({
|
||||
@@ -141,8 +143,9 @@
|
||||
if (!box) {
|
||||
return;
|
||||
}
|
||||
|
||||
searchListElement.style.position = searchListPosition;
|
||||
|
||||
searchListElement.style.position =
|
||||
searchListPosition;
|
||||
searchListElement.style.top = `${box.bottom + window.scrollY + 4}px`;
|
||||
searchListElement.style.left = `${box.left + window.scrollX}px`;
|
||||
searchListElement.style.zIndex = "1001";
|
||||
@@ -179,12 +182,12 @@
|
||||
|
||||
onKeyDown(props) {
|
||||
if (props.event.key === "Escape") {
|
||||
this.onExit?.({} as unknown as any)
|
||||
this.onExit?.({} as unknown as any);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false
|
||||
return false;
|
||||
},
|
||||
onExit() {
|
||||
unmount(component);
|
||||
@@ -315,7 +318,7 @@
|
||||
})
|
||||
.run();
|
||||
|
||||
modal.closeModal();
|
||||
modal.closeModal();
|
||||
} catch (e) {
|
||||
if (
|
||||
e instanceof ZodError &&
|
||||
|
||||
@@ -547,16 +547,19 @@
|
||||
clusterPopup.on("close", () => {
|
||||
unHighlightCluster(false);
|
||||
});
|
||||
map.on("mousemove", unHighlightClusterDistanceNotifier)
|
||||
map.on("mousemove", unHighlightClusterDistanceNotifier);
|
||||
}
|
||||
|
||||
function unHighlightClusterDistanceNotifier(e: M.MapMouseEvent) {
|
||||
if (!clusterPopup || !map) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
if (map.project(clusterPopup.getLngLat()).dist(map.project(e.lngLat)) > 60) {
|
||||
if (
|
||||
map.project(clusterPopup.getLngLat()).dist(map.project(e.lngLat)) >
|
||||
60
|
||||
) {
|
||||
clusterPopup.remove();
|
||||
map.off("mousemove", unHighlightClusterDistanceNotifier)
|
||||
map.off("mousemove", unHighlightClusterDistanceNotifier);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -129,9 +129,8 @@
|
||||
|
||||
async function fetchComments() {
|
||||
commentsLoading = true;
|
||||
const trailId = trail.iri ? trail.iri : trail.id!;
|
||||
try {
|
||||
await comments_index(trailId, handle);
|
||||
await comments_index(trail.id!);
|
||||
} catch (e) {
|
||||
show_toast({
|
||||
type: "error",
|
||||
|
||||
@@ -7,8 +7,8 @@ export interface Actor {
|
||||
domain?: string;
|
||||
summary?: string;
|
||||
published?: string;
|
||||
followerCount?: number,
|
||||
followingCount?: number,
|
||||
follower_count?: number,
|
||||
following_count?: number,
|
||||
iri: string;
|
||||
inbox: string;
|
||||
outbox?: string;
|
||||
|
||||
@@ -22,6 +22,7 @@ export class List {
|
||||
|
||||
}
|
||||
created?: string;
|
||||
updated?: string;
|
||||
author: string;
|
||||
|
||||
constructor(name: string, trails: Trail[], params?: { description?: string, public?: boolean, avatar?: string, author?: string }) {
|
||||
|
||||
@@ -1,25 +1,17 @@
|
||||
import { Comment } from "$lib/models/comment";
|
||||
import type { Trail } from "$lib/models/trail";
|
||||
import { APIError } from "$lib/util/api_util";
|
||||
import { type ListResult } from "pocketbase";
|
||||
import { get, writable, type Writable } from "svelte/store";
|
||||
import { currentUser } from "./user_store";
|
||||
import { isURL } from "$lib/util/file_util";
|
||||
|
||||
export const comments: Writable<Comment[]> = writable([])
|
||||
|
||||
export async function comments_index(trailId: string, handle?: string) {
|
||||
export async function comments_index(trailId: string) {
|
||||
let filter: string;
|
||||
if (isURL(trailId)) {
|
||||
filter = `trail="${trailId}"||trail.iri="${trailId}"||trail="${trailId.substring(trailId.length - 15)}"`
|
||||
} else {
|
||||
filter = `trail="${trailId}"`
|
||||
}
|
||||
let r = await fetch(`/api/v1/comment?` + new URLSearchParams({
|
||||
filter,
|
||||
|
||||
let r = await fetch(`/api/v1/trail/${trailId}/comment?` + new URLSearchParams({
|
||||
expand: "author",
|
||||
sort: "-created",
|
||||
...(handle ? { handle } : {})
|
||||
}), {
|
||||
method: 'GET',
|
||||
})
|
||||
|
||||
@@ -5,11 +5,9 @@ import { type ListResult } from "pocketbase";
|
||||
|
||||
let follows: Actor[] = [];
|
||||
|
||||
export async function follows_index(data: { username: string, type: "followers" | "following" }, page: number = 1, perPage: number = 10, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch) {
|
||||
export async function follows_index(page: number = 1, perPage: number = 10, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch) {
|
||||
|
||||
const r = await f(`/api/v1/follow?` + new URLSearchParams({
|
||||
handle: data.username,
|
||||
type: data.type,
|
||||
page: page.toString(),
|
||||
perPage: perPage.toString(),
|
||||
}), {
|
||||
@@ -25,7 +23,7 @@ export async function follows_index(data: { username: string, type: "followers"
|
||||
|
||||
const result = page > 1 ? [...follows, ...fetchedFollows.items] : fetchedFollows.items
|
||||
|
||||
follows = result;
|
||||
follows = result;
|
||||
|
||||
return { ...fetchedFollows, items: result };
|
||||
}
|
||||
|
||||
@@ -9,9 +9,10 @@ import { searchResultToLists } from "./list_store";
|
||||
import type { ListSearchResult } from "./search_store";
|
||||
import { buildFilterText } from "./summit_log_store";
|
||||
import { searchResultToTrailList } from "./trail_store";
|
||||
import type { Actor } from "$lib/models/activitypub/actor";
|
||||
|
||||
let feed: FeedItem[] = []
|
||||
|
||||
let follows: Actor[] = [];
|
||||
|
||||
export async function profile_show(handle: string, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch) {
|
||||
let r = await f('/api/v1/profile/' + handle, {
|
||||
@@ -132,4 +133,26 @@ export async function profile_stats_index(handle: string, filter: SummitLogFilte
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
export async function profile_follows_index(handle: string, type: "followers" | "following", page: number, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch) {
|
||||
const r = await f(`/api/v1/profile/${handle}/follows?` + new URLSearchParams({
|
||||
type,
|
||||
page: page.toString(),
|
||||
}), {
|
||||
method: 'GET',
|
||||
})
|
||||
|
||||
if (!r.ok) {
|
||||
const response = await r.json();
|
||||
throw new APIError(r.status, response.message, response.detail)
|
||||
}
|
||||
|
||||
const fetchedFollows: ListResult<Actor> = await r.json();
|
||||
|
||||
const result = page > 1 ? [...follows, ...fetchedFollows.items] : fetchedFollows.items
|
||||
|
||||
follows = result;
|
||||
|
||||
return { ...fetchedFollows, items: result };
|
||||
}
|
||||
@@ -12,7 +12,7 @@ export async function waypoints_create(waypoint: Waypoint, f: (url: RequestInfo
|
||||
throw Error("Unauthenticated")
|
||||
}
|
||||
|
||||
waypoint.author = user.id
|
||||
waypoint.author = user.actor
|
||||
|
||||
let r = await f('/api/v1/waypoint', {
|
||||
method: 'PUT',
|
||||
|
||||
Reference in New Issue
Block a user