mirror of
https://github.com/versia-pub/server.git
synced 2025-12-06 08:28:19 +01:00
Add bait mode, fix bugs
This commit is contained in:
parent
d633116571
commit
480fcb363f
13
build.ts
13
build.ts
|
|
@ -1,12 +1,6 @@
|
||||||
// Delete dist directory
|
// Delete dist directory
|
||||||
import chalk from "chalk";
|
|
||||||
import { rm, cp, mkdir, exists } from "fs/promises";
|
import { rm, cp, mkdir, exists } from "fs/promises";
|
||||||
|
import { rawRoutes } from "~routes";
|
||||||
console.log(
|
|
||||||
chalk.red(
|
|
||||||
"Warning: Build is currently broken due to a bug in Bun causing it not to parse dynamic imports"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!(await exists("./pages/dist"))) {
|
if (!(await exists("./pages/dist"))) {
|
||||||
console.log("Please build the Vite server first, or use `bun prod-build`");
|
console.log("Please build the Vite server first, or use `bun prod-build`");
|
||||||
|
|
@ -25,6 +19,8 @@ await Bun.build({
|
||||||
process.cwd() + "/index.ts",
|
process.cwd() + "/index.ts",
|
||||||
process.cwd() + "/prisma.ts",
|
process.cwd() + "/prisma.ts",
|
||||||
process.cwd() + "/cli.ts",
|
process.cwd() + "/cli.ts",
|
||||||
|
// Force Bun to include endpoints
|
||||||
|
...Object.values(rawRoutes),
|
||||||
],
|
],
|
||||||
outdir: process.cwd() + "/dist",
|
outdir: process.cwd() + "/dist",
|
||||||
target: "bun",
|
target: "bun",
|
||||||
|
|
@ -47,4 +43,7 @@ await cp(process.cwd() + "/pages/dist", process.cwd() + "/dist/pages/", {
|
||||||
recursive: true,
|
recursive: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Copy the Bee Movie script from pages
|
||||||
|
await cp(process.cwd() + "/pages/beemovie.txt", process.cwd() + "/dist/pages/");
|
||||||
|
|
||||||
console.log(`Built!`);
|
console.log(`Built!`);
|
||||||
|
|
|
||||||
103
cli.ts
103
cli.ts
|
|
@ -19,6 +19,28 @@ const args = process.argv;
|
||||||
|
|
||||||
const config = await new ConfigManager({}).getConfig();
|
const config = await new ConfigManager({}).getConfig();
|
||||||
|
|
||||||
|
const filterObjects = <T extends object>(output: T[], fields: string[]) => {
|
||||||
|
if (fields.length === 0) return output;
|
||||||
|
|
||||||
|
return output.map(element => {
|
||||||
|
// If fields is specified, only include provided fields
|
||||||
|
// This is a bit of a mess
|
||||||
|
if (fields.length > 0) {
|
||||||
|
const keys = Object.keys(element);
|
||||||
|
const filteredKeys = keys.filter(key => fields.includes(key));
|
||||||
|
return Object.entries(element)
|
||||||
|
.filter(([key]) => filteredKeys.includes(key))
|
||||||
|
.reduce((acc, [key, value]) => {
|
||||||
|
// @ts-expect-error This is fine
|
||||||
|
acc[key] = value;
|
||||||
|
return acc;
|
||||||
|
}, {}) as Partial<T>;
|
||||||
|
} else {
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const cliBuilder = new CliBuilder([
|
const cliBuilder = new CliBuilder([
|
||||||
new CliCommand<{
|
new CliCommand<{
|
||||||
username: string;
|
username: string;
|
||||||
|
|
@ -270,7 +292,7 @@ const cliBuilder = new CliBuilder([
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
async (instance: CliCommand, args) => {
|
async (instance: CliCommand, args) => {
|
||||||
const { admins, help } = args;
|
const { admins, help, fields = [] } = args;
|
||||||
|
|
||||||
if (help) {
|
if (help) {
|
||||||
instance.displayHelp();
|
instance.displayHelp();
|
||||||
|
|
@ -281,16 +303,21 @@ const cliBuilder = new CliBuilder([
|
||||||
console.log(`${chalk.red(`✗`)} Invalid format`);
|
console.log(`${chalk.red(`✗`)} Invalid format`);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
const users = filterObjects(
|
||||||
const users = await client.user.findMany({
|
await client.user.findMany({
|
||||||
where: {
|
where: {
|
||||||
isAdmin: admins || undefined,
|
isAdmin: admins || undefined,
|
||||||
},
|
},
|
||||||
take: args.limit ?? 200,
|
take: args.limit ?? 200,
|
||||||
include: {
|
include: {
|
||||||
instance: true,
|
instance:
|
||||||
},
|
fields.length == 0
|
||||||
});
|
? true
|
||||||
|
: fields.includes("instance"),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
fields
|
||||||
|
);
|
||||||
|
|
||||||
if (args.redact) {
|
if (args.redact) {
|
||||||
for (const user of users) {
|
for (const user of users) {
|
||||||
|
|
@ -301,19 +328,6 @@ const cliBuilder = new CliBuilder([
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.fields) {
|
|
||||||
for (const user of users) {
|
|
||||||
const keys = Object.keys(user);
|
|
||||||
for (const key of keys) {
|
|
||||||
if (!args.fields.includes(key)) {
|
|
||||||
// @ts-expect-error Shouldn't cause issues in this case
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
||||||
delete user[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args.format === "json") {
|
if (args.format === "json") {
|
||||||
console.log(JSON.stringify(users, null, 4));
|
console.log(JSON.stringify(users, null, 4));
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -327,27 +341,20 @@ const cliBuilder = new CliBuilder([
|
||||||
`${chalk.green(`✓`)} Found ${chalk.blue(users.length)} users (limit ${args.limit ?? 200})`
|
`${chalk.green(`✓`)} Found ${chalk.blue(users.length)} users (limit ${args.limit ?? 200})`
|
||||||
);
|
);
|
||||||
|
|
||||||
const tableHead = {
|
const tableHead = filterObjects(
|
||||||
username: chalk.white(chalk.bold("Username")),
|
[
|
||||||
email: chalk.white(chalk.bold("Email")),
|
{
|
||||||
displayName: chalk.white(chalk.bold("Display Name")),
|
username: chalk.white(chalk.bold("Username")),
|
||||||
isAdmin: chalk.white(chalk.bold("Admin?")),
|
email: chalk.white(chalk.bold("Email")),
|
||||||
instance: chalk.white(chalk.bold("Instance URL")),
|
displayName: chalk.white(chalk.bold("Display Name")),
|
||||||
createdAt: chalk.white(chalk.bold("Created At")),
|
isAdmin: chalk.white(chalk.bold("Admin?")),
|
||||||
id: chalk.white(chalk.bold("Internal UUID")),
|
instance: chalk.white(chalk.bold("Instance URL")),
|
||||||
};
|
createdAt: chalk.white(chalk.bold("Created At")),
|
||||||
|
id: chalk.white(chalk.bold("Internal UUID")),
|
||||||
// Only keep the fields specified if --fields is provided
|
},
|
||||||
if (args.fields) {
|
],
|
||||||
const keys = Object.keys(tableHead);
|
fields
|
||||||
for (const key of keys) {
|
)[0];
|
||||||
if (!args.fields.includes(key)) {
|
|
||||||
// @ts-expect-error This is fine
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
||||||
delete tableHead[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const table = new Table({
|
const table = new Table({
|
||||||
head: Object.values(tableHead),
|
head: Object.values(tableHead),
|
||||||
|
|
@ -364,7 +371,7 @@ const cliBuilder = new CliBuilder([
|
||||||
chalk.blue(
|
chalk.blue(
|
||||||
user.instance ? user.instance.base_url : "Local"
|
user.instance ? user.instance.base_url : "Local"
|
||||||
),
|
),
|
||||||
createdAt: () => chalk.blue(user.createdAt.toISOString()),
|
createdAt: () => chalk.blue(user.createdAt?.toISOString()),
|
||||||
id: () => chalk.blue(user.id),
|
id: () => chalk.blue(user.id),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1744,8 +1751,6 @@ const cliBuilder = new CliBuilder([
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-confusing-void-expression
|
|
||||||
const exitCode = await cliBuilder.processArgs(args);
|
const exitCode = await cliBuilder.processArgs(args);
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
process.exit(Number(exitCode == undefined ? 0 : exitCode));
|
||||||
process.exit(Number(exitCode ?? 0));
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import type { APIAccount } from "~types/entities/account";
|
import type { APIAccount } from "~types/entities/account";
|
||||||
import type { User as LysandUser } from "~types/lysand/Object";
|
import type { LysandUser as LysandUser } from "~types/lysand/Object";
|
||||||
import { htmlToText } from "html-to-text";
|
import { htmlToText } from "html-to-text";
|
||||||
import type { User } from "@prisma/client";
|
import type { User } from "@prisma/client";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,7 @@
|
||||||
"next-route-matcher": "^1.0.1",
|
"next-route-matcher": "^1.0.1",
|
||||||
"oauth4webapi": "^2.4.0",
|
"oauth4webapi": "^2.4.0",
|
||||||
"prisma": "^5.6.0",
|
"prisma": "^5.6.0",
|
||||||
|
"prisma-json-types-generator": "^3.0.4",
|
||||||
"prisma-redis-middleware": "^4.8.0",
|
"prisma-redis-middleware": "^4.8.0",
|
||||||
"request-parser": "file:packages/request-parser",
|
"request-parser": "file:packages/request-parser",
|
||||||
"semver": "^7.5.4",
|
"semver": "^7.5.4",
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,12 @@ export interface ConfigType {
|
||||||
bind_port: string;
|
bind_port: string;
|
||||||
banned_ips: string[];
|
banned_ips: string[];
|
||||||
banned_user_agents: string[];
|
banned_user_agents: string[];
|
||||||
|
bait: {
|
||||||
|
enabled: boolean;
|
||||||
|
send_file?: string;
|
||||||
|
bait_ips: string[];
|
||||||
|
bait_user_agents: string[];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
instance: {
|
instance: {
|
||||||
|
|
@ -181,6 +187,12 @@ export const configDefaults: ConfigType = {
|
||||||
base_url: "http://lysand.localhost:8000",
|
base_url: "http://lysand.localhost:8000",
|
||||||
banned_ips: [],
|
banned_ips: [],
|
||||||
banned_user_agents: [],
|
banned_user_agents: [],
|
||||||
|
bait: {
|
||||||
|
enabled: false,
|
||||||
|
send_file: "",
|
||||||
|
bait_ips: [],
|
||||||
|
bait_user_agents: [],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
database: {
|
database: {
|
||||||
host: "localhost",
|
host: "localhost",
|
||||||
|
|
|
||||||
1363
pages/beemovie.txt
Normal file
1363
pages/beemovie.txt
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -4,6 +4,10 @@ generator client {
|
||||||
binaryTargets = ["native", "debian-openssl-3.0.x"]
|
binaryTargets = ["native", "debian-openssl-3.0.x"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generator json {
|
||||||
|
provider = "prisma-json-types-generator"
|
||||||
|
}
|
||||||
|
|
||||||
datasource db {
|
datasource db {
|
||||||
provider = "postgresql"
|
provider = "postgresql"
|
||||||
url = env("DATABASE_URL")
|
url = env("DATABASE_URL")
|
||||||
|
|
@ -42,6 +46,7 @@ model Instance {
|
||||||
base_url String
|
base_url String
|
||||||
name String
|
name String
|
||||||
version String
|
version String
|
||||||
|
/// [InstanceLogo]
|
||||||
logo Json
|
logo Json
|
||||||
emojis Emoji[] // One to many relation with Emoji
|
emojis Emoji[] // One to many relation with Emoji
|
||||||
statuses Status[] // One to many relation with Status
|
statuses Status[] // One to many relation with Status
|
||||||
|
|
@ -66,7 +71,9 @@ model LysandObject {
|
||||||
created_at DateTime @default(now())
|
created_at DateTime @default(now())
|
||||||
author LysandObject? @relation("LysandObjectToAuthor", fields: [authorId], references: [id], onDelete: Cascade)
|
author LysandObject? @relation("LysandObjectToAuthor", fields: [authorId], references: [id], onDelete: Cascade)
|
||||||
authorId String? @db.Uuid
|
authorId String? @db.Uuid
|
||||||
|
/// [ObjectData]
|
||||||
extra_data Json
|
extra_data Json
|
||||||
|
/// [ObjectExtensions]
|
||||||
extensions Json
|
extensions Json
|
||||||
children LysandObject[] @relation("LysandObjectToAuthor")
|
children LysandObject[] @relation("LysandObjectToAuthor")
|
||||||
}
|
}
|
||||||
|
|
@ -227,7 +234,9 @@ model User {
|
||||||
email String? @unique // Nullable
|
email String? @unique // Nullable
|
||||||
note String @default("")
|
note String @default("")
|
||||||
isAdmin Boolean @default(false)
|
isAdmin Boolean @default(false)
|
||||||
|
/// [UserEndpoints]
|
||||||
endpoints Json? // Nullable
|
endpoints Json? // Nullable
|
||||||
|
/// [UserSource]
|
||||||
source Json
|
source Json
|
||||||
avatar String
|
avatar String
|
||||||
header String
|
header String
|
||||||
|
|
|
||||||
249
routes.ts
249
routes.ts
|
|
@ -5,158 +5,89 @@ import type { APIRouteMeta } from "./types/api";
|
||||||
// This is to allow for compilation of the routes, so that we can minify them and
|
// This is to allow for compilation of the routes, so that we can minify them and
|
||||||
// node_modules in production
|
// node_modules in production
|
||||||
export const rawRoutes = {
|
export const rawRoutes = {
|
||||||
"/api/v1/accounts": await import("./server/api/api/v1/accounts"),
|
"/api/v1/accounts": "./server/api/api/v1/accounts",
|
||||||
"/api/v1/accounts/familiar_followers": await import(
|
"/api/v1/accounts/familiar_followers":
|
||||||
"./server/api/api/v1/accounts/familiar_followers/index"
|
"+api/v1/accounts/familiar_followers/index",
|
||||||
),
|
"/api/v1/accounts/relationships":
|
||||||
"/api/v1/accounts/relationships": await import(
|
"./server/api/api/v1/accounts/relationships/index",
|
||||||
"./server/api/api/v1/accounts/relationships/index"
|
"/api/v1/accounts/search": "./server/api/api/v1/accounts/search/index",
|
||||||
),
|
"/api/v1/accounts/update_credentials":
|
||||||
"/api/v1/accounts/search": await import(
|
"./server/api/api/v1/accounts/update_credentials/index",
|
||||||
"./server/api/api/v1/accounts/search/index"
|
"/api/v1/accounts/verify_credentials":
|
||||||
),
|
"./server/api/api/v1/accounts/verify_credentials/index",
|
||||||
"/api/v1/accounts/update_credentials": await import(
|
"/api/v1/apps": "./server/api/api/v1/apps/index",
|
||||||
"./server/api/api/v1/accounts/update_credentials/index"
|
"/api/v1/apps/verify_credentials":
|
||||||
),
|
"./server/api/api/v1/apps/verify_credentials/index",
|
||||||
"/api/v1/accounts/verify_credentials": await import(
|
"/api/v1/blocks": "./server/api/api/v1/blocks/index",
|
||||||
"./server/api/api/v1/accounts/verify_credentials/index"
|
"/api/v1/custom_emojis": "./server/api/api/v1/custom_emojis/index",
|
||||||
),
|
"/api/v1/favourites": "./server/api/api/v1/favourites/index",
|
||||||
"/api/v1/apps": await import("./server/api/api/v1/apps/index"),
|
"/api/v1/follow_requests": "./server/api/api/v1/follow_requests/index",
|
||||||
"/api/v1/apps/verify_credentials": await import(
|
"/api/v1/instance": "./server/api/api/v1/instance/index",
|
||||||
"./server/api/api/v1/apps/verify_credentials/index"
|
"/api/v1/media": "./server/api/api/v1/media/index",
|
||||||
),
|
"/api/v1/mutes": "./server/api/api/v1/mutes/index",
|
||||||
"/api/v1/blocks": await import("./server/api/api/v1/blocks/index"),
|
"/api/v1/notifications": "./server/api/api/v1/notifications/index",
|
||||||
"/api/v1/custom_emojis": await import(
|
"/api/v1/profile/avatar": "./server/api/api/v1/profile/avatar",
|
||||||
"./server/api/api/v1/custom_emojis/index"
|
"/api/v1/profile/header": "./server/api/api/v1/profile/header",
|
||||||
),
|
"/api/v1/statuses": "./server/api/api/v1/statuses/index",
|
||||||
"/api/v1/favourites": await import("./server/api/api/v1/favourites/index"),
|
"/api/v1/timelines/home": "./server/api/api/v1/timelines/home",
|
||||||
"/api/v1/follow_requests": await import(
|
"/api/v1/timelines/public": "./server/api/api/v1/timelines/public",
|
||||||
"./server/api/api/v1/follow_requests/index"
|
"/api/v2/media": "./server/api/api/v2/media/index",
|
||||||
),
|
"/api/v2/search": "./server/api/api/v2/search/index",
|
||||||
"/api/v1/instance": await import("./server/api/api/v1/instance/index"),
|
"/auth/login": "./server/api/auth/login/index",
|
||||||
"/api/v1/media": await import("./server/api/api/v1/media/index"),
|
"/nodeinfo/2.0": "./server/api/nodeinfo/2.0/index",
|
||||||
"/api/v1/mutes": await import("./server/api/api/v1/mutes/index"),
|
"/oauth/authorize-external": "./server/api/oauth/authorize-external/index",
|
||||||
"/api/v1/notifications": await import(
|
"/oauth/providers": "./server/api/oauth/providers/index",
|
||||||
"./server/api/api/v1/notifications/index"
|
"/oauth/token": "./server/api/oauth/token/index",
|
||||||
),
|
"/api/v1/accounts/[id]": "./server/api/api/v1/accounts/[id]/index",
|
||||||
"/api/v1/profile/avatar": await import(
|
"/api/v1/accounts/[id]/block": "./server/api/api/v1/accounts/[id]/block",
|
||||||
"./server/api/api/v1/profile/avatar"
|
"/api/v1/accounts/[id]/follow": "./server/api/api/v1/accounts/[id]/follow",
|
||||||
),
|
"/api/v1/accounts/[id]/followers":
|
||||||
"/api/v1/profile/header": await import(
|
"./server/api/api/v1/accounts/[id]/followers",
|
||||||
"./server/api/api/v1/profile/header"
|
"/api/v1/accounts/[id]/following":
|
||||||
),
|
"./server/api/api/v1/accounts/[id]/following",
|
||||||
"/api/v1/statuses": await import("./server/api/api/v1/statuses/index"),
|
"/api/v1/accounts/[id]/mute": "./server/api/api/v1/accounts/[id]/mute",
|
||||||
"/api/v1/timelines/home": await import(
|
"/api/v1/accounts/[id]/note": "./server/api/api/v1/accounts/[id]/note",
|
||||||
"./server/api/api/v1/timelines/home"
|
"/api/v1/accounts/[id]/pin": "./server/api/api/v1/accounts/[id]/pin",
|
||||||
),
|
"/api/v1/accounts/[id]/remove_from_followers":
|
||||||
"/api/v1/timelines/public": await import(
|
"./server/api/api/v1/accounts/[id]/remove_from_followers",
|
||||||
"./server/api/api/v1/timelines/public"
|
"/api/v1/accounts/[id]/statuses":
|
||||||
),
|
"./server/api/api/v1/accounts/[id]/statuses",
|
||||||
"/api/v2/media": await import("./server/api/api/v2/media/index"),
|
"/api/v1/accounts/[id]/unblock":
|
||||||
"/api/v2/search": await import("./server/api/api/v2/search/index"),
|
"./server/api/api/v1/accounts/[id]/unblock",
|
||||||
"/auth/login": await import("./server/api/auth/login/index"),
|
"/api/v1/accounts/[id]/unfollow":
|
||||||
"/nodeinfo/2.0": await import("./server/api/nodeinfo/2.0/index"),
|
"./server/api/api/v1/accounts/[id]/unfollow",
|
||||||
"/oauth/authorize-external": await import(
|
"/api/v1/accounts/[id]/unmute": "./server/api/api/v1/accounts/[id]/unmute",
|
||||||
"./server/api/oauth/authorize-external/index"
|
"/api/v1/accounts/[id]/unpin": "./server/api/api/v1/accounts/[id]/unpin",
|
||||||
),
|
"/api/v1/follow_requests/[account_id]/authorize":
|
||||||
"/oauth/providers": await import("./server/api/oauth/providers/index"),
|
"./server/api/api/v1/follow_requests/[account_id]/authorize",
|
||||||
"/oauth/token": await import("./server/api/oauth/token/index"),
|
"/api/v1/follow_requests/[account_id]/reject":
|
||||||
"/api/v1/accounts/[id]": await import(
|
"./server/api/api/v1/follow_requests/[account_id]/reject",
|
||||||
"./server/api/api/v1/accounts/[id]/index"
|
"/api/v1/media/[id]": "./server/api/api/v1/media/[id]/index",
|
||||||
),
|
"/api/v1/statuses/[id]": "./server/api/api/v1/statuses/[id]/index",
|
||||||
"/api/v1/accounts/[id]/block": await import(
|
"/api/v1/statuses/[id]/context":
|
||||||
"./server/api/api/v1/accounts/[id]/block"
|
"./server/api/api/v1/statuses/[id]/context",
|
||||||
),
|
"/api/v1/statuses/[id]/favourite":
|
||||||
"/api/v1/accounts/[id]/follow": await import(
|
"./server/api/api/v1/statuses/[id]/favourite",
|
||||||
"./server/api/api/v1/accounts/[id]/follow"
|
"/api/v1/statuses/[id]/favourited_by":
|
||||||
),
|
"./server/api/api/v1/statuses/[id]/favourited_by",
|
||||||
"/api/v1/accounts/[id]/followers": await import(
|
"/api/v1/statuses/[id]/pin": "./server/api/api/v1/statuses/[id]/pin",
|
||||||
"./server/api/api/v1/accounts/[id]/followers"
|
"/api/v1/statuses/[id]/reblog": "./server/api/api/v1/statuses/[id]/reblog",
|
||||||
),
|
"/api/v1/statuses/[id]/reblogged_by":
|
||||||
"/api/v1/accounts/[id]/following": await import(
|
"./server/api/api/v1/statuses/[id]/reblogged_by",
|
||||||
"./server/api/api/v1/accounts/[id]/following"
|
"/api/v1/statuses/[id]/source": "./server/api/api/v1/statuses/[id]/source",
|
||||||
),
|
"/api/v1/statuses/[id]/unfavourite":
|
||||||
"/api/v1/accounts/[id]/mute": await import(
|
"./server/api/api/v1/statuses/[id]/unfavourite",
|
||||||
"./server/api/api/v1/accounts/[id]/mute"
|
"/api/v1/statuses/[id]/unpin": "./server/api/api/v1/statuses/[id]/unpin",
|
||||||
),
|
"/api/v1/statuses/[id]/unreblog":
|
||||||
"/api/v1/accounts/[id]/note": await import(
|
"./server/api/api/v1/statuses/[id]/unreblog",
|
||||||
"./server/api/api/v1/accounts/[id]/note"
|
"/media/[id]": "./server/api/media/[id]/index",
|
||||||
),
|
"/oauth/callback/[issuer]": "./server/api/oauth/callback/[issuer]/index",
|
||||||
"/api/v1/accounts/[id]/pin": await import(
|
"/object/[uuid]": "./server/api/object/[uuid]/index",
|
||||||
"./server/api/api/v1/accounts/[id]/pin"
|
"/users/[uuid]": "./server/api/users/[uuid]/index",
|
||||||
),
|
"/users/[uuid]/inbox": "./server/api/users/[uuid]/inbox/index",
|
||||||
"/api/v1/accounts/[id]/remove_from_followers": await import(
|
"/users/[uuid]/outbox": "./server/api/users/[uuid]/outbox/index",
|
||||||
"./server/api/api/v1/accounts/[id]/remove_from_followers"
|
"/[...404]": "./server/api/[...404]",
|
||||||
),
|
} as Record<string, string>;
|
||||||
"/api/v1/accounts/[id]/statuses": await import(
|
|
||||||
"./server/api/api/v1/accounts/[id]/statuses"
|
|
||||||
),
|
|
||||||
"/api/v1/accounts/[id]/unblock": await import(
|
|
||||||
"./server/api/api/v1/accounts/[id]/unblock"
|
|
||||||
),
|
|
||||||
"/api/v1/accounts/[id]/unfollow": await import(
|
|
||||||
"./server/api/api/v1/accounts/[id]/unfollow"
|
|
||||||
),
|
|
||||||
"/api/v1/accounts/[id]/unmute": await import(
|
|
||||||
"./server/api/api/v1/accounts/[id]/unmute"
|
|
||||||
),
|
|
||||||
"/api/v1/accounts/[id]/unpin": await import(
|
|
||||||
"./server/api/api/v1/accounts/[id]/unpin"
|
|
||||||
),
|
|
||||||
"/api/v1/follow_requests/[account_id]/authorize": await import(
|
|
||||||
"./server/api/api/v1/follow_requests/[account_id]/authorize"
|
|
||||||
),
|
|
||||||
"/api/v1/follow_requests/[account_id]/reject": await import(
|
|
||||||
"./server/api/api/v1/follow_requests/[account_id]/reject"
|
|
||||||
),
|
|
||||||
"/api/v1/media/[id]": await import("./server/api/api/v1/media/[id]/index"),
|
|
||||||
"/api/v1/statuses/[id]": await import(
|
|
||||||
"./server/api/api/v1/statuses/[id]/index"
|
|
||||||
),
|
|
||||||
"/api/v1/statuses/[id]/context": await import(
|
|
||||||
"./server/api/api/v1/statuses/[id]/context"
|
|
||||||
),
|
|
||||||
"/api/v1/statuses/[id]/favourite": await import(
|
|
||||||
"./server/api/api/v1/statuses/[id]/favourite"
|
|
||||||
),
|
|
||||||
"/api/v1/statuses/[id]/favourited_by": await import(
|
|
||||||
"./server/api/api/v1/statuses/[id]/favourited_by"
|
|
||||||
),
|
|
||||||
"/api/v1/statuses/[id]/pin": await import(
|
|
||||||
"./server/api/api/v1/statuses/[id]/pin"
|
|
||||||
),
|
|
||||||
"/api/v1/statuses/[id]/reblog": await import(
|
|
||||||
"./server/api/api/v1/statuses/[id]/reblog"
|
|
||||||
),
|
|
||||||
"/api/v1/statuses/[id]/reblogged_by": await import(
|
|
||||||
"./server/api/api/v1/statuses/[id]/reblogged_by"
|
|
||||||
),
|
|
||||||
"/api/v1/statuses/[id]/source": await import(
|
|
||||||
"./server/api/api/v1/statuses/[id]/source"
|
|
||||||
),
|
|
||||||
"/api/v1/statuses/[id]/unfavourite": await import(
|
|
||||||
"./server/api/api/v1/statuses/[id]/unfavourite"
|
|
||||||
),
|
|
||||||
"/api/v1/statuses/[id]/unpin": await import(
|
|
||||||
"./server/api/api/v1/statuses/[id]/unpin"
|
|
||||||
),
|
|
||||||
"/api/v1/statuses/[id]/unreblog": await import(
|
|
||||||
"./server/api/api/v1/statuses/[id]/unreblog"
|
|
||||||
),
|
|
||||||
"/media/[id]": await import("./server/api/media/[id]/index"),
|
|
||||||
"/oauth/callback/[issuer]": await import(
|
|
||||||
"./server/api/oauth/callback/[issuer]/index"
|
|
||||||
),
|
|
||||||
"/object/[uuid]": await import("./server/api/object/[uuid]/index"),
|
|
||||||
"/users/[uuid]": await import("./server/api/users/[uuid]/index"),
|
|
||||||
"/users/[uuid]/inbox": await import(
|
|
||||||
"./server/api/users/[uuid]/inbox/index"
|
|
||||||
),
|
|
||||||
"/users/[uuid]/outbox": await import(
|
|
||||||
"./server/api/users/[uuid]/outbox/index"
|
|
||||||
),
|
|
||||||
"/[...404]": await import("./server/api/[...404]"),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Returns the route filesystem path when given a URL
|
// Returns the route filesystem path when given a URL
|
||||||
export const routeMatcher = new Bun.FileSystemRouter({
|
export const routeMatcher = new Bun.FileSystemRouter({
|
||||||
|
|
@ -164,19 +95,15 @@ export const routeMatcher = new Bun.FileSystemRouter({
|
||||||
dir: process.cwd() + "/server/api",
|
dir: process.cwd() + "/server/api",
|
||||||
});
|
});
|
||||||
|
|
||||||
export const matchRoute = <T = Record<string, never>>(url: string) => {
|
export const matchRoute = async <T = Record<string, never>>(url: string) => {
|
||||||
const route = routeMatcher.match(url);
|
const route = routeMatcher.match(url);
|
||||||
if (!route) return { file: null, matchedRoute: null };
|
if (!route) return { file: null, matchedRoute: null };
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// @ts-expect-error TypeScript parses this as a defined object instead of an arbitrarily editable route file
|
file: (await import(rawRoutes[route.name])) as {
|
||||||
file: rawRoutes[route.name] as Promise<
|
default: RouteHandler<T>;
|
||||||
| {
|
meta: APIRouteMeta;
|
||||||
meta: APIRouteMeta;
|
},
|
||||||
default: RouteHandler<T>;
|
|
||||||
}
|
|
||||||
| undefined
|
|
||||||
>,
|
|
||||||
matchedRoute: route,
|
matchedRoute: route,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
60
server.ts
60
server.ts
|
|
@ -5,6 +5,7 @@ import type { ConfigManager, ConfigType } from "config-manager";
|
||||||
import type { LogManager, MultiLogManager } from "log-manager";
|
import type { LogManager, MultiLogManager } from "log-manager";
|
||||||
import { LogLevel } from "log-manager";
|
import { LogLevel } from "log-manager";
|
||||||
import { RequestParser } from "request-parser";
|
import { RequestParser } from "request-parser";
|
||||||
|
import { matchRoute } from "~routes";
|
||||||
|
|
||||||
export const createServer = (
|
export const createServer = (
|
||||||
config: ConfigType,
|
config: ConfigType,
|
||||||
|
|
@ -45,6 +46,55 @@ export const createServer = (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.http.bait.enabled) {
|
||||||
|
// Check for bait IPs
|
||||||
|
for (const ip of config.http.bait.bait_ips) {
|
||||||
|
try {
|
||||||
|
if (matches(ip, request_ip)) {
|
||||||
|
const file = Bun.file(
|
||||||
|
config.http.bait.send_file ||
|
||||||
|
"./pages/beemovie.txt"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (await file.exists()) {
|
||||||
|
return new Response(file);
|
||||||
|
} else {
|
||||||
|
await logger.log(
|
||||||
|
LogLevel.ERROR,
|
||||||
|
"Server.Bait",
|
||||||
|
`Bait file not found: ${config.http.bait.send_file}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(
|
||||||
|
`[-] Error while parsing bait IP "${ip}" `
|
||||||
|
);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for bait user agents (regex)
|
||||||
|
for (const agent of config.http.bait.bait_user_agents) {
|
||||||
|
console.log(agent);
|
||||||
|
if (new RegExp(agent).test(ua)) {
|
||||||
|
const file = Bun.file(
|
||||||
|
config.http.bait.send_file || "./pages/beemovie.txt"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (await file.exists()) {
|
||||||
|
return new Response(file);
|
||||||
|
} else {
|
||||||
|
await logger.log(
|
||||||
|
LogLevel.ERROR,
|
||||||
|
"Server.Bait",
|
||||||
|
`Bait file not found: ${config.http.bait.send_file}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (config.logging.log_requests) {
|
if (config.logging.log_requests) {
|
||||||
await logger.logRequest(
|
await logger.logRequest(
|
||||||
req,
|
req,
|
||||||
|
|
@ -57,13 +107,11 @@ export const createServer = (
|
||||||
return jsonResponse({});
|
return jsonResponse({});
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it isn't dynamically imported, it causes trouble with imports
|
const { file: filePromise, matchedRoute } = await matchRoute(
|
||||||
// There shouldn't be a performance hit after bundling right?
|
req.url
|
||||||
const { matchRoute } = await import("~routes");
|
);
|
||||||
|
|
||||||
const { file: filePromise, matchedRoute } = matchRoute(req.url);
|
const file = filePromise;
|
||||||
|
|
||||||
const file = await filePromise;
|
|
||||||
|
|
||||||
if (matchedRoute && file == undefined) {
|
if (matchedRoute && file == undefined) {
|
||||||
await logger.log(
|
await logger.log(
|
||||||
|
|
|
||||||
|
|
@ -64,14 +64,14 @@ export default apiRoute<{
|
||||||
ALLOWED_ATTR: [],
|
ALLOWED_ATTR: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user.source) {
|
/* if (!user.source) {
|
||||||
user.source = {
|
user.source = {
|
||||||
privacy: "public",
|
privacy: "public",
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
language: "en",
|
language: "en",
|
||||||
note: "",
|
note: "",
|
||||||
};
|
};
|
||||||
}
|
} */
|
||||||
|
|
||||||
let mediaManager: MediaBackend;
|
let mediaManager: MediaBackend;
|
||||||
|
|
||||||
|
|
@ -132,7 +132,7 @@ export default apiRoute<{
|
||||||
return errorResponse("Bio contains blocked words", 422);
|
return errorResponse("Bio contains blocked words", 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
(user.source as unknown as APISource).note = sanitizedNote;
|
(user.source as APISource).note = sanitizedNote;
|
||||||
// TODO: Convert note to HTML
|
// TODO: Convert note to HTML
|
||||||
user.note = await convertTextToHtml(sanitizedNote);
|
user.note = await convertTextToHtml(sanitizedNote);
|
||||||
}
|
}
|
||||||
|
|
@ -150,8 +150,7 @@ export default apiRoute<{
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-expect-error Prisma Typescript doesn't include relations
|
(user.source as APISource).privacy = source_privacy;
|
||||||
user.source.privacy = source_privacy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source_sensitive && user.source) {
|
if (source_sensitive && user.source) {
|
||||||
|
|
@ -160,8 +159,7 @@ export default apiRoute<{
|
||||||
return errorResponse("Sensitive must be a boolean", 422);
|
return errorResponse("Sensitive must be a boolean", 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-expect-error Prisma Typescript doesn't include relations
|
(user.source as APISource).sensitive = source_sensitive === "true";
|
||||||
user.source.sensitive = source_sensitive === "true";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source_language && user.source) {
|
if (source_language && user.source) {
|
||||||
|
|
@ -172,8 +170,7 @@ export default apiRoute<{
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-expect-error Prisma Typescript doesn't include relations
|
(user.source as APISource).language = source_language;
|
||||||
user.source.language = source_language;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (avatar) {
|
if (avatar) {
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"verbatimModuleSyntax": true,
|
"verbatimModuleSyntax": true,
|
||||||
"types": [
|
"types": [
|
||||||
"bun-types" // add Bun global
|
"bun-types", // add Bun global
|
||||||
],
|
],
|
||||||
"paths": {
|
"paths": {
|
||||||
"@*": ["./utils/*"],
|
"@*": ["./utils/*"],
|
||||||
|
|
@ -32,9 +32,11 @@
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"*.ts",
|
"*.ts",
|
||||||
|
"*.d.ts",
|
||||||
"*.vue",
|
"*.vue",
|
||||||
"**/*.ts",
|
"**/*.ts",
|
||||||
|
"**/*.d.ts",
|
||||||
"**/*.vue",
|
"**/*.vue",
|
||||||
"server/api/.well-known/**/*.ts"
|
"server/api/.well-known/**/*.ts",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
28
types.d.ts
vendored
Normal file
28
types.d.ts
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import type { LysandObject } from "@prisma/client";
|
||||||
|
import type { APIAccount } from "~types/entities/account";
|
||||||
|
import type { APIField } from "~types/entities/field";
|
||||||
|
import type { ContentFormat } from "~types/lysand/Object";
|
||||||
|
|
||||||
|
declare namespace global {
|
||||||
|
namespace PrismaJson {
|
||||||
|
type InstanceLogo = ContentFormat[];
|
||||||
|
type ObjectData = LysandObject;
|
||||||
|
type ObjectExtensions = LysandObject["extensions"];
|
||||||
|
interface UserEndpoints {
|
||||||
|
inbox: string;
|
||||||
|
liked: string;
|
||||||
|
outbox: string;
|
||||||
|
disliked: string;
|
||||||
|
featured: string;
|
||||||
|
followers: string;
|
||||||
|
following: string;
|
||||||
|
}
|
||||||
|
interface UserSource {
|
||||||
|
note: string;
|
||||||
|
fields: APIField[];
|
||||||
|
privacy: APIAccount["privacy"];
|
||||||
|
language: string;
|
||||||
|
sensitive: boolean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -41,7 +41,7 @@ export interface Collection<T> {
|
||||||
items: T[];
|
items: T[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface User extends LysandObjectType {
|
export interface LysandUser extends LysandObjectType {
|
||||||
type: "User";
|
type: "User";
|
||||||
bio: ContentFormat[];
|
bio: ContentFormat[];
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue