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
|
||||
import chalk from "chalk";
|
||||
import { rm, cp, mkdir, exists } from "fs/promises";
|
||||
|
||||
console.log(
|
||||
chalk.red(
|
||||
"Warning: Build is currently broken due to a bug in Bun causing it not to parse dynamic imports"
|
||||
)
|
||||
);
|
||||
import { rawRoutes } from "~routes";
|
||||
|
||||
if (!(await exists("./pages/dist"))) {
|
||||
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() + "/prisma.ts",
|
||||
process.cwd() + "/cli.ts",
|
||||
// Force Bun to include endpoints
|
||||
...Object.values(rawRoutes),
|
||||
],
|
||||
outdir: process.cwd() + "/dist",
|
||||
target: "bun",
|
||||
|
|
@ -47,4 +43,7 @@ await cp(process.cwd() + "/pages/dist", process.cwd() + "/dist/pages/", {
|
|||
recursive: true,
|
||||
});
|
||||
|
||||
// Copy the Bee Movie script from pages
|
||||
await cp(process.cwd() + "/pages/beemovie.txt", process.cwd() + "/dist/pages/");
|
||||
|
||||
console.log(`Built!`);
|
||||
|
|
|
|||
77
cli.ts
77
cli.ts
|
|
@ -19,6 +19,28 @@ const args = process.argv;
|
|||
|
||||
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([
|
||||
new CliCommand<{
|
||||
username: string;
|
||||
|
|
@ -270,7 +292,7 @@ const cliBuilder = new CliBuilder([
|
|||
},
|
||||
],
|
||||
async (instance: CliCommand, args) => {
|
||||
const { admins, help } = args;
|
||||
const { admins, help, fields = [] } = args;
|
||||
|
||||
if (help) {
|
||||
instance.displayHelp();
|
||||
|
|
@ -281,16 +303,21 @@ const cliBuilder = new CliBuilder([
|
|||
console.log(`${chalk.red(`✗`)} Invalid format`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const users = await client.user.findMany({
|
||||
const users = filterObjects(
|
||||
await client.user.findMany({
|
||||
where: {
|
||||
isAdmin: admins || undefined,
|
||||
},
|
||||
take: args.limit ?? 200,
|
||||
include: {
|
||||
instance: true,
|
||||
instance:
|
||||
fields.length == 0
|
||||
? true
|
||||
: fields.includes("instance"),
|
||||
},
|
||||
});
|
||||
}),
|
||||
fields
|
||||
);
|
||||
|
||||
if (args.redact) {
|
||||
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") {
|
||||
console.log(JSON.stringify(users, null, 4));
|
||||
return 0;
|
||||
|
|
@ -327,7 +341,9 @@ const cliBuilder = new CliBuilder([
|
|||
`${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")),
|
||||
|
|
@ -335,19 +351,10 @@ const cliBuilder = new CliBuilder([
|
|||
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);
|
||||
for (const key of keys) {
|
||||
if (!args.fields.includes(key)) {
|
||||
// @ts-expect-error This is fine
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete tableHead[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
],
|
||||
fields
|
||||
)[0];
|
||||
|
||||
const table = new Table({
|
||||
head: Object.values(tableHead),
|
||||
|
|
@ -364,7 +371,7 @@ const cliBuilder = new CliBuilder([
|
|||
chalk.blue(
|
||||
user.instance ? user.instance.base_url : "Local"
|
||||
),
|
||||
createdAt: () => chalk.blue(user.createdAt.toISOString()),
|
||||
createdAt: () => chalk.blue(user.createdAt?.toISOString()),
|
||||
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);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
process.exit(Number(exitCode ?? 0));
|
||||
process.exit(Number(exitCode == undefined ? 0 : exitCode));
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
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 type { User } from "@prisma/client";
|
||||
import { Prisma } from "@prisma/client";
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@
|
|||
"next-route-matcher": "^1.0.1",
|
||||
"oauth4webapi": "^2.4.0",
|
||||
"prisma": "^5.6.0",
|
||||
"prisma-json-types-generator": "^3.0.4",
|
||||
"prisma-redis-middleware": "^4.8.0",
|
||||
"request-parser": "file:packages/request-parser",
|
||||
"semver": "^7.5.4",
|
||||
|
|
|
|||
|
|
@ -55,6 +55,12 @@ export interface ConfigType {
|
|||
bind_port: string;
|
||||
banned_ips: string[];
|
||||
banned_user_agents: string[];
|
||||
bait: {
|
||||
enabled: boolean;
|
||||
send_file?: string;
|
||||
bait_ips: string[];
|
||||
bait_user_agents: string[];
|
||||
};
|
||||
};
|
||||
|
||||
instance: {
|
||||
|
|
@ -181,6 +187,12 @@ export const configDefaults: ConfigType = {
|
|||
base_url: "http://lysand.localhost:8000",
|
||||
banned_ips: [],
|
||||
banned_user_agents: [],
|
||||
bait: {
|
||||
enabled: false,
|
||||
send_file: "",
|
||||
bait_ips: [],
|
||||
bait_user_agents: [],
|
||||
},
|
||||
},
|
||||
database: {
|
||||
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"]
|
||||
}
|
||||
|
||||
generator json {
|
||||
provider = "prisma-json-types-generator"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
|
|
@ -42,6 +46,7 @@ model Instance {
|
|||
base_url String
|
||||
name String
|
||||
version String
|
||||
/// [InstanceLogo]
|
||||
logo Json
|
||||
emojis Emoji[] // One to many relation with Emoji
|
||||
statuses Status[] // One to many relation with Status
|
||||
|
|
@ -66,7 +71,9 @@ model LysandObject {
|
|||
created_at DateTime @default(now())
|
||||
author LysandObject? @relation("LysandObjectToAuthor", fields: [authorId], references: [id], onDelete: Cascade)
|
||||
authorId String? @db.Uuid
|
||||
/// [ObjectData]
|
||||
extra_data Json
|
||||
/// [ObjectExtensions]
|
||||
extensions Json
|
||||
children LysandObject[] @relation("LysandObjectToAuthor")
|
||||
}
|
||||
|
|
@ -227,7 +234,9 @@ model User {
|
|||
email String? @unique // Nullable
|
||||
note String @default("")
|
||||
isAdmin Boolean @default(false)
|
||||
/// [UserEndpoints]
|
||||
endpoints Json? // Nullable
|
||||
/// [UserSource]
|
||||
source Json
|
||||
avatar String
|
||||
header String
|
||||
|
|
|
|||
247
routes.ts
247
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
|
||||
// node_modules in production
|
||||
export const rawRoutes = {
|
||||
"/api/v1/accounts": await import("./server/api/api/v1/accounts"),
|
||||
"/api/v1/accounts/familiar_followers": await import(
|
||||
"./server/api/api/v1/accounts/familiar_followers/index"
|
||||
),
|
||||
"/api/v1/accounts/relationships": await import(
|
||||
"./server/api/api/v1/accounts/relationships/index"
|
||||
),
|
||||
"/api/v1/accounts/search": await import(
|
||||
"./server/api/api/v1/accounts/search/index"
|
||||
),
|
||||
"/api/v1/accounts/update_credentials": await import(
|
||||
"./server/api/api/v1/accounts/update_credentials/index"
|
||||
),
|
||||
"/api/v1/accounts/verify_credentials": await import(
|
||||
"./server/api/api/v1/accounts/verify_credentials/index"
|
||||
),
|
||||
"/api/v1/apps": await import("./server/api/api/v1/apps/index"),
|
||||
"/api/v1/apps/verify_credentials": await import(
|
||||
"./server/api/api/v1/apps/verify_credentials/index"
|
||||
),
|
||||
"/api/v1/blocks": await import("./server/api/api/v1/blocks/index"),
|
||||
"/api/v1/custom_emojis": await import(
|
||||
"./server/api/api/v1/custom_emojis/index"
|
||||
),
|
||||
"/api/v1/favourites": await import("./server/api/api/v1/favourites/index"),
|
||||
"/api/v1/follow_requests": await import(
|
||||
"./server/api/api/v1/follow_requests/index"
|
||||
),
|
||||
"/api/v1/instance": await import("./server/api/api/v1/instance/index"),
|
||||
"/api/v1/media": await import("./server/api/api/v1/media/index"),
|
||||
"/api/v1/mutes": await import("./server/api/api/v1/mutes/index"),
|
||||
"/api/v1/notifications": await import(
|
||||
"./server/api/api/v1/notifications/index"
|
||||
),
|
||||
"/api/v1/profile/avatar": await import(
|
||||
"./server/api/api/v1/profile/avatar"
|
||||
),
|
||||
"/api/v1/profile/header": await import(
|
||||
"./server/api/api/v1/profile/header"
|
||||
),
|
||||
"/api/v1/statuses": await import("./server/api/api/v1/statuses/index"),
|
||||
"/api/v1/timelines/home": await import(
|
||||
"./server/api/api/v1/timelines/home"
|
||||
),
|
||||
"/api/v1/timelines/public": await import(
|
||||
"./server/api/api/v1/timelines/public"
|
||||
),
|
||||
"/api/v2/media": await import("./server/api/api/v2/media/index"),
|
||||
"/api/v2/search": await import("./server/api/api/v2/search/index"),
|
||||
"/auth/login": await import("./server/api/auth/login/index"),
|
||||
"/nodeinfo/2.0": await import("./server/api/nodeinfo/2.0/index"),
|
||||
"/oauth/authorize-external": await import(
|
||||
"./server/api/oauth/authorize-external/index"
|
||||
),
|
||||
"/oauth/providers": await import("./server/api/oauth/providers/index"),
|
||||
"/oauth/token": await import("./server/api/oauth/token/index"),
|
||||
"/api/v1/accounts/[id]": await import(
|
||||
"./server/api/api/v1/accounts/[id]/index"
|
||||
),
|
||||
"/api/v1/accounts/[id]/block": await import(
|
||||
"./server/api/api/v1/accounts/[id]/block"
|
||||
),
|
||||
"/api/v1/accounts/[id]/follow": await import(
|
||||
"./server/api/api/v1/accounts/[id]/follow"
|
||||
),
|
||||
"/api/v1/accounts/[id]/followers": await import(
|
||||
"./server/api/api/v1/accounts/[id]/followers"
|
||||
),
|
||||
"/api/v1/accounts/[id]/following": await import(
|
||||
"./server/api/api/v1/accounts/[id]/following"
|
||||
),
|
||||
"/api/v1/accounts/[id]/mute": await import(
|
||||
"./server/api/api/v1/accounts/[id]/mute"
|
||||
),
|
||||
"/api/v1/accounts/[id]/note": await import(
|
||||
"./server/api/api/v1/accounts/[id]/note"
|
||||
),
|
||||
"/api/v1/accounts/[id]/pin": await import(
|
||||
"./server/api/api/v1/accounts/[id]/pin"
|
||||
),
|
||||
"/api/v1/accounts/[id]/remove_from_followers": await import(
|
||||
"./server/api/api/v1/accounts/[id]/remove_from_followers"
|
||||
),
|
||||
"/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]"),
|
||||
};
|
||||
"/api/v1/accounts": "./server/api/api/v1/accounts",
|
||||
"/api/v1/accounts/familiar_followers":
|
||||
"+api/v1/accounts/familiar_followers/index",
|
||||
"/api/v1/accounts/relationships":
|
||||
"./server/api/api/v1/accounts/relationships/index",
|
||||
"/api/v1/accounts/search": "./server/api/api/v1/accounts/search/index",
|
||||
"/api/v1/accounts/update_credentials":
|
||||
"./server/api/api/v1/accounts/update_credentials/index",
|
||||
"/api/v1/accounts/verify_credentials":
|
||||
"./server/api/api/v1/accounts/verify_credentials/index",
|
||||
"/api/v1/apps": "./server/api/api/v1/apps/index",
|
||||
"/api/v1/apps/verify_credentials":
|
||||
"./server/api/api/v1/apps/verify_credentials/index",
|
||||
"/api/v1/blocks": "./server/api/api/v1/blocks/index",
|
||||
"/api/v1/custom_emojis": "./server/api/api/v1/custom_emojis/index",
|
||||
"/api/v1/favourites": "./server/api/api/v1/favourites/index",
|
||||
"/api/v1/follow_requests": "./server/api/api/v1/follow_requests/index",
|
||||
"/api/v1/instance": "./server/api/api/v1/instance/index",
|
||||
"/api/v1/media": "./server/api/api/v1/media/index",
|
||||
"/api/v1/mutes": "./server/api/api/v1/mutes/index",
|
||||
"/api/v1/notifications": "./server/api/api/v1/notifications/index",
|
||||
"/api/v1/profile/avatar": "./server/api/api/v1/profile/avatar",
|
||||
"/api/v1/profile/header": "./server/api/api/v1/profile/header",
|
||||
"/api/v1/statuses": "./server/api/api/v1/statuses/index",
|
||||
"/api/v1/timelines/home": "./server/api/api/v1/timelines/home",
|
||||
"/api/v1/timelines/public": "./server/api/api/v1/timelines/public",
|
||||
"/api/v2/media": "./server/api/api/v2/media/index",
|
||||
"/api/v2/search": "./server/api/api/v2/search/index",
|
||||
"/auth/login": "./server/api/auth/login/index",
|
||||
"/nodeinfo/2.0": "./server/api/nodeinfo/2.0/index",
|
||||
"/oauth/authorize-external": "./server/api/oauth/authorize-external/index",
|
||||
"/oauth/providers": "./server/api/oauth/providers/index",
|
||||
"/oauth/token": "./server/api/oauth/token/index",
|
||||
"/api/v1/accounts/[id]": "./server/api/api/v1/accounts/[id]/index",
|
||||
"/api/v1/accounts/[id]/block": "./server/api/api/v1/accounts/[id]/block",
|
||||
"/api/v1/accounts/[id]/follow": "./server/api/api/v1/accounts/[id]/follow",
|
||||
"/api/v1/accounts/[id]/followers":
|
||||
"./server/api/api/v1/accounts/[id]/followers",
|
||||
"/api/v1/accounts/[id]/following":
|
||||
"./server/api/api/v1/accounts/[id]/following",
|
||||
"/api/v1/accounts/[id]/mute": "./server/api/api/v1/accounts/[id]/mute",
|
||||
"/api/v1/accounts/[id]/note": "./server/api/api/v1/accounts/[id]/note",
|
||||
"/api/v1/accounts/[id]/pin": "./server/api/api/v1/accounts/[id]/pin",
|
||||
"/api/v1/accounts/[id]/remove_from_followers":
|
||||
"./server/api/api/v1/accounts/[id]/remove_from_followers",
|
||||
"/api/v1/accounts/[id]/statuses":
|
||||
"./server/api/api/v1/accounts/[id]/statuses",
|
||||
"/api/v1/accounts/[id]/unblock":
|
||||
"./server/api/api/v1/accounts/[id]/unblock",
|
||||
"/api/v1/accounts/[id]/unfollow":
|
||||
"./server/api/api/v1/accounts/[id]/unfollow",
|
||||
"/api/v1/accounts/[id]/unmute": "./server/api/api/v1/accounts/[id]/unmute",
|
||||
"/api/v1/accounts/[id]/unpin": "./server/api/api/v1/accounts/[id]/unpin",
|
||||
"/api/v1/follow_requests/[account_id]/authorize":
|
||||
"./server/api/api/v1/follow_requests/[account_id]/authorize",
|
||||
"/api/v1/follow_requests/[account_id]/reject":
|
||||
"./server/api/api/v1/follow_requests/[account_id]/reject",
|
||||
"/api/v1/media/[id]": "./server/api/api/v1/media/[id]/index",
|
||||
"/api/v1/statuses/[id]": "./server/api/api/v1/statuses/[id]/index",
|
||||
"/api/v1/statuses/[id]/context":
|
||||
"./server/api/api/v1/statuses/[id]/context",
|
||||
"/api/v1/statuses/[id]/favourite":
|
||||
"./server/api/api/v1/statuses/[id]/favourite",
|
||||
"/api/v1/statuses/[id]/favourited_by":
|
||||
"./server/api/api/v1/statuses/[id]/favourited_by",
|
||||
"/api/v1/statuses/[id]/pin": "./server/api/api/v1/statuses/[id]/pin",
|
||||
"/api/v1/statuses/[id]/reblog": "./server/api/api/v1/statuses/[id]/reblog",
|
||||
"/api/v1/statuses/[id]/reblogged_by":
|
||||
"./server/api/api/v1/statuses/[id]/reblogged_by",
|
||||
"/api/v1/statuses/[id]/source": "./server/api/api/v1/statuses/[id]/source",
|
||||
"/api/v1/statuses/[id]/unfavourite":
|
||||
"./server/api/api/v1/statuses/[id]/unfavourite",
|
||||
"/api/v1/statuses/[id]/unpin": "./server/api/api/v1/statuses/[id]/unpin",
|
||||
"/api/v1/statuses/[id]/unreblog":
|
||||
"./server/api/api/v1/statuses/[id]/unreblog",
|
||||
"/media/[id]": "./server/api/media/[id]/index",
|
||||
"/oauth/callback/[issuer]": "./server/api/oauth/callback/[issuer]/index",
|
||||
"/object/[uuid]": "./server/api/object/[uuid]/index",
|
||||
"/users/[uuid]": "./server/api/users/[uuid]/index",
|
||||
"/users/[uuid]/inbox": "./server/api/users/[uuid]/inbox/index",
|
||||
"/users/[uuid]/outbox": "./server/api/users/[uuid]/outbox/index",
|
||||
"/[...404]": "./server/api/[...404]",
|
||||
} as Record<string, string>;
|
||||
|
||||
// Returns the route filesystem path when given a URL
|
||||
export const routeMatcher = new Bun.FileSystemRouter({
|
||||
|
|
@ -164,19 +95,15 @@ export const routeMatcher = new Bun.FileSystemRouter({
|
|||
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);
|
||||
if (!route) return { file: null, matchedRoute: null };
|
||||
|
||||
return {
|
||||
// @ts-expect-error TypeScript parses this as a defined object instead of an arbitrarily editable route file
|
||||
file: rawRoutes[route.name] as Promise<
|
||||
| {
|
||||
meta: APIRouteMeta;
|
||||
file: (await import(rawRoutes[route.name])) as {
|
||||
default: RouteHandler<T>;
|
||||
}
|
||||
| undefined
|
||||
>,
|
||||
meta: APIRouteMeta;
|
||||
},
|
||||
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 { LogLevel } from "log-manager";
|
||||
import { RequestParser } from "request-parser";
|
||||
import { matchRoute } from "~routes";
|
||||
|
||||
export const createServer = (
|
||||
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) {
|
||||
await logger.logRequest(
|
||||
req,
|
||||
|
|
@ -57,13 +107,11 @@ export const createServer = (
|
|||
return jsonResponse({});
|
||||
}
|
||||
|
||||
// If it isn't dynamically imported, it causes trouble with imports
|
||||
// There shouldn't be a performance hit after bundling right?
|
||||
const { matchRoute } = await import("~routes");
|
||||
const { file: filePromise, matchedRoute } = await matchRoute(
|
||||
req.url
|
||||
);
|
||||
|
||||
const { file: filePromise, matchedRoute } = matchRoute(req.url);
|
||||
|
||||
const file = await filePromise;
|
||||
const file = filePromise;
|
||||
|
||||
if (matchedRoute && file == undefined) {
|
||||
await logger.log(
|
||||
|
|
|
|||
|
|
@ -64,14 +64,14 @@ export default apiRoute<{
|
|||
ALLOWED_ATTR: [],
|
||||
});
|
||||
|
||||
if (!user.source) {
|
||||
/* if (!user.source) {
|
||||
user.source = {
|
||||
privacy: "public",
|
||||
sensitive: false,
|
||||
language: "en",
|
||||
note: "",
|
||||
};
|
||||
}
|
||||
} */
|
||||
|
||||
let mediaManager: MediaBackend;
|
||||
|
||||
|
|
@ -132,7 +132,7 @@ export default apiRoute<{
|
|||
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
|
||||
user.note = await convertTextToHtml(sanitizedNote);
|
||||
}
|
||||
|
|
@ -150,8 +150,7 @@ export default apiRoute<{
|
|||
);
|
||||
}
|
||||
|
||||
// @ts-expect-error Prisma Typescript doesn't include relations
|
||||
user.source.privacy = source_privacy;
|
||||
(user.source as APISource).privacy = source_privacy;
|
||||
}
|
||||
|
||||
if (source_sensitive && user.source) {
|
||||
|
|
@ -160,8 +159,7 @@ export default apiRoute<{
|
|||
return errorResponse("Sensitive must be a boolean", 422);
|
||||
}
|
||||
|
||||
// @ts-expect-error Prisma Typescript doesn't include relations
|
||||
user.source.sensitive = source_sensitive === "true";
|
||||
(user.source as APISource).sensitive = source_sensitive === "true";
|
||||
}
|
||||
|
||||
if (source_language && user.source) {
|
||||
|
|
@ -172,8 +170,7 @@ export default apiRoute<{
|
|||
);
|
||||
}
|
||||
|
||||
// @ts-expect-error Prisma Typescript doesn't include relations
|
||||
user.source.language = source_language;
|
||||
(user.source as APISource).language = source_language;
|
||||
}
|
||||
|
||||
if (avatar) {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
"experimentalDecorators": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"types": [
|
||||
"bun-types" // add Bun global
|
||||
"bun-types", // add Bun global
|
||||
],
|
||||
"paths": {
|
||||
"@*": ["./utils/*"],
|
||||
|
|
@ -32,9 +32,11 @@
|
|||
},
|
||||
"include": [
|
||||
"*.ts",
|
||||
"*.d.ts",
|
||||
"*.vue",
|
||||
"**/*.ts",
|
||||
"**/*.d.ts",
|
||||
"**/*.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[];
|
||||
}
|
||||
|
||||
export interface User extends LysandObjectType {
|
||||
export interface LysandUser extends LysandObjectType {
|
||||
type: "User";
|
||||
bio: ContentFormat[];
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue