mirror of
https://github.com/versia-pub/server.git
synced 2025-12-06 08:28:19 +01:00
Refactors, bugfixing
This commit is contained in:
parent
5812618170
commit
e26d604a54
|
|
@ -18,7 +18,7 @@ if (!token) {
|
|||
}
|
||||
|
||||
const fetchTimeline = () =>
|
||||
fetch(`${config.http.base_url}/api/v1/timelines/home`, {
|
||||
fetch(new URL("/api/v1/timelines/home", config.http.base_url), {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
|
|
|
|||
1
cli.ts
1
cli.ts
|
|
@ -388,7 +388,6 @@ const cliBuilder = new CliBuilder([
|
|||
for (const key of keys) {
|
||||
if (!args.fields.includes(key)) {
|
||||
// @ts-expect-error This is fine
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete data[key];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,13 +58,11 @@ export const attachmentToAPI = (
|
|||
};
|
||||
|
||||
export const getUrl = (name: string, config: Config) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison
|
||||
if (config.media.backend === MediaBackendType.LOCAL) {
|
||||
return `${config.http.base_url}/media/${name}`;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison, @typescript-eslint/no-unnecessary-condition
|
||||
return new URL(`/media/${name}`, config.http.base_url).toString();
|
||||
}
|
||||
if (config.media.backend === MediaBackendType.S3) {
|
||||
return `${config.s3.public_url}/${name}`;
|
||||
return new URL(`/${name}`, config.s3.public_url).toString();
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import type { Like, Prisma } from "@prisma/client";
|
||||
import type { Like } from "@prisma/client";
|
||||
import { config } from "config-manager";
|
||||
import { client } from "~database/datasource";
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import type { Like as LysandLike } from "~types/lysand/Object";
|
||||
import type { StatusWithRelations } from "./Status";
|
||||
import type { UserWithRelations } from "./User";
|
||||
|
|
@ -18,7 +17,7 @@ export const toLysand = (like: Like): LysandLike => {
|
|||
created_at: new Date(like.createdAt).toISOString(),
|
||||
// biome-ignore lint/suspicious/noExplicitAny: to be rewritten
|
||||
object: (like as any).liked?.uri,
|
||||
uri: `${config.http.base_url}/actions/${like.id}`,
|
||||
uri: new URL(`/actions/${like.id}`, config.http.base_url).toString(),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import type { LysandObject } from "@prisma/client";
|
||||
import { client } from "~database/datasource";
|
||||
import type { LysandObjectType } from "~types/lysand/Object";
|
||||
|
|
|
|||
|
|
@ -149,7 +149,6 @@ export const federateStatusTo = async (
|
|||
new TextEncoder().encode("request_body"),
|
||||
);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
const userInbox = new URL(user.endpoints.inbox);
|
||||
|
||||
const date = new Date();
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import type { LysandPublication, Note } from "~types/lysand/Object";
|
|||
import { applicationToAPI } from "./Application";
|
||||
import { attachmentToAPI } from "./Attachment";
|
||||
import { emojiToAPI, emojiToLysand, parseEmojis } from "./Emoji";
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import type { UserWithRelations } from "./User";
|
||||
import { fetchRemoteUser, parseMentionsUris, userToAPI } from "./User";
|
||||
import { statusAndUserRelations, userRelations } from "./relations";
|
||||
|
|
@ -118,7 +117,6 @@ export const fetchFromRemote = async (uri: string): Promise<Status | null> => {
|
|||
/**
|
||||
* Return all the ancestors of this post,
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/require-await, @typescript-eslint/no-unused-vars
|
||||
export const getAncestors = async (
|
||||
status: StatusWithRelations,
|
||||
fetcher: UserWithRelations | null,
|
||||
|
|
@ -154,7 +152,6 @@ export const getAncestors = async (
|
|||
* Return all the descendants of this post (recursive)
|
||||
* Temporary implementation, will be replaced with a recursive SQL query when Prisma adds support for it
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/require-await, @typescript-eslint/no-unused-vars
|
||||
export const getDescendants = async (
|
||||
status: StatusWithRelations,
|
||||
fetcher: UserWithRelations | null,
|
||||
|
|
@ -295,7 +292,10 @@ export const createNewStatus = async (data: {
|
|||
isReblog: false,
|
||||
uri:
|
||||
data.uri ||
|
||||
`${config.http.base_url}/statuses/FAKE-${crypto.randomUUID()}`,
|
||||
new URL(
|
||||
`/statuses/FAKE-${crypto.randomUUID()}`,
|
||||
config.http.base_url,
|
||||
).toString(),
|
||||
mentions: {
|
||||
connect: mentions.map((mention) => {
|
||||
return {
|
||||
|
|
@ -313,7 +313,12 @@ export const createNewStatus = async (data: {
|
|||
id: status.id,
|
||||
},
|
||||
data: {
|
||||
uri: data.uri || `${config.http.base_url}/statuses/${status.id}`,
|
||||
uri:
|
||||
data.uri ||
|
||||
new URL(
|
||||
`/statuses/${status.id}`,
|
||||
config.http.base_url,
|
||||
).toString(),
|
||||
},
|
||||
include: statusAndUserRelations,
|
||||
});
|
||||
|
|
@ -467,13 +472,10 @@ export const statusToAPI = async (
|
|||
card: null,
|
||||
content: status.content,
|
||||
emojis: status.emojis.map((emoji) => emojiToAPI(emoji)),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
favourited: !!(status.likes ?? []).find(
|
||||
(like) => like.likerId === user?.id,
|
||||
),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
favourites_count: (status.likes ?? []).length,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
media_attachments: (status.attachments ?? []).map(
|
||||
(a) => attachmentToAPI(a) as APIAttachment,
|
||||
),
|
||||
|
|
@ -485,7 +487,7 @@ export const statusToAPI = async (
|
|||
?.muting || false
|
||||
: false,
|
||||
pinned: status.pinnedBy.find((u) => u.id === user?.id) ? true : false,
|
||||
// TODO: Add pols
|
||||
// TODO: Add polls
|
||||
poll: null,
|
||||
reblog: status.reblog
|
||||
? await statusToAPI(status.reblog as unknown as StatusWithRelations)
|
||||
|
|
@ -501,9 +503,9 @@ export const statusToAPI = async (
|
|||
sensitive: status.sensitive,
|
||||
spoiler_text: status.spoilerText,
|
||||
tags: [],
|
||||
uri: `${config.http.base_url}/statuses/${status.id}`,
|
||||
uri: new URL(`/statuses/${status.id}`, config.http.base_url).toString(),
|
||||
visibility: "public",
|
||||
url: `${config.http.base_url}/statuses/${status.id}`,
|
||||
url: new URL(`/statuses/${status.id}`, config.http.base_url).toString(),
|
||||
bookmarked: false,
|
||||
quote: status.quotingPost
|
||||
? await statusToAPI(
|
||||
|
|
@ -567,7 +569,7 @@ export const statusToLysand = (status: StatusWithRelations): Note => {
|
|||
created_at: new Date(status.createdAt).toISOString(),
|
||||
id: status.id,
|
||||
author: status.authorId,
|
||||
uri: `${config.http.base_url}/users/${status.authorId}/statuses/${status.id}`,
|
||||
uri: new URL(`/statuses/${status.id}`, config.http.base_url).toString(),
|
||||
contents: [
|
||||
{
|
||||
content: status.content,
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ import { Prisma } from "@prisma/client";
|
|||
import { type Config, config } from "config-manager";
|
||||
import { htmlToText } from "html-to-text";
|
||||
import { client } from "~database/datasource";
|
||||
import { MediaBackendType } from "~packages/media-manager";
|
||||
import type { APIAccount } from "~types/entities/account";
|
||||
import type { APISource } from "~types/entities/source";
|
||||
import type { LysandUser } from "~types/lysand/Object";
|
||||
import { addEmojiIfNotExists, emojiToAPI, emojiToLysand } from "./Emoji";
|
||||
import { addInstanceIfNotExists } from "./Instance";
|
||||
import { userRelations } from "./relations";
|
||||
import { getUrl } from "./Attachment";
|
||||
|
||||
export interface AuthData {
|
||||
user: UserWithRelations | null;
|
||||
|
|
@ -35,14 +35,7 @@ export type UserWithRelations = Prisma.UserGetPayload<typeof userRelations2>;
|
|||
*/
|
||||
export const getAvatarUrl = (user: User, config: Config) => {
|
||||
if (!user.avatar) return config.defaults.avatar;
|
||||
if (config.media.backend === MediaBackendType.LOCAL) {
|
||||
return `${config.http.base_url}/media/${user.avatar}`;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
}
|
||||
if (config.media.backend === MediaBackendType.S3) {
|
||||
return `${config.s3.public_url}/${user.avatar}`;
|
||||
}
|
||||
return "";
|
||||
return getUrl(user.avatar, config);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -52,14 +45,7 @@ export const getAvatarUrl = (user: User, config: Config) => {
|
|||
*/
|
||||
export const getHeaderUrl = (user: User, config: Config) => {
|
||||
if (!user.header) return config.defaults.header;
|
||||
if (config.media.backend === MediaBackendType.LOCAL) {
|
||||
return `${config.http.base_url}/media/${user.header}`;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
}
|
||||
if (config.media.backend === MediaBackendType.S3) {
|
||||
return `${config.s3.public_url}/${user.header}`;
|
||||
}
|
||||
return "";
|
||||
return getUrl(user.header, config);
|
||||
};
|
||||
|
||||
export const getFromRequest = async (req: Request): Promise<AuthData> => {
|
||||
|
|
@ -224,16 +210,7 @@ export const createNewLocalUser = async (data: {
|
|||
id: user.id,
|
||||
},
|
||||
data: {
|
||||
uri: `${config.http.base_url}/users/${user.id}`,
|
||||
endpoints: {
|
||||
disliked: `${config.http.base_url}/users/${user.id}/disliked`,
|
||||
featured: `${config.http.base_url}/users/${user.id}/featured`,
|
||||
liked: `${config.http.base_url}/users/${user.id}/liked`,
|
||||
followers: `${config.http.base_url}/users/${user.id}/followers`,
|
||||
following: `${config.http.base_url}/users/${user.id}/following`,
|
||||
inbox: `${config.http.base_url}/users/${user.id}/inbox`,
|
||||
outbox: `${config.http.base_url}/users/${user.id}/outbox`,
|
||||
},
|
||||
uri: new URL(`/users/${user.id}`, config.http.base_url).toString(),
|
||||
},
|
||||
include: userRelations,
|
||||
});
|
||||
|
|
@ -399,13 +376,35 @@ export const userToLysand = (user: UserWithRelations): LysandUser => {
|
|||
},
|
||||
],
|
||||
created_at: new Date(user.createdAt).toISOString(),
|
||||
disliked: `${user.uri}/disliked`,
|
||||
featured: `${user.uri}/featured`,
|
||||
liked: `${user.uri}/liked`,
|
||||
followers: `${user.uri}/followers`,
|
||||
following: `${user.uri}/following`,
|
||||
inbox: `${user.uri}/inbox`,
|
||||
outbox: `${user.uri}/outbox`,
|
||||
|
||||
disliked: new URL(
|
||||
`/users/${user.id}/disliked`,
|
||||
config.http.base_url,
|
||||
).toString(),
|
||||
featured: new URL(
|
||||
`/users/${user.id}/featured`,
|
||||
config.http.base_url,
|
||||
).toString(),
|
||||
liked: new URL(
|
||||
`/users/${user.id}/liked`,
|
||||
config.http.base_url,
|
||||
).toString(),
|
||||
followers: new URL(
|
||||
`/users/${user.id}/followers`,
|
||||
config.http.base_url,
|
||||
).toString(),
|
||||
following: new URL(
|
||||
`/users/${user.id}/following`,
|
||||
config.http.base_url,
|
||||
).toString(),
|
||||
inbox: new URL(
|
||||
`/users/${user.id}/inbox`,
|
||||
config.http.base_url,
|
||||
).toString(),
|
||||
outbox: new URL(
|
||||
`/users/${user.id}/outbox`,
|
||||
config.http.base_url,
|
||||
).toString(),
|
||||
indexable: false,
|
||||
username: user.username,
|
||||
avatar: [
|
||||
|
|
@ -444,7 +443,10 @@ export const userToLysand = (user: UserWithRelations): LysandUser => {
|
|||
],
|
||||
})),
|
||||
public_key: {
|
||||
actor: `${config.http.base_url}/users/${user.id}`,
|
||||
actor: new URL(
|
||||
`/users/${user.id}`,
|
||||
config.http.base_url,
|
||||
).toString(),
|
||||
public_key: user.publicKey,
|
||||
},
|
||||
extensions: {
|
||||
|
|
|
|||
|
|
@ -174,7 +174,6 @@ export class CliBuilder {
|
|||
// Split the command into parts and iterate over them
|
||||
for (const part of command.categories) {
|
||||
// If this part doesn't exist in the current level of the tree, add it (__proto__ check to prevent prototype pollution)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!currentLevel[part] && part !== "__proto__") {
|
||||
// If this is the last part of the command, add the command itself
|
||||
if (
|
||||
|
|
@ -297,7 +296,6 @@ export class CliBuilder {
|
|||
type ExecuteFunction<T> = (
|
||||
instance: CliCommand,
|
||||
args: Partial<T>,
|
||||
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
||||
) => Promise<number> | Promise<void> | number | void;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -64,9 +64,7 @@ export class MediaBackend {
|
|||
* @returns The file as a File object
|
||||
*/
|
||||
public getFileByHash(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
file: string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
databaseHashFetcher: (sha256: string) => Promise<string>,
|
||||
): Promise<File | null> {
|
||||
return Promise.reject(
|
||||
|
|
@ -79,7 +77,6 @@ export class MediaBackend {
|
|||
* @param filename File name
|
||||
* @returns The file as a File object
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
public getFile(filename: string): Promise<File | null> {
|
||||
return Promise.reject(
|
||||
new Error("Do not call MediaBackend directly: use a subclass"),
|
||||
|
|
@ -91,7 +88,6 @@ export class MediaBackend {
|
|||
* @param file File to add
|
||||
* @returns Metadata about the uploaded file
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
public addFile(file: File): Promise<UploadedFileMetadata> {
|
||||
return Promise.reject(
|
||||
new Error("Do not call MediaBackend directly: use a subclass"),
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ export default defineConfig({
|
|||
},
|
||||
},
|
||||
define: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
__VERSION__: JSON.stringify(pkg.version),
|
||||
},
|
||||
ssr: {
|
||||
|
|
|
|||
48
server.ts
48
server.ts
|
|
@ -1,4 +1,9 @@
|
|||
import { errorResponse, jsonResponse } from "@response";
|
||||
import {
|
||||
clientResponse,
|
||||
errorResponse,
|
||||
jsonResponse,
|
||||
response,
|
||||
} from "@response";
|
||||
import type { Config } from "config-manager";
|
||||
import { matches } from "ip-matching";
|
||||
import type { LogManager, MultiLogManager } from "log-manager";
|
||||
|
|
@ -22,10 +27,7 @@ export const createServer = (
|
|||
for (const ip of config.http.banned_ips) {
|
||||
try {
|
||||
if (matches(ip, request_ip)) {
|
||||
return new Response(undefined, {
|
||||
status: 403,
|
||||
statusText: "Forbidden",
|
||||
});
|
||||
return errorResponse("Forbidden", 403);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`[-] Error while parsing banned IP "${ip}" `);
|
||||
|
|
@ -38,10 +40,7 @@ export const createServer = (
|
|||
|
||||
for (const agent of config.http.banned_user_agents) {
|
||||
if (new RegExp(agent).test(ua)) {
|
||||
return new Response(undefined, {
|
||||
status: 403,
|
||||
statusText: "Forbidden",
|
||||
});
|
||||
return errorResponse("Forbidden", 403);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +55,7 @@ export const createServer = (
|
|||
);
|
||||
|
||||
if (await file.exists()) {
|
||||
return new Response(file);
|
||||
return response(file);
|
||||
}
|
||||
await logger.log(
|
||||
LogLevel.ERROR,
|
||||
|
|
@ -81,7 +80,7 @@ export const createServer = (
|
|||
);
|
||||
|
||||
if (await file.exists()) {
|
||||
return new Response(file);
|
||||
return response(file);
|
||||
}
|
||||
await logger.log(
|
||||
LogLevel.ERROR,
|
||||
|
|
@ -126,12 +125,12 @@ export const createServer = (
|
|||
// Check for allowed requests
|
||||
// @ts-expect-error Stupid error
|
||||
if (!meta.allowedMethods.includes(req.method as string)) {
|
||||
return new Response(undefined, {
|
||||
status: 405,
|
||||
statusText: `Method not allowed: allowed methods are: ${meta.allowedMethods.join(
|
||||
return errorResponse(
|
||||
`Method not allowed: allowed methods are: ${meta.allowedMethods.join(
|
||||
", ",
|
||||
)}`,
|
||||
});
|
||||
405,
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Check for ratelimits
|
||||
|
|
@ -140,20 +139,14 @@ export const createServer = (
|
|||
// Check for authentication if required
|
||||
if (meta.auth.required) {
|
||||
if (!auth.user) {
|
||||
return new Response(undefined, {
|
||||
status: 401,
|
||||
statusText: "Unauthorized",
|
||||
});
|
||||
return errorResponse("Unauthorized", 401);
|
||||
}
|
||||
} else if (
|
||||
// @ts-expect-error Stupid error
|
||||
(meta.auth.requiredOnMethods ?? []).includes(req.method)
|
||||
) {
|
||||
if (!auth.user) {
|
||||
return new Response(undefined, {
|
||||
status: 401,
|
||||
statusText: "Unauthorized",
|
||||
});
|
||||
return errorResponse("Unauthorized", 401);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -167,10 +160,7 @@ export const createServer = (
|
|||
"Server.RouteRequestParser",
|
||||
e as Error,
|
||||
);
|
||||
return new Response(undefined, {
|
||||
status: 400,
|
||||
statusText: "Bad request",
|
||||
});
|
||||
return errorResponse("Bad request", 400);
|
||||
}
|
||||
|
||||
return await file.default(req.clone(), matchedRoute, {
|
||||
|
|
@ -196,7 +186,7 @@ export const createServer = (
|
|||
|
||||
// Serve from pages/dist/assets
|
||||
if (await file.exists()) {
|
||||
return new Response(file);
|
||||
return clientResponse(file);
|
||||
}
|
||||
return errorResponse("Asset not found", 404);
|
||||
}
|
||||
|
|
@ -207,7 +197,7 @@ export const createServer = (
|
|||
const file = Bun.file("./pages/dist/index.html");
|
||||
|
||||
// Serve from pages/dist
|
||||
return new Response(file);
|
||||
return clientResponse(file);
|
||||
}
|
||||
const proxy = await fetch(
|
||||
req.url.replace(
|
||||
|
|
|
|||
|
|
@ -19,7 +19,10 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
return xmlResponse(`
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
|
||||
<Link rel="lrdd" template="${config.http.base_url}/.well-known/webfinger?resource={uri}"/>
|
||||
<Link rel="lrdd" template="${new URL(
|
||||
"/.well-known/webfinger",
|
||||
config.http.base_url,
|
||||
).toString()}?resource={uri}"/>
|
||||
</XRD>
|
||||
`);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { apiRoute, applyConfig } from "@api";
|
||||
import { redirect } from "@response";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["GET"],
|
||||
|
|
@ -15,10 +16,8 @@ export const meta = applyConfig({
|
|||
export default apiRoute(async (req, matchedRoute, extraData) => {
|
||||
const config = await extraData.configManager.getConfig();
|
||||
|
||||
return new Response("", {
|
||||
status: 301,
|
||||
headers: {
|
||||
Location: `${config.http.base_url}/.well-known/nodeinfo/2.0`,
|
||||
},
|
||||
});
|
||||
return redirect(
|
||||
new URL("/.well-known/nodeinfo/2.0", config.http.base_url),
|
||||
301,
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -41,18 +41,11 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
links: [
|
||||
{
|
||||
rel: "self",
|
||||
type: "application/activity+json",
|
||||
href: `${config.http.base_url}/users/${user.username}/actor`,
|
||||
},
|
||||
{
|
||||
rel: "https://webfinger.net/rel/profile-page",
|
||||
type: "text/html",
|
||||
href: `${config.http.base_url}/users/${user.username}`,
|
||||
},
|
||||
{
|
||||
rel: "self",
|
||||
type: 'application/activity+json; profile="https://www.w3.org/ns/activitystreams"',
|
||||
href: `${config.http.base_url}/users/${user.username}/actor`,
|
||||
type: "application/json",
|
||||
href: new URL(
|
||||
`/users/${user.id}`,
|
||||
config.http.base_url,
|
||||
).toString(),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { apiRoute, applyConfig } from "@api";
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { client } from "~database/datasource";
|
||||
import { userToAPI } from "~database/entities/User";
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { apiRoute, applyConfig } from "@api";
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { client } from "~database/datasource";
|
||||
import { userToAPI } from "~database/entities/User";
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ export default apiRoute<{
|
|||
|
||||
if (!self) return errorResponse("Unauthorized", 401);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { notifications, duration } = extraData.parsedRequest;
|
||||
|
||||
const user = await client.user.findUnique({
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
import { apiRoute, applyConfig } from "@api";
|
||||
import type { Prisma, Status, User } from "@prisma/client";
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { fetchTimeline } from "@timelines";
|
||||
import { client } from "~database/datasource";
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { apiRoute, applyConfig } from "@api";
|
||||
import { jsonResponse } from "@response";
|
||||
import { jsonResponse, response } from "@response";
|
||||
import { tempmailDomains } from "@tempmail";
|
||||
import ISO6391 from "iso-639-1";
|
||||
import { client } from "~database/datasource";
|
||||
|
|
@ -200,7 +200,5 @@ export default apiRoute<{
|
|||
email: body.email ?? "",
|
||||
});
|
||||
|
||||
return new Response("", {
|
||||
status: 200,
|
||||
});
|
||||
return response(null, 200);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { apiRoute, applyConfig } from "@api";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { fetchTimeline } from "@timelines";
|
||||
import { client } from "~database/datasource";
|
||||
import { userToAPI } from "~database/entities/User";
|
||||
import { userToAPI, type UserWithRelations } from "~database/entities/User";
|
||||
import { userRelations } from "~database/entities/relations";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -13,25 +14,48 @@ export const meta = applyConfig({
|
|||
},
|
||||
auth: {
|
||||
required: true,
|
||||
oauthPermissions: ["read:blocks"],
|
||||
},
|
||||
});
|
||||
|
||||
export default apiRoute(async (req, matchedRoute, extraData) => {
|
||||
export default apiRoute<{
|
||||
max_id?: string;
|
||||
since_id?: string;
|
||||
min_id?: string;
|
||||
limit?: number;
|
||||
}>(async (req, matchedRoute, extraData) => {
|
||||
const { user } = extraData.auth;
|
||||
|
||||
if (!user) return errorResponse("Unauthorized", 401);
|
||||
|
||||
const blocks = await client.user.findMany({
|
||||
where: {
|
||||
relationshipSubjects: {
|
||||
some: {
|
||||
ownerId: user.id,
|
||||
blocking: true,
|
||||
const { max_id, since_id, limit = 40 } = extraData.parsedRequest;
|
||||
|
||||
const { objects: blocks, link } = await fetchTimeline<UserWithRelations>(
|
||||
client.user,
|
||||
{
|
||||
where: {
|
||||
relationshipSubjects: {
|
||||
some: {
|
||||
ownerId: user.id,
|
||||
blocking: true,
|
||||
},
|
||||
},
|
||||
id: {
|
||||
lt: max_id,
|
||||
gte: since_id,
|
||||
},
|
||||
},
|
||||
include: userRelations,
|
||||
take: Number(limit),
|
||||
},
|
||||
include: userRelations,
|
||||
});
|
||||
req,
|
||||
);
|
||||
|
||||
return jsonResponse(blocks.map((u) => userToAPI(u)));
|
||||
return jsonResponse(
|
||||
blocks.map((u) => userToAPI(u)),
|
||||
200,
|
||||
{
|
||||
Link: link,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
import { apiRoute, applyConfig } from "@api";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { fetchTimeline } from "@timelines";
|
||||
import { client } from "~database/datasource";
|
||||
import { statusToAPI } from "~database/entities/Status";
|
||||
import {
|
||||
statusToAPI,
|
||||
type StatusWithRelations,
|
||||
} from "~database/entities/Status";
|
||||
import { statusAndUserRelations } from "~database/entities/relations";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -32,35 +36,29 @@ export default apiRoute<{
|
|||
|
||||
if (!user) return errorResponse("Unauthorized", 401);
|
||||
|
||||
const objects = await client.status.findMany({
|
||||
where: {
|
||||
id: {
|
||||
lt: max_id ?? undefined,
|
||||
gte: since_id ?? undefined,
|
||||
gt: min_id ?? undefined,
|
||||
},
|
||||
likes: {
|
||||
some: {
|
||||
likerId: user.id,
|
||||
const { objects, link } = await fetchTimeline<StatusWithRelations>(
|
||||
client.status,
|
||||
{
|
||||
where: {
|
||||
id: {
|
||||
lt: max_id ?? undefined,
|
||||
gte: since_id ?? undefined,
|
||||
gt: min_id ?? undefined,
|
||||
},
|
||||
likes: {
|
||||
some: {
|
||||
likerId: user.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
include: statusAndUserRelations,
|
||||
take: Number(limit),
|
||||
orderBy: {
|
||||
id: "desc",
|
||||
},
|
||||
},
|
||||
include: statusAndUserRelations,
|
||||
take: Number(limit),
|
||||
orderBy: {
|
||||
id: "desc",
|
||||
},
|
||||
});
|
||||
|
||||
// Constuct HTTP Link header (next and prev)
|
||||
const linkHeader = [];
|
||||
if (objects.length > 0) {
|
||||
const urlWithoutQuery = req.url.split("?")[0];
|
||||
linkHeader.push(
|
||||
`<${urlWithoutQuery}?max_id=${objects.at(-1)?.id}>; rel="next"`,
|
||||
`<${urlWithoutQuery}?min_id=${objects[0].id}>; rel="prev"`,
|
||||
);
|
||||
}
|
||||
req,
|
||||
);
|
||||
|
||||
return jsonResponse(
|
||||
await Promise.all(
|
||||
|
|
@ -68,7 +66,7 @@ export default apiRoute<{
|
|||
),
|
||||
200,
|
||||
{
|
||||
Link: linkHeader.join(", "),
|
||||
Link: link,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { apiRoute, applyConfig } from "@api";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { fetchTimeline } from "@timelines";
|
||||
import { client } from "~database/datasource";
|
||||
import { userToAPI } from "~database/entities/User";
|
||||
import { userToAPI, type UserWithRelations } from "~database/entities/User";
|
||||
import { userRelations } from "~database/entities/relations";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -32,42 +33,36 @@ export default apiRoute<{
|
|||
|
||||
if (!user) return errorResponse("Unauthorized", 401);
|
||||
|
||||
const objects = await client.user.findMany({
|
||||
where: {
|
||||
id: {
|
||||
lt: max_id ?? undefined,
|
||||
gte: since_id ?? undefined,
|
||||
gt: min_id ?? undefined,
|
||||
},
|
||||
relationships: {
|
||||
some: {
|
||||
subjectId: user.id,
|
||||
requested: true,
|
||||
const { objects, link } = await fetchTimeline<UserWithRelations>(
|
||||
client.user,
|
||||
{
|
||||
where: {
|
||||
id: {
|
||||
lt: max_id ?? undefined,
|
||||
gte: since_id ?? undefined,
|
||||
gt: min_id ?? undefined,
|
||||
},
|
||||
relationships: {
|
||||
some: {
|
||||
subjectId: user.id,
|
||||
requested: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
include: userRelations,
|
||||
take: Number(limit),
|
||||
orderBy: {
|
||||
id: "desc",
|
||||
},
|
||||
},
|
||||
include: userRelations,
|
||||
take: Number(limit),
|
||||
orderBy: {
|
||||
id: "desc",
|
||||
},
|
||||
});
|
||||
|
||||
// Constuct HTTP Link header (next and prev)
|
||||
const linkHeader = [];
|
||||
if (objects.length > 0) {
|
||||
const urlWithoutQuery = req.url.split("?")[0];
|
||||
linkHeader.push(
|
||||
`<${urlWithoutQuery}?max_id=${objects.at(-1)?.id}>; rel="next"`,
|
||||
`<${urlWithoutQuery}?min_id=${objects[0].id}>; rel="prev"`,
|
||||
);
|
||||
}
|
||||
req,
|
||||
);
|
||||
|
||||
return jsonResponse(
|
||||
objects.map((user) => userToAPI(user)),
|
||||
200,
|
||||
{
|
||||
Link: linkHeader.join(", "),
|
||||
Link: link,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { apiRoute, applyConfig } from "@api";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { errorResponse, jsonResponse, response } from "@response";
|
||||
import type { MediaBackend } from "media-manager";
|
||||
import { MediaBackendType } from "media-manager";
|
||||
import { client } from "~database/datasource";
|
||||
|
|
@ -52,9 +52,7 @@ export default apiRoute<{
|
|||
if (attachment.url) {
|
||||
return jsonResponse(attachmentToAPI(attachment));
|
||||
}
|
||||
return new Response(null, {
|
||||
status: 206,
|
||||
});
|
||||
return response(null, 206);
|
||||
}
|
||||
case "PUT": {
|
||||
const { description, thumbnail } = extraData.parsedRequest;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { apiRoute, applyConfig } from "@api";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { fetchTimeline } from "@timelines";
|
||||
import { client } from "~database/datasource";
|
||||
import { userToAPI } from "~database/entities/User";
|
||||
import { userToAPI, type UserWithRelations } from "~database/entities/User";
|
||||
import { userRelations } from "~database/entities/relations";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -13,25 +14,40 @@ export const meta = applyConfig({
|
|||
},
|
||||
auth: {
|
||||
required: true,
|
||||
oauthPermissions: ["read:mutes"],
|
||||
},
|
||||
});
|
||||
|
||||
export default apiRoute(async (req, matchedRoute, extraData) => {
|
||||
export default apiRoute<{
|
||||
max_id?: string;
|
||||
since_id?: string;
|
||||
limit?: number;
|
||||
}>(async (req, matchedRoute, extraData) => {
|
||||
const { user } = extraData.auth;
|
||||
const { max_id, since_id, limit = 40 } = extraData.parsedRequest;
|
||||
|
||||
if (!user) return errorResponse("Unauthorized", 401);
|
||||
|
||||
const blocks = await client.user.findMany({
|
||||
where: {
|
||||
relationshipSubjects: {
|
||||
some: {
|
||||
ownerId: user.id,
|
||||
muting: true,
|
||||
const { objects: blocks, link } = await fetchTimeline<UserWithRelations>(
|
||||
client.user,
|
||||
{
|
||||
where: {
|
||||
relationshipSubjects: {
|
||||
some: {
|
||||
ownerId: user.id,
|
||||
muting: true,
|
||||
},
|
||||
},
|
||||
id: {
|
||||
lt: max_id,
|
||||
gte: since_id,
|
||||
},
|
||||
},
|
||||
include: userRelations,
|
||||
take: Number(limit),
|
||||
},
|
||||
include: userRelations,
|
||||
});
|
||||
req,
|
||||
);
|
||||
|
||||
return jsonResponse(blocks.map((u) => userToAPI(u)));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { apiRoute, applyConfig } from "@api";
|
||||
import type { Prisma } from "@prisma/client";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { fetchTimeline } from "@timelines";
|
||||
import { client } from "~database/datasource";
|
||||
import { notificationToAPI } from "~database/entities/Notification";
|
||||
import {
|
||||
|
|
@ -50,53 +52,49 @@ export default apiRoute<{
|
|||
return errorResponse("Can't use both types and exclude_types", 400);
|
||||
}
|
||||
|
||||
const objects = await client.notification.findMany({
|
||||
where: {
|
||||
notifiedId: user.id,
|
||||
id: {
|
||||
lt: max_id,
|
||||
gt: min_id,
|
||||
gte: since_id,
|
||||
const { objects, link } = await fetchTimeline<
|
||||
Prisma.NotificationGetPayload<{
|
||||
include: {
|
||||
account: {
|
||||
include: typeof userRelations;
|
||||
};
|
||||
status: {
|
||||
include: typeof statusAndUserRelations;
|
||||
};
|
||||
};
|
||||
}>
|
||||
>(
|
||||
client.notification,
|
||||
{
|
||||
where: {
|
||||
id: {
|
||||
lt: max_id ?? undefined,
|
||||
gte: since_id ?? undefined,
|
||||
gt: min_id ?? undefined,
|
||||
},
|
||||
accountId: account_id,
|
||||
},
|
||||
type: {
|
||||
in: types,
|
||||
notIn: exclude_types,
|
||||
include: {
|
||||
account: {
|
||||
include: userRelations,
|
||||
},
|
||||
status: {
|
||||
include: statusAndUserRelations,
|
||||
},
|
||||
},
|
||||
accountId: account_id,
|
||||
orderBy: {
|
||||
id: "desc",
|
||||
},
|
||||
take: Number(limit),
|
||||
},
|
||||
include: {
|
||||
account: {
|
||||
include: userRelations,
|
||||
},
|
||||
status: {
|
||||
include: statusAndUserRelations,
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
id: "desc",
|
||||
},
|
||||
take: Number(limit),
|
||||
});
|
||||
|
||||
// Constuct HTTP Link header (next and prev)
|
||||
const linkHeader = [];
|
||||
if (objects.length > 0) {
|
||||
const urlWithoutQuery = req.url.split("?")[0];
|
||||
linkHeader.push(
|
||||
`<${urlWithoutQuery}?max_id=${objects[0].id}&limit=${limit}>; rel="next"`,
|
||||
);
|
||||
linkHeader.push(
|
||||
`<${urlWithoutQuery}?since_id=${
|
||||
objects.at(-1)?.id
|
||||
}&limit=${limit}>; rel="prev"`,
|
||||
);
|
||||
}
|
||||
req,
|
||||
);
|
||||
|
||||
return jsonResponse(
|
||||
await Promise.all(objects.map((n) => notificationToAPI(n))),
|
||||
200,
|
||||
{
|
||||
Link: linkHeader.join(", "),
|
||||
Link: link,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import { apiRoute, applyConfig } from "@api";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { client } from "~database/datasource";
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import { apiRoute, applyConfig } from "@api";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { fetchTimeline } from "@timelines";
|
||||
import { client } from "~database/datasource";
|
||||
import { isViewableByUser } from "~database/entities/Status";
|
||||
import { userToAPI } from "~database/entities/User";
|
||||
import { userToAPI, type UserWithRelations } from "~database/entities/User";
|
||||
import {
|
||||
statusAndUserRelations,
|
||||
userRelations,
|
||||
|
|
@ -42,63 +43,48 @@ export default apiRoute<{
|
|||
if (!status || !isViewableByUser(status, user))
|
||||
return errorResponse("Record not found", 404);
|
||||
|
||||
const {
|
||||
max_id = null,
|
||||
min_id = null,
|
||||
since_id = null,
|
||||
limit = 40,
|
||||
} = extraData.parsedRequest;
|
||||
const { max_id, min_id, since_id, limit = 40 } = extraData.parsedRequest;
|
||||
|
||||
// Check for limit limits
|
||||
if (limit > 80) return errorResponse("Invalid limit (maximum is 80)", 400);
|
||||
if (limit < 1) return errorResponse("Invalid limit", 400);
|
||||
|
||||
const objects = await client.user.findMany({
|
||||
where: {
|
||||
likes: {
|
||||
some: {
|
||||
likedId: status.id,
|
||||
const { objects, link } = await fetchTimeline<UserWithRelations>(
|
||||
client.user,
|
||||
{
|
||||
where: {
|
||||
likes: {
|
||||
some: {
|
||||
likedId: status.id,
|
||||
},
|
||||
},
|
||||
id: {
|
||||
lt: max_id,
|
||||
gte: since_id,
|
||||
gt: min_id,
|
||||
},
|
||||
},
|
||||
id: {
|
||||
lt: max_id ?? undefined,
|
||||
gte: since_id ?? undefined,
|
||||
gt: min_id ?? undefined,
|
||||
},
|
||||
},
|
||||
include: {
|
||||
...userRelations,
|
||||
likes: {
|
||||
where: {
|
||||
likedId: status.id,
|
||||
include: {
|
||||
...userRelations,
|
||||
likes: {
|
||||
where: {
|
||||
likedId: status.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
take: Number(limit),
|
||||
orderBy: {
|
||||
id: "desc",
|
||||
},
|
||||
},
|
||||
take: Number(limit),
|
||||
orderBy: {
|
||||
id: "desc",
|
||||
},
|
||||
});
|
||||
|
||||
// Constuct HTTP Link header (next and prev)
|
||||
const linkHeader = [];
|
||||
if (objects.length > 0) {
|
||||
const urlWithoutQuery = req.url.split("?")[0];
|
||||
linkHeader.push(
|
||||
`<${urlWithoutQuery}?max_id=${objects[0].id}&limit=${limit}>; rel="next"`,
|
||||
);
|
||||
linkHeader.push(
|
||||
`<${urlWithoutQuery}?since_id=${
|
||||
objects[objects.length - 1].id
|
||||
}&limit=${limit}>; rel="prev"`,
|
||||
);
|
||||
}
|
||||
req,
|
||||
);
|
||||
|
||||
return jsonResponse(
|
||||
objects.map((user) => userToAPI(user)),
|
||||
200,
|
||||
{
|
||||
Link: linkHeader.join(", "),
|
||||
Link: link,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import { apiRoute, applyConfig } from "@api";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { client } from "~database/datasource";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import { apiRoute, applyConfig } from "@api";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { client } from "~database/datasource";
|
||||
|
|
@ -58,7 +57,10 @@ export default apiRoute<{
|
|||
authorId: user.id,
|
||||
reblogId: status.id,
|
||||
isReblog: true,
|
||||
uri: `${config.http.base_url}/statuses/FAKE-${crypto.randomUUID()}`,
|
||||
uri: new URL(
|
||||
`/statuses/FAKE-${crypto.randomUUID()}`,
|
||||
config.http.base_url,
|
||||
).toString(),
|
||||
visibility,
|
||||
sensitive: false,
|
||||
},
|
||||
|
|
@ -68,7 +70,10 @@ export default apiRoute<{
|
|||
await client.status.update({
|
||||
where: { id: newReblog.id },
|
||||
data: {
|
||||
uri: `${config.http.base_url}/statuses/${newReblog.id}`,
|
||||
uri: new URL(
|
||||
`/statuses/${newReblog.id}`,
|
||||
config.http.base_url,
|
||||
).toString(),
|
||||
},
|
||||
include: statusAndUserRelations,
|
||||
});
|
||||
|
|
@ -89,7 +94,10 @@ export default apiRoute<{
|
|||
await statusToAPI(
|
||||
{
|
||||
...newReblog,
|
||||
uri: `${config.http.base_url}/statuses/${newReblog.id}`,
|
||||
uri: new URL(
|
||||
`/statuses/${newReblog.id}`,
|
||||
config.http.base_url,
|
||||
).toString(),
|
||||
},
|
||||
user,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import { apiRoute, applyConfig } from "@api";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { fetchTimeline } from "@timelines";
|
||||
import { client } from "~database/datasource";
|
||||
import { isViewableByUser } from "~database/entities/Status";
|
||||
import { userToAPI } from "~database/entities/User";
|
||||
import { type UserWithRelations, userToAPI } from "~database/entities/User";
|
||||
import {
|
||||
statusAndUserRelations,
|
||||
userRelations,
|
||||
|
|
@ -53,53 +54,43 @@ export default apiRoute<{
|
|||
if (limit > 80) return errorResponse("Invalid limit (maximum is 80)", 400);
|
||||
if (limit < 1) return errorResponse("Invalid limit", 400);
|
||||
|
||||
const objects = await client.user.findMany({
|
||||
where: {
|
||||
statuses: {
|
||||
some: {
|
||||
reblogId: status.id,
|
||||
const { objects, link } = await fetchTimeline<UserWithRelations>(
|
||||
client.user,
|
||||
{
|
||||
where: {
|
||||
statuses: {
|
||||
some: {
|
||||
reblogId: status.id,
|
||||
},
|
||||
},
|
||||
id: {
|
||||
lt: max_id ?? undefined,
|
||||
gte: since_id ?? undefined,
|
||||
gt: min_id ?? undefined,
|
||||
},
|
||||
},
|
||||
id: {
|
||||
lt: max_id ?? undefined,
|
||||
gte: since_id ?? undefined,
|
||||
gt: min_id ?? undefined,
|
||||
},
|
||||
},
|
||||
include: {
|
||||
...userRelations,
|
||||
statuses: {
|
||||
where: {
|
||||
reblogId: status.id,
|
||||
include: {
|
||||
...userRelations,
|
||||
statuses: {
|
||||
where: {
|
||||
reblogId: status.id,
|
||||
},
|
||||
include: statusAndUserRelations,
|
||||
},
|
||||
include: statusAndUserRelations,
|
||||
},
|
||||
take: Number(limit),
|
||||
orderBy: {
|
||||
id: "desc",
|
||||
},
|
||||
},
|
||||
take: Number(limit),
|
||||
orderBy: {
|
||||
id: "desc",
|
||||
},
|
||||
});
|
||||
|
||||
// Constuct HTTP Link header (next and prev)
|
||||
const linkHeader = [];
|
||||
if (objects.length > 0) {
|
||||
const urlWithoutQuery = req.url.split("?")[0];
|
||||
linkHeader.push(
|
||||
`<${urlWithoutQuery}?max_id=${objects[0].id}&limit=${limit}>; rel="next"`,
|
||||
);
|
||||
linkHeader.push(
|
||||
`<${urlWithoutQuery}?since_id=${
|
||||
objects[objects.length - 1].id
|
||||
}&limit=${limit}>; rel="prev"`,
|
||||
);
|
||||
}
|
||||
req,
|
||||
);
|
||||
|
||||
return jsonResponse(
|
||||
objects.map((user) => userToAPI(user)),
|
||||
200,
|
||||
{
|
||||
Link: linkHeader.join(", "),
|
||||
Link: link,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import { apiRoute, applyConfig } from "@api";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { client } from "~database/datasource";
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { apiRoute, applyConfig } from "@api";
|
||||
import { errorResponse } from "@response";
|
||||
import { errorResponse, response } from "@response";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["GET"],
|
||||
route: "/media/:id",
|
||||
route: "/api/v1/media/:id",
|
||||
ratelimits: {
|
||||
max: 100,
|
||||
duration: 60,
|
||||
|
|
@ -35,11 +35,9 @@ export default apiRoute(async (req, matchedRoute) => {
|
|||
if (!(await file.exists())) return errorResponse("File not found", 404);
|
||||
|
||||
// Can't directly copy file into Response because this crashes Bun for now
|
||||
return new Response(buffer, {
|
||||
headers: {
|
||||
"Content-Type": file.type || "application/octet-stream",
|
||||
"Content-Length": `${file.size - start}`,
|
||||
"Content-Range": `bytes ${start}-${end}/${file.size}`,
|
||||
},
|
||||
return response(buffer, 200, {
|
||||
"Content-Type": file.type || "application/octet-stream",
|
||||
"Content-Length": `${file.size - start}`,
|
||||
"Content-Range": `bytes ${start}-${end}/${file.size}`,
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -157,7 +157,6 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
return redirectToLogin("No user found with that account");
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!flow.application) return redirectToLogin("Invalid client_id");
|
||||
|
||||
const code = randomBytes(32).toString("hex");
|
||||
|
|
|
|||
|
|
@ -51,14 +51,11 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
last: `${host}/users/${uuid}/outbox?page=1`,
|
||||
total_items: totalStatuses,
|
||||
// Server actor
|
||||
author: `${config.http.base_url}/users/actor`,
|
||||
next:
|
||||
statuses.length === 20
|
||||
? `${host}/users/${uuid}/outbox?page=${pageNumber + 1}`
|
||||
: undefined,
|
||||
prev:
|
||||
pageNumber > 1
|
||||
? `${host}/users/${uuid}/outbox?page=${pageNumber - 1}`
|
||||
author: new URL("/users/actor", config.http.base_url).toString(),
|
||||
next: statuses.length === 20
|
||||
? new URL(`/users/${uuid}/outbox?page=${pageNumber + 1}`, config.http.base_url).toString(),
|
||||
prev: pageNumber > 1
|
||||
? new URL(`/users/${uuid}/outbox?page=${pageNumber - 1}`, config.http.base_url).toString()
|
||||
: undefined,
|
||||
items: statuses.map((s) => statusToLysand(s)),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ describe("API Tests", () => {
|
|||
expect(account.statuses_count).toBe(0);
|
||||
expect(account.note).toBe("");
|
||||
expect(account.url).toBe(
|
||||
`${config.http.base_url}/users/${user.id}`,
|
||||
new URL(`/users/${user.id}`, config.http.base_url).toString(),
|
||||
);
|
||||
expect(account.avatar).toBeDefined();
|
||||
expect(account.avatar_static).toBeDefined();
|
||||
|
|
|
|||
|
|
@ -136,7 +136,6 @@ describe("API Tests", () => {
|
|||
"application/json",
|
||||
);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||
status = (await response.json()) as APIStatus;
|
||||
expect(status.content).toContain("Hello, world!");
|
||||
expect(status.visibility).toBe("public");
|
||||
|
|
@ -184,7 +183,6 @@ describe("API Tests", () => {
|
|||
"application/json",
|
||||
);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||
status2 = (await response.json()) as APIStatus;
|
||||
expect(status2.content).toContain("This is a reply!");
|
||||
expect(status2.visibility).toBe("public");
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ describe("POST /api/v1/apps/", () => {
|
|||
expect(response.status).toBe(200);
|
||||
expect(response.headers.get("content-type")).toBe("application/json");
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const json = await response.json();
|
||||
|
||||
expect(json).toEqual({
|
||||
|
|
@ -53,9 +52,7 @@ describe("POST /api/v1/apps/", () => {
|
|||
vapid_link: null,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
|
||||
client_id = json.client_id;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
|
||||
client_secret = json.client_secret;
|
||||
});
|
||||
});
|
||||
|
|
@ -111,7 +108,6 @@ describe("POST /oauth/token/", () => {
|
|||
}),
|
||||
);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const json = await response.json();
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
|
|
@ -123,7 +119,6 @@ describe("POST /oauth/token/", () => {
|
|||
created_at: expect.any(Number),
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
|
||||
token = json;
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ export const applyConfig = (routeMeta: APIRouteMeta) => {
|
|||
newMeta.ratelimits.duration *= config.ratelimits.duration_coeff;
|
||||
newMeta.ratelimits.max *= config.ratelimits.max_coeff;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (config.custom_ratelimits[routeMeta.route]) {
|
||||
newMeta.ratelimits = config.custom_ratelimits[routeMeta.route];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { config } from "config-manager";
|
||||
|
||||
export const oauthRedirectUri = (issuer: string) =>
|
||||
`${config.http.base_url}/oauth/callback/${issuer}`;
|
||||
new URL(`/oauth/callback/${issuer}`, config.http.base_url).toString();
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import type { APActivity, APObject } from "activitypub-types";
|
||||
import type { NodeObject } from "jsonld";
|
||||
|
||||
export const jsonResponse = (
|
||||
data: object,
|
||||
export const response = (
|
||||
data: BodyInit | null = null,
|
||||
status = 200,
|
||||
headers: Record<string, string> = {},
|
||||
) => {
|
||||
return new Response(JSON.stringify(data), {
|
||||
return new Response(data, {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Frame-Options": "DENY",
|
||||
|
|
@ -27,12 +27,32 @@ export const jsonResponse = (
|
|||
});
|
||||
};
|
||||
|
||||
export const clientResponse = (
|
||||
data: BodyInit | null = null,
|
||||
status = 200,
|
||||
headers: Record<string, string> = {},
|
||||
) => {
|
||||
return response(data, status, {
|
||||
...headers,
|
||||
"Content-Security-Policy":
|
||||
"default-src 'none'; frame-ancestors 'none'; form-action 'none'; connect-src 'self' blob: https: wss:; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; object-src 'none'; media-src 'self'; frame-src 'none'; worker-src 'self'; manifest-src 'self'; prefetch-src 'self'; base-uri 'none';",
|
||||
});
|
||||
};
|
||||
|
||||
export const jsonResponse = (
|
||||
data: object,
|
||||
status = 200,
|
||||
headers: Record<string, string> = {},
|
||||
) => {
|
||||
return response(JSON.stringify(data), status, {
|
||||
"Content-Type": "application/json",
|
||||
...headers,
|
||||
});
|
||||
};
|
||||
|
||||
export const xmlResponse = (data: string, status = 200) => {
|
||||
return new Response(data, {
|
||||
headers: {
|
||||
"Content-Type": "application/xml",
|
||||
},
|
||||
status,
|
||||
return response(data, status, {
|
||||
"Content-Type": "application/xml",
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -40,11 +60,8 @@ export const jsonLdResponse = (
|
|||
data: NodeObject | APActivity | APObject,
|
||||
status = 200,
|
||||
) => {
|
||||
return new Response(JSON.stringify(data), {
|
||||
headers: {
|
||||
"Content-Type": "application/activity+json",
|
||||
},
|
||||
status,
|
||||
return response(JSON.stringify(data), status, {
|
||||
"Content-Type": "application/activity+json",
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -56,3 +73,9 @@ export const errorResponse = (error: string, status = 500) => {
|
|||
status,
|
||||
);
|
||||
};
|
||||
|
||||
export const redirect = (url: string | URL, status = 302) => {
|
||||
return response(null, status, {
|
||||
Location: url.toString(),
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,8 +1,14 @@
|
|||
import type { Status, User, Prisma } from "@prisma/client";
|
||||
import type { Status, User, Prisma, Notification } from "@prisma/client";
|
||||
|
||||
export async function fetchTimeline<T extends User | Status>(
|
||||
model: Prisma.StatusDelegate | Prisma.UserDelegate,
|
||||
args: Prisma.StatusFindManyArgs | Prisma.UserFindManyArgs,
|
||||
export async function fetchTimeline<T extends User | Status | Notification>(
|
||||
model:
|
||||
| Prisma.StatusDelegate
|
||||
| Prisma.UserDelegate
|
||||
| Prisma.NotificationDelegate,
|
||||
args:
|
||||
| Prisma.StatusFindManyArgs
|
||||
| Prisma.UserFindManyArgs
|
||||
| Prisma.NotificationFindManyArgs,
|
||||
req: Request,
|
||||
) {
|
||||
// BEFORE: Before in a top-to-bottom order, so the most recent posts
|
||||
|
|
|
|||
Loading…
Reference in a new issue