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