From b099e40c6005ae5e578caa38675b2e1370f183e0 Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Tue, 28 Nov 2023 14:34:09 -1000 Subject: [PATCH 01/13] Enable serving files using local uploads --- server/api/media/[id]/index.ts | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 server/api/media/[id]/index.ts diff --git a/server/api/media/[id]/index.ts b/server/api/media/[id]/index.ts new file mode 100644 index 00000000..4857a0cd --- /dev/null +++ b/server/api/media/[id]/index.ts @@ -0,0 +1,32 @@ +import { errorResponse } from "@response"; +import { applyConfig } from "@api"; +import type { MatchedRoute } from "bun"; + +export const meta = applyConfig({ + allowedMethods: ["GET"], + route: "/media/:id", + ratelimits: { + max: 100, + duration: 60, + }, + auth: { + required: false, + }, +}); + +export default async ( + req: Request, + matchedRoute: MatchedRoute +): Promise => { + // TODO: Add checks for disabled or not email verified accounts + + const id = matchedRoute.params.id; + + // Serve file from filesystem + const file = Bun.file(`./uploads/${id}`); + + if (!(await file.exists())) return errorResponse("File not found", 404); + + // @ts-expect-error Bun allows this + return new Response(file); +}; From 0aeeeabb338fab42e6ae26b27b3440c8ff8351fc Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Tue, 28 Nov 2023 14:34:33 -1000 Subject: [PATCH 02/13] Edit docker-compose file to add uploads folder --- docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yml b/docker-compose.yml index f8f28d75..e8100507 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,6 +8,7 @@ services: #- ./logs:/app/logs - ./config:/app/config - ./.env:/app/.env + - ./uploads:/app/uploads restart: unless-stopped container_name: lysand networks: From e01e92c9cee840f8ca52506f9553c29aab8613fa Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Tue, 28 Nov 2023 17:57:35 -1000 Subject: [PATCH 03/13] Fix various missing things and bugs --- database/entities/User.ts | 5 +- index.ts | 4 +- package.json | 2 +- .../v1/accounts/verify_credentials/index.ts | 8 --- server/api/api/v1/instance/index.ts | 52 ++++++++++++++++--- types/entities/account.ts | 1 + 6 files changed, 52 insertions(+), 20 deletions(-) diff --git a/database/entities/User.ts b/database/entities/User.ts index 462292ac..6c781fb6 100644 --- a/database/entities/User.ts +++ b/database/entities/User.ts @@ -376,7 +376,10 @@ export const userToAPI = ( discoverable: undefined, mute_expires_at: undefined, group: false, - role: undefined, + pleroma: { + is_admin: user.isAdmin, + is_moderator: user.isAdmin, + }, }; }; diff --git a/index.ts b/index.ts index b812efc9..b1aadd86 100644 --- a/index.ts +++ b/index.ts @@ -33,9 +33,9 @@ if (!(await requests_log.exists())) { } // Check if database is reachable -const postCount = 0; +let postCount = 0; try { - await client.status.count(); + postCount = await client.status.count(); } catch (e) { const error = e as PrismaClientInitializationError; console.error( diff --git a/package.json b/package.json index a8cbc3f1..fb68b757 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "lysand", "module": "index.ts", "type": "module", - "version": "0.0.1", + "version": "0.1.2", "description": "A project to build a federated social network", "author": { "email": "contact@cpluspatch.com", diff --git a/server/api/api/v1/accounts/verify_credentials/index.ts b/server/api/api/v1/accounts/verify_credentials/index.ts index 49964db5..94543895 100644 --- a/server/api/api/v1/accounts/verify_credentials/index.ts +++ b/server/api/api/v1/accounts/verify_credentials/index.ts @@ -23,13 +23,5 @@ export default async (req: Request): Promise => { return jsonResponse({ ...userToAPI(user, true), - // TODO: Add role support - role: { - id: 0, - name: "", - permissions: "", - color: "", - highlighted: false, - }, }); }; diff --git a/server/api/api/v1/instance/index.ts b/server/api/api/v1/instance/index.ts index 1904aa31..d9a87537 100644 --- a/server/api/api/v1/instance/index.ts +++ b/server/api/api/v1/instance/index.ts @@ -2,6 +2,9 @@ import { applyConfig } from "@api"; import { getConfig } from "@config"; import { jsonResponse } from "@response"; import { client } from "~database/datasource"; +import { userRelations, userToAPI } from "~database/entities/User"; +import type { APIInstance } from "~types/entities/instance"; +import manifest from "~package.json"; export const meta = applyConfig({ allowedMethods: ["GET"], @@ -22,6 +25,9 @@ export const meta = applyConfig({ export default async (): Promise => { const config = getConfig(); + // Get software version from package.json + const version = manifest.version; + const statusCount = await client.status.count({ where: { instanceId: null, @@ -33,12 +39,40 @@ export default async (): Promise => { }, }); + // Get the first created admin user + const contactAccount = await client.user.findFirst({ + where: { + instanceId: null, + isAdmin: true, + }, + orderBy: { + id: "asc", + }, + include: userRelations, + }); + + // Get user that have posted once in the last 30 days + const monthlyActiveUsers = await client.user.count({ + where: { + instanceId: null, + statuses: { + some: { + createdAt: { + gte: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000), + }, + }, + }, + }, + }); + + const knownDomainsCount = await client.instance.count(); + // TODO: fill in more values return jsonResponse({ approval_required: false, configuration: { media_attachments: { - image_matrix_limit: 10, + image_matrix_limit: config.validation.max_media_attachments, image_size_limit: config.validation.max_media_size, supported_mime_types: config.validation.allowed_mime_types, video_frame_limit: 60, @@ -46,9 +80,10 @@ export default async (): Promise => { video_size_limit: config.validation.max_media_size, }, polls: { - max_characters_per_option: 100, - max_expiration: 60 * 60 * 24 * 365 * 100, // 100 years, - max_options: 40, + max_characters_per_option: + config.validation.max_poll_option_size, + max_expiration: config.validation.max_poll_duration, + max_options: config.validation.max_poll_options, min_expiration: 60, }, statuses: { @@ -70,7 +105,7 @@ export default async (): Promise => { languages: ["en"], rules: [], stats: { - domain_count: 1, + domain_count: knownDomainsCount, status_count: statusCount, user_count: userCount, }, @@ -80,7 +115,7 @@ export default async (): Promise => { urls: { streaming_api: "", }, - version: "4.2.0+glitch (compatible; Lysand 0.0.1)", + version: `4.2.0+glitch (compatible; Lysand ${version}})`, max_toot_chars: config.validation.max_note_size, pleroma: { metadata: { @@ -115,8 +150,9 @@ export default async (): Promise => { privileged_staff: false, }, stats: { - mau: 2, + mau: monthlyActiveUsers, }, }, - }); + contact_account: contactAccount ? userToAPI(contactAccount) : null, + } as APIInstance); }; diff --git a/types/entities/account.ts b/types/entities/account.ts index 6348dc09..0d418927 100644 --- a/types/entities/account.ts +++ b/types/entities/account.ts @@ -31,4 +31,5 @@ export interface APIAccount { source?: APISource; role?: APIRole; mute_expires_at?: string; + pleroma?: any; } From 91838e7aecf3eee4bde70001eb60811027eececf Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Wed, 29 Nov 2023 08:46:34 -1000 Subject: [PATCH 04/13] Add new CLI command: Search --- bun.lockb | Bin 342400 -> 343464 bytes cli.ts | 170 ++++++++++++++++++++++++++++++++++++++++++++------- package.json | 2 + 3 files changed, 151 insertions(+), 21 deletions(-) diff --git a/bun.lockb b/bun.lockb index 43fc0ec32e55fc482d0e3f9bd2a3452b314555a1..00d627a2ce70be44ca95aeca154762d4d1453387 100755 GIT binary patch delta 63354 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

NcgQY?E^a2d%4XA0B^#*k74T$ss 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 G&G|3xXHq!; diff --git a/cli.ts b/cli.ts index c9533e57..8d99616d 100644 --- a/cli.ts +++ b/cli.ts @@ -1,43 +1,70 @@ +import type { Prisma } from "@prisma/client"; import chalk from "chalk"; import { client } from "~database/datasource"; import { createNewLocalUser } from "~database/entities/User"; +import Table from "cli-table"; const args = process.argv; +/** + * Make the text have a width of 20 characters, padding with gray dots + * Text can be a Chalk string, in which case formatting codes should not be counted in text length + * @param text The text to align + */ +const alignDots = (text: string, length = 20) => { + // Remove formatting codes + // eslint-disable-next-line no-control-regex + const textLength = text.replace(/\u001b\[\d+m/g, "").length; + const dots = ".".repeat(length - textLength); + return `${text}${chalk.gray(dots)}`; +}; + +const alignDotsSmall = (text: string, length = 16) => alignDots(text, length); + const help = ` ${chalk.bold(`Usage: bun cli ${chalk.blue("[...flags]")} [...args]`)} ${chalk.bold("Commands:")} - ${chalk.blue("help")} ${chalk.gray( - "................." - )} Show this help message - ${chalk.blue("user")} ${chalk.gray(".................")} Manage users - ${chalk.blue("create")} ${chalk.gray("...........")} Create a new user - ${chalk.green("username")} ${chalk.gray( - "....." - )} Username of the user - ${chalk.green("password")} ${chalk.gray( - "....." - )} Password of the user - ${chalk.green("email")} ${chalk.gray("........")} Email of the user - ${chalk.yellow("--admin")} ${chalk.gray( - "......" + ${alignDots(chalk.blue("help"), 24)} Show this help message + ${alignDots(chalk.blue("user"), 24)} Manage users + ${alignDots(chalk.blue("create"))} Create a new user + ${alignDotsSmall(chalk.green("username"))} Username of the user + ${alignDotsSmall(chalk.green("password"))} Password of the user + ${alignDotsSmall(chalk.green("email"))} Email of the user + ${alignDotsSmall( + chalk.yellow("--admin") )} Make the user an admin (optional) ${chalk.bold("Example:")} ${chalk.bgGray( `bun cli user create admin password123 admin@gmail.com --admin` )} - ${chalk.blue("delete")} ${chalk.gray("...........")} Delete a user - ${chalk.green("username")} ${chalk.gray( - "....." - )} Username of the user + ${alignDots(chalk.blue("delete"))} Delete a user + ${alignDotsSmall(chalk.green("username"))} Username of the user ${chalk.bold("Example:")} ${chalk.bgGray( `bun cli user delete admin` )} - ${chalk.blue("list")} ${chalk.gray(".............")} List all users - ${chalk.yellow("--admins")} ${chalk.gray( - "....." + ${alignDots(chalk.blue("list"))} List all users + ${alignDotsSmall( + chalk.yellow("--admins") )} List only admins (optional) ${chalk.bold("Example:")} ${chalk.bgGray(`bun cli user list`)} + ${alignDots(chalk.blue("search"))} Search for a user + ${alignDotsSmall(chalk.green("query"))} Query to search for + ${alignDotsSmall( + chalk.yellow("--displayname") + )} Search by display name (optional) + ${alignDotsSmall(chalk.yellow("--bio"))} Search in bio (optional) + ${alignDotsSmall( + chalk.yellow("--local") + )} Search in local users (optional) + ${alignDotsSmall( + chalk.yellow("--remote") + )} Search in remote users (optional) + ${alignDotsSmall( + chalk.yellow("--email") + )} Search in emails (optional) + ${chalk.bold("Example:")} ${chalk.bgGray( + `bun cli user search admin` + )} `; if (args.length < 3) { @@ -158,6 +185,107 @@ switch (command) { } break; } + case "search": { + const argsWithoutFlags = args.filter( + arg => !arg.startsWith("--") + ); + const query = argsWithoutFlags[4]; + + if (!query) { + console.log(`${chalk.red(`✗`)} Missing query`); + process.exit(1); + } + + const displayname = args.includes("--displayname"); + const bio = args.includes("--bio"); + const local = args.includes("--local"); + const remote = args.includes("--remote"); + const email = args.includes("--email"); + + const queries: Prisma.UserWhereInput[] = []; + + if (displayname) { + queries.push({ + displayName: { + contains: query, + mode: "insensitive", + }, + }); + } + + if (bio) { + queries.push({ + note: { + contains: query, + mode: "insensitive", + }, + }); + } + + if (local) { + queries.push({ + instanceId: null, + }); + } + + if (remote) { + queries.push({ + instanceId: { + not: null, + }, + }); + } + + if (email) { + queries.push({ + email: { + contains: query, + mode: "insensitive", + }, + }); + } + + const users = await client.user.findMany({ + where: { + AND: queries, + }, + include: { + instance: true, + }, + }); + + console.log( + `${chalk.green(`✓`)} Found ${chalk.blue( + users.length + )} users` + ); + + const table = new Table({ + head: [ + chalk.white(chalk.bold("Username")), + chalk.white(chalk.bold("Email")), + chalk.white(chalk.bold("Display Name")), + chalk.white(chalk.bold("Admin?")), + chalk.white(chalk.bold("Instance URL")), + ], + }); + + for (const user of users) { + table.push([ + chalk.yellow(`@${user.username}`), + chalk.green(user.email), + chalk.blue(user.displayName), + chalk.red(user.isAdmin ? "Yes" : "No"), + chalk.blue( + user.instanceId ? user.instance?.base_url : "Local" + ), + ]); + } + + console.log(table.toString()); + + break; + } default: console.log(`Unknown command ${chalk.blue(command)}`); break; diff --git a/package.json b/package.json index fb68b757..23c4985f 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "devDependencies": { "@julr/unocss-preset-forms": "^0.1.0", "@microsoft/eslint-formatter-sarif": "^3.0.0", + "@types/cli-table": "^0.3.4", "@types/html-to-text": "^9.0.4", "@types/jsonld": "^1.5.13", "@typescript-eslint/eslint-plugin": "^6.13.1", @@ -73,6 +74,7 @@ "blurhash": "^2.0.5", "bullmq": "^4.14.4", "chalk": "^5.3.0", + "cli-table": "^0.3.11", "eventemitter3": "^5.0.1", "html-to-text": "^9.0.5", "ip-matching": "^2.1.2", From 5ddacdbce50d3afbf8b506cd28262904500e3446 Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Wed, 29 Nov 2023 08:56:07 -1000 Subject: [PATCH 05/13] Add --json and --csv to CLI --- cli.ts | 94 ++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 68 insertions(+), 26 deletions(-) diff --git a/cli.ts b/cli.ts index 8d99616d..9146bb44 100644 --- a/cli.ts +++ b/cli.ts @@ -62,6 +62,8 @@ ${chalk.bold("Commands:")} ${alignDotsSmall( chalk.yellow("--email") )} Search in emails (optional) + ${alignDotsSmall(chalk.yellow("--json"))} Output as JSON (optional) + ${alignDotsSmall(chalk.yellow("--csv"))} Output as CSV (optional) ${chalk.bold("Example:")} ${chalk.bgGray( `bun cli user search admin` )} @@ -201,6 +203,8 @@ switch (command) { const local = args.includes("--local"); const remote = args.includes("--remote"); const email = args.includes("--email"); + const json = args.includes("--json"); + const csv = args.includes("--csv"); const queries: Prisma.UserWhereInput[] = []; @@ -254,36 +258,74 @@ switch (command) { }, }); - console.log( - `${chalk.green(`✓`)} Found ${chalk.blue( - users.length - )} users` - ); + if (json || csv) { + if (json) { + console.log(JSON.stringify(users, null, 4)); + } + if (csv) { + // Convert the outputted JSON to CSV + + // Remove all object children from each object + const items = users.map(user => { + const item = { + ...user, + instance: undefined, + endpoints: undefined, + source: undefined, + }; + return item; + }); + const replacer = (key: string, value: any): any => + value === null ? "" : value; // Null values are returned as empty strings + const header = Object.keys(items[0]); + const csv = [ + header.join(","), // header row first + ...items.map(row => + header + .map(fieldName => + // @ts-expect-error This is fine + JSON.stringify(row[fieldName], replacer) + ) + .join(",") + ), + ].join("\r\n"); + + console.log(csv); + } + } else { + console.log( + `${chalk.green(`✓`)} Found ${chalk.blue( + users.length + )} users` + ); - const table = new Table({ - head: [ - chalk.white(chalk.bold("Username")), - chalk.white(chalk.bold("Email")), - chalk.white(chalk.bold("Display Name")), - chalk.white(chalk.bold("Admin?")), - chalk.white(chalk.bold("Instance URL")), - ], - }); + const table = new Table({ + head: [ + chalk.white(chalk.bold("Username")), + chalk.white(chalk.bold("Email")), + chalk.white(chalk.bold("Display Name")), + chalk.white(chalk.bold("Admin?")), + chalk.white(chalk.bold("Instance URL")), + ], + }); - for (const user of users) { - table.push([ - chalk.yellow(`@${user.username}`), - chalk.green(user.email), - chalk.blue(user.displayName), - chalk.red(user.isAdmin ? "Yes" : "No"), - chalk.blue( - user.instanceId ? user.instance?.base_url : "Local" - ), - ]); + for (const user of users) { + table.push([ + chalk.yellow(`@${user.username}`), + chalk.green(user.email), + chalk.blue(user.displayName), + chalk.red(user.isAdmin ? "Yes" : "No"), + chalk.blue( + user.instanceId + ? user.instance?.base_url + : "Local" + ), + ]); + } + + console.log(table.toString()); } - console.log(table.toString()); - break; } default: From a92b45da4117ad5199dc2e16d532d5674e9fb333 Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Wed, 29 Nov 2023 09:06:28 -1000 Subject: [PATCH 06/13] Add Note search command to CLI --- cli.ts | 138 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/cli.ts b/cli.ts index 9146bb44..e0a72fc7 100644 --- a/cli.ts +++ b/cli.ts @@ -67,6 +67,21 @@ ${chalk.bold("Commands:")} ${chalk.bold("Example:")} ${chalk.bgGray( `bun cli user search admin` )} + ${alignDots(chalk.blue("note"), 24)} Manage notes + ${alignDots(chalk.blue("search"))} Search for a status + ${alignDotsSmall(chalk.green("query"))} Query to search for + ${alignDotsSmall( + chalk.yellow("--local") + )} Search in local statuses (optional) + ${alignDotsSmall( + chalk.yellow("--remote") + )} Search in remote statuses (optional) + ${alignDotsSmall(chalk.yellow("--json"))} Output as JSON (optional) + ${alignDotsSmall(chalk.yellow("--csv"))} Output as CSV (optional) + ${chalk.bold("Example:")} ${chalk.bgGray( + `bun cli note search hello` + )} + `; if (args.length < 3) { @@ -170,6 +185,7 @@ switch (command) { where: { isAdmin: admins || undefined, }, + take: 200, }); console.log( @@ -256,6 +272,7 @@ switch (command) { include: { instance: true, }, + take: 40, }); if (json || csv) { @@ -333,6 +350,127 @@ switch (command) { break; } break; + case "note": { + switch (args[3]) { + case "search": { + const argsWithoutFlags = args.filter( + arg => !arg.startsWith("--") + ); + const query = argsWithoutFlags[4]; + + if (!query) { + console.log(`${chalk.red(`✗`)} Missing query`); + process.exit(1); + } + + const local = args.includes("--local"); + const remote = args.includes("--remote"); + const json = args.includes("--json"); + const csv = args.includes("--csv"); + + const queries: Prisma.StatusWhereInput[] = []; + + if (local) { + queries.push({ + instanceId: null, + }); + } + + if (remote) { + queries.push({ + instanceId: { + not: null, + }, + }); + } + + const statuses = await client.status.findMany({ + where: { + AND: queries, + content: { + contains: query, + mode: "insensitive", + }, + }, + take: 40, + include: { + author: true, + instance: true, + }, + }); + + if (json || csv) { + if (json) { + console.log(JSON.stringify(statuses, null, 4)); + } + if (csv) { + // Convert the outputted JSON to CSV + + // Remove all object children from each object + const items = statuses.map(status => { + const item = { + ...status, + author: undefined, + instance: undefined, + }; + return item; + }); + const replacer = (key: string, value: any): any => + value === null ? "" : value; // Null values are returned as empty strings + const header = Object.keys(items[0]); + const csv = [ + header.join(","), // header row first + ...items.map(row => + header + .map(fieldName => + // @ts-expect-error This is fine + JSON.stringify(row[fieldName], replacer) + ) + .join(",") + ), + ].join("\r\n"); + + console.log(csv); + } + } else { + console.log( + `${chalk.green(`✓`)} Found ${chalk.blue( + statuses.length + )} statuses` + ); + + const table = new Table({ + head: [ + chalk.white(chalk.bold("Username")), + chalk.white(chalk.bold("Instance URL")), + chalk.white(chalk.bold("Content")), + ], + }); + + for (const status of statuses) { + table.push([ + chalk.yellow(`@${status.author.username}`), + chalk.blue( + status.instanceId + ? status.instance?.base_url + : "Local" + ), + chalk.green(status.content.slice(0, 50)), + ]); + } + + console.log(table.toString()); + } + + break; + } + default: + console.log(`Unknown command ${chalk.blue(command)}`); + break; + } + + break; + } default: console.log(`Unknown command ${chalk.blue(command)}`); break; From 128827cee4b0d704b1f4480b275dc4fea264c60a Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Wed, 29 Nov 2023 09:08:09 -1000 Subject: [PATCH 07/13] Add Note delete command to CLI --- cli.ts | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/cli.ts b/cli.ts index e0a72fc7..a42f4d77 100644 --- a/cli.ts +++ b/cli.ts @@ -68,6 +68,11 @@ ${chalk.bold("Commands:")} `bun cli user search admin` )} ${alignDots(chalk.blue("note"), 24)} Manage notes + ${alignDots(chalk.blue("delete"))} Delete a note + ${alignDotsSmall(chalk.green("id"))} ID of the note + ${chalk.bold("Example:")} ${chalk.bgGray( + `bun cli note delete 018c1838-6e0b-73c4-a157-a91ea4e25d1d` + )} ${alignDots(chalk.blue("search"))} Search for a status ${alignDotsSmall(chalk.green("query"))} Query to search for ${alignDotsSmall( @@ -352,6 +357,37 @@ switch (command) { break; case "note": { switch (args[3]) { + case "delete": { + const id = args[4]; + + if (!id) { + console.log(`${chalk.red(`✗`)} Missing ID`); + process.exit(1); + } + + const note = await client.status.findFirst({ + where: { + id: id, + }, + }); + + if (!note) { + console.log(`${chalk.red(`✗`)} Note not found`); + process.exit(1); + } + + await client.status.delete({ + where: { + id: note.id, + }, + }); + + console.log( + `${chalk.green(`✓`)} Deleted note ${chalk.blue(note.id)}` + ); + + break; + } case "search": { const argsWithoutFlags = args.filter( arg => !arg.startsWith("--") From c7c11d8d2b920c8f04013a9b5402d089e601ef6c Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Wed, 29 Nov 2023 09:32:46 -1000 Subject: [PATCH 08/13] chore: Update documentation --- CONTRIBUTING.md | 12 +++++++++--- README.md | 6 ++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2b8cd2f4..19e9ce47 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -67,8 +67,14 @@ RUN chmod +x /docker-entrypoint-initdb.d/init.sh ``` 4. Copy the `config.toml.example` file to `config.toml` and fill in the values (you can leave most things to the default, but you will need to configure things such as the database connection) + +5. Generate the Prisma client: -5. Run migrations: +```bash +bun prisma generate +``` + +6. Run migrations: ```bash bun migrate @@ -130,9 +136,9 @@ When you are done with your changes, you can open a pull request. Please make su We use Bun's integrated testing system to write tests. You can find more information about it [here](https://bun.sh/docs/cli/test). It uses a Jest-like syntax. -Tests **must** be written for all API routes and all functions that are not trivial. If you are not sure whether you should write a test for something, you probably should. +Tests **should** be written for all API routes and all functions that are not trivial. If you are not sure whether you should write a test for something, you probably should. -To help with the creation of tests, you may find [GitHub Copilot](https://copilot.github.com/) useful (or some of its free alternatives like [Codeium](https://codeium.com/)). Please do not blindly copy the code that it generates, but use it as a starting point for your own tests. +To help with the creation of tests, you may find [GitHub Copilot](https://copilot.github.com/) useful (or some of its free alternatives like [Codeium](https://codeium.com/)). Please do not blindly copy the code that it generates, but use it as a starting point for your own tests. I recognize that writing tests is very tedious, which is why LLMs can come in handy. ### Writing documentation diff --git a/README.md b/README.md index 2cbc5774..d54ddb74 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,12 @@ bun cli You can use the `help` command to see a list of available commands. These include creating users, deleting users and more. +#### Scripting with the CLI + +Some CLI commands that return data as tables can be used in scripts. To do so, you can use the `--json` flag to output the data as JSON instead of a table, or even `--csv` to output the data as CSV. See `bun cli help` for more information. + +Flags can be used in any order and anywhere in the script (except for the `bun cli` command itself). The command arguments themselves must be in the correct order, however. + ### Using Database Commands The `bun prisma` commands allows you to use Prisma commands without needing to add in environment variables for the database config. Just run Prisma commands as you would normally, replacing `bunx prisma` with `bun prisma`. From fdf32c0b6c74db755453f384db7f99946d0ba3aa Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Wed, 29 Nov 2023 10:29:29 -1000 Subject: [PATCH 09/13] fix: Fix crash on serving local media --- server/api/media/[id]/index.ts | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/server/api/media/[id]/index.ts b/server/api/media/[id]/index.ts index 4857a0cd..cd8ebfae 100644 --- a/server/api/media/[id]/index.ts +++ b/server/api/media/[id]/index.ts @@ -22,11 +22,28 @@ export default async ( const id = matchedRoute.params.id; + // parse `Range` header + const [start = 0, end = Infinity] = ( + (req.headers.get("Range") || "") + .split("=") // ["Range: bytes", "0-100"] + .at(-1) || "" + ) // "0-100" + .split("-") // ["0", "100"] + .map(Number); // [0, 100] + // Serve file from filesystem const file = Bun.file(`./uploads/${id}`); + const buffer = await file.arrayBuffer(); + if (!(await file.exists())) return errorResponse("File not found", 404); - // @ts-expect-error Bun allows this - return new Response(file); + // Can't directly copy file into Response because this crashes Bun for now + return new Response(buffer, { + headers: { + "Content-Type": file.type || "application/octet-stream", + "Content-Length": `${file.size - start}`, + "Content-Range": `bytes ${start}-${end}/${file.size}`, + }, + }); }; From 15ef1851fc7de2563a34968bd0c2c5b76e281301 Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Wed, 29 Nov 2023 16:45:58 -1000 Subject: [PATCH 10/13] Update lockfile --- bun.lockb | Bin 343464 -> 343464 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/bun.lockb b/bun.lockb index 00d627a2ce70be44ca95aeca154762d4d1453387..0f28a8644432d21b8507a8007bfbb11d73de62e7 100755 GIT binary patch delta 33 pcmZ4STV%y=k%kt=7N#xC` Date: Wed, 29 Nov 2023 18:16:58 -1000 Subject: [PATCH 11/13] feat: Add timeline benchmark --- README.md | 15 +++++++++++ benchmarks/timelines.ts | 56 +++++++++++++++++++++++++++++++++++++++++ index.ts | 3 ++- package.json | 1 + 4 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 benchmarks/timelines.ts diff --git a/README.md b/README.md index d54ddb74..6d3a627a 100644 --- a/README.md +++ b/README.md @@ -23,10 +23,25 @@ This project aims to be a fully featured social network, with a focus on privacy - [x] Full regex-based filters for posts, users and media - [x] Custom emoji support - [x] Automatic image conversion to WebP or other formats +- [x] Scripting-compatible CLI with JSON and CSV outputs - [ ] Moderation tools - [ ] Full Mastodon API support - [ ] Outbound federation +## Benchmarks + +> **Note**: These benchmarks are not representative of real-world performance, and are only meant to be used as a rough guide. + +### Timeline Benchmarks + +You may run the following command to benchmark the `/api/v1/timelines/home` endpoint: + +```bash +TOKEN=token_here bun benchmark:timeline +``` + +The `request_count` variable is optional and defaults to 100. `TOKEN` is your personal user token, used to login to the API. + ## How do I run it? ### Requirements diff --git a/benchmarks/timelines.ts b/benchmarks/timelines.ts new file mode 100644 index 00000000..2804f691 --- /dev/null +++ b/benchmarks/timelines.ts @@ -0,0 +1,56 @@ +/** + * Usage: TOKEN=your_token_here bun benchmark:timeline + */ + +import { getConfig } from "@config"; +import chalk from "chalk"; + +const config = getConfig(); + +const token = process.env.TOKEN; +const requestCount = Number(process.argv[2]) || 100; + +if (!token) { + console.log( + `${chalk.red( + "✗" + )} No token provided. Provide one via the TOKEN environment variable.` + ); + process.exit(1); +} + +const fetchTimeline = () => + fetch(`${config.http.base_url}/api/v1/timelines/home`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }).then(res => res.ok); + +const timeNow = performance.now(); + +const requests = Array.from({ length: requestCount }, () => fetchTimeline()); + +Promise.all(requests) + .then(results => { + const timeTaken = performance.now() - timeNow; + if (results.every(t => t)) { + console.log(`${chalk.green("✓")} All requests succeeded`); + } else { + console.log( + `${chalk.red("✗")} ${ + results.filter(t => !t).length + } requests failed` + ); + } + console.log( + `${chalk.green("✓")} ${ + requests.length + } requests fulfilled in ${chalk.bold( + (timeTaken / 1000).toFixed(5) + )}s` + ); + }) + .catch(err => { + console.log(`${chalk.red("✗")} ${err}`); + process.exit(1); + }); diff --git a/index.ts b/index.ts index b1aadd86..ce7ababe 100644 --- a/index.ts +++ b/index.ts @@ -12,6 +12,7 @@ import { client } from "~database/datasource"; import type { PrismaClientInitializationError } from "@prisma/client/runtime/library"; import { HookTypes, Server } from "~plugins/types"; +const timeAtStart = performance.now(); const server = new Server(); const router = new Bun.FileSystemRouter({ @@ -171,7 +172,7 @@ console.log( `${chalk.green(`✓`)} ${chalk.bold( `Lysand started at ${chalk.blue( `${config.http.bind}:${config.http.bind_port}` - )}` + )} in ${chalk.gray((performance.now() - timeAtStart).toFixed(0))}ms` )}` ); diff --git a/package.json b/package.json index 23c4985f..90da5362 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "lint": "eslint --config .eslintrc.cjs --ext .ts .", "prisma": "bun run prisma.ts", "generate": "bun prisma generate", + "benchmark:timeline": "bun run benchmarks/timelines.ts", "cli": "bun run cli.ts" }, "trustedDependencies": [ From a17b52b2c56b456b2f3161530aa143d8f9312ff7 Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Fri, 1 Dec 2023 13:00:00 -1000 Subject: [PATCH 12/13] feat: Add new Redis caching to queries --- README.md | 25 +++++++++++++++- bun.lockb | Bin 343464 -> 345808 bytes config/config.example.toml | 11 +++++-- index.ts | 7 +++++ package.json | 3 ++ utils/config.ts | 16 +++++++++- utils/redis.ts | 60 +++++++++++++++++++++++++++++++++++++ 7 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 utils/redis.ts diff --git a/README.md b/README.md index 6d3a627a..b23635cf 100644 --- a/README.md +++ b/README.md @@ -9,13 +9,14 @@ This is a project to create a federated social network based on the [Lysand](https://lysand.org) protocol. It is currently in alpha phase, with basic federation and API support. -This project aims to be a fully featured social network, with a focus on privacy and security. It will implement the Mastodon API for support with clients that already support Mastodon or Pleroma. +This project aims to be a fully featured social network, with a focus on privacy, security, and performance. It will implement the Mastodon API for support with clients that already support Mastodon or Pleroma. > **Note:** This project is not affiliated with Mastodon or Pleroma, and is not a fork of either project. It is a new project built from the ground up. ## Features - [x] Inbound federation +- [x] Hyper fast (thousands of HTTP requests per second) - [x] S3 or local media storage - [x] Deduplication of uploaded files - [x] Federation limits @@ -42,6 +43,28 @@ TOKEN=token_here bun benchmark:timeline The `request_count` variable is optional and defaults to 100. `TOKEN` is your personal user token, used to login to the API. +On a quad-core laptop: + +``` +$ bun run benchmarks/timelines.ts 100 +✓ All requests succeeded +✓ 100 requests fulfilled in 0.12611s +``` + +``` +$ bun run benchmarks/timelines.ts 1000 +✓ All requests succeeded +✓ 1000 requests fulfilled in 0.90925s +``` + +``` +$ bun run benchmarks/timelines.ts 10000 +✓ All requests succeeded +✓ 10000 requests fulfilled in 12.44852s +``` + +Lysand is extremely fast and can handle tens of thousands of HTTP requests per second on a good server. + ## How do I run it? ### Requirements diff --git a/bun.lockb b/bun.lockb index 0f28a8644432d21b8507a8007bfbb11d73de62e7..2cfe188606c5449d2ac4dc6e86915b3146d539f4 100755 GIT binary patch delta 68274 zcmeFaXM9vu`?ftZFpvRJkd~l;fC`9Fv5-cFULpiRYJdPCL`}i;Khv)tBx_?|b*LAG2SKoWjB%3w= zYWDi#=1WfN+^)&G;9VD%_b%`M`Z--1x4-D#M;m;dy}WGLlDzv`4Q*WXS1m?YrHb=LhP0&y41_H;Rvr7x|CzI|D zcszbH^aQjDZH4|A3Iy6K9lpwW7yo#40)7j06MmaY0s|emNdW(Bm*G;EAqQ;@?~S%a z6H!&D1=vn0Q?FeebG##h0GIVCgl z7@Fy&#YF|^>ZZ0LKR8|Cv=UX<&ncRcpPNrvbd&PGMD11a8D+LYYv5||pceGMYSfDX z5qOvgwe(Yb@zYVYa5-EpXwN{4FQtc+ZhGmI;zG(ilwi|cRBBJ~9lpwY%JJDHXLnP~ z%sEA6xy4iRK8CAeZ&bQK0y9Gyic!_{0k}?d2P%IU6F@z5C#oU30i_q`^+Oduv$ViJ zvmw2Durz0Gp6W8UuppQDTzZY;GYRLN6A0uL&CZ{lpI7RAABw9iAo2t)8!L{yasx3}?y`Gt9jQzi#~ZD(7v9j;2{l$I6c&d8tLf%Gb;mCKh~T3R~0 ztRU}0dQs)Pic)^7c@-zx?yRC)6;Xkz6|=nC>$Du!`(#^8L0(R-8u|Q5HkPiRGdsVa zv~EWm|10Tq`i;a(A3#;wGE@t13Z<&=#(9yt9mhP`**2=Mbn48U+!-Y|onjl?>C`}g zfAfCtV)a$@H2gc9KL<_3?~k62o{XyV3W}%Xluio-yw~f-SDtl-J@tF2#`*NFwo3U$ z)ACC4XP4#|T~Lreb=vGs#5W{<1C@~$%$ZwUL=z_$%qf{hahX(EP3e1^HG;-lM5@Q`$g;hV-U1>xa7Af^Kse9>7l^{!22dfCZ@h zhtmUrQH=gi4x#|Q-K;j2ZT(}H&BPE=i6jcU(WjVk^1sCw*TR23hTVNZAl zs{DQ6+NG~JHxTH69zDpWD@T=2mxap7oUniv=mgt|V3yBYFxZ}`6$LQd^9~WOioA+y zFIkVOC9!-<@ztVov?Y2js+K=}o=yKW%UBgJ8EPxug?ROFqi*!SYWNEY)S~xL)qDlY zQkhrjGLCV63aX4vQI^oW=m6$RGu2673yn3*74T8se31)i_&DKYTT4PEj#iNg&W` zg5CTs%%7b{hX-;d+Eui^lE6_Uyr4AiLUy{+lH!7bIWyUQi%VxK=i`&?iPxjrpo(&e ziZx5}N+-|B=K?c}eMXaU0;&p}i#9`(9Y4|eO0P%$Yq+!}Dp8R}*Pd z*$u3*`J9|8=^n;KV{ibKKe;3)SC_6Svn;0XU6PsN3&9&?II#fGViSx5iZ6s-E z8*PmV1lj{?F0#wweW%rE8~82GzYJADQ&EjweNNmzhtgz8^m{m(DA^?eCdJ+g^c z$En+M=+<8Netj0${o^*YHSr5jr61zbzd7G_AU7lhGc?DlYzK3pOT3^Ur*i5ffR@il zR6`rPAC29gHic_w2VZTc!W2%RhJ8xG;?qm>N?DPCvpAuqOf$4C+T|Kskz-JG=uf0q zzW=;ouACj1x!AUR>a})xQCvw@#Epe7vwQR1A(OFw#%pFai>7f3?lwm;?G1iCtIVM5xEA3D%aym1ypg1>Yb{?8vm_IwP=w=(wUXhza z1xBp070*C5#KlEfP3V!}%@lu_r3XyRb-Q9fhw3 z?04yYswAK>nUY8E@znJU8CBE2-b-e$wHY5HUOlxGJqev&npl=oSP;l5;f9vG{LA4- z!{?%EY5vqAp63FAVth3)wtNrWXZ_09jyjP9T29a2Z%Ez-7C8AqTV7r%eMm*pNvBDC8=MMN&g%+L zixxd(YZTiLrWEJSF3B&-`!6<*sj>YcXS!dXz?_o&#ObAhz@mrka-D^0>|0U3HpbuY zva4(_sts!PBU+B^%sT-ZlNV9-z~o154M(A>SmVd6)i}av&_a>WfBB~zneXalV06K9`R9(nJT0vfZdEQ@_PpxJ1x2xu_ ztEIsfyKJv}!k+jtR1IiWZ7cZaX7^FR%>bSF6k4LL8t|me(1_U{k6$!Xb2kvkc-p3W zAJy2u?sVpynOf$7sZUw{3|y12*)z81N}ZNypXP}5aAAH?B^yTR?EIp9KjLc=s^Cee zx@c-KYmDRj=WNY;JZ}s95MLY8o2Z89IaDWn%xP?&m^r7=uX@85ZT@w{Yv^u5RsO=~ z=zm?_$8EC(4MEjKv4Z|O+f#|xju|__srWkaYzowt`tVD3D5v0SNFRCH#xnzHiu(PC<2=`{2?WUAo0iFGsaDx^sFJ z-{mdaacxnR6Wd0*Jngk=)~50VSIZdIT~*@D8B=5aEAQCi^53u^2n7opb;*!L? zx%zNQ)p(3_EzrO&d&#Uq)op*EjnVH=P1s9NRl|K4Hi-K^w(|?~0(0K8*Wwwd>Yt5j zoaoNti;4n)2j92tS-^OxpcR}@6^UKp7OnI`%}=guNqaP3j&?dTv6SmTLEc>-TFwQ` zUy0fHDrRQll)MXgKrOx~;2-aPWYcX!b^0mAvlC~Q6wi!JkJuK$+(<0SDa;$a*KR3# zAYoBdmd;Ep%$Z41XMbW(a6PJVScs~lhLAxO?2D?MNvPsqrCs7pKeHVaTS&2ih%KPl zyq}rqKQjel4L^0PVqb%p(V<=7F7EttDGkw)g-FKxxH#gE6oVxOHb`KZ6^KKJ(v zg&-F2xrF z796nYx#iabLSP^98k*gxhLHYFES@|)@cg%Ijw5;L7p^8n` zUk}=`zl zDGL0L8;^AL=DM~(-K9-tHdC1YVEkb?=&%2ksD^O7JAr?%NI9$O1^w$@CR{xhYfvsX zi@J+qiZJ(B|FI1})|G!9!<}(K|AH91kj&&WOjWdf1ABtln8sE`3hA{^#3oy8z4yAy z_ViIl1^p}g$CRPZ>F4bvpq=amvp{!~;$Is(wZbk!qO`)itAS@X>w zMZey+b^Cx{KEAN+7h~Hrnltd%&)#oUwxQ?o^MV^*Z#^@(p{i|ii@4L~KX~cp$R96y zPoFb4xXnxH)+79Loj_oim)$#rr3f+J_w4+O>?Nq!yPNI&^!geD&0ed1z# zIiY{^-|%cX0~sJ&&eq41<8ZQLnIl?9IWaGsgJ)av2%*tBSt!1twWZS!tn#$9k#JX!sQ$q({?2 zEt`51Lyjc)|gz2r(UV+1*1uk z_wdwM+ka=V1eG$ZxKKsBm)a*9`ZC@t?-PwQY-RH?2z`=5qg#13z?*^TWH{RI{@pG~ zecwBY8-+9c!gSh^c*%J5RS$*+&klwPd+Ru_yk9ib;dpNga`EwAO}}X5@#AB4QIc?L zue^UWG`6+3rGGT?5KF!{h4~94@*^JWoT4>E-P+h05s(+Uu#LB6Ks0n$8?Rpwo1x37%xg(!0Sc7T#a`&UVTl1(35Sw)Qo88*S21HMl^ED34y>g;+P<$ zT!yDUqtV@yB46NXIPjQr?c3SjwH-1YPt(O;4Us4Cv{HO8bWD41%b;jzaC@(2P&9mb zd%DM~?vWPRNl0z8Ej^CUkyK;q*dr-4yn|OWI2u_3R6+j0L|$@9`~eMxPV}~9MkC!% zw38S}zNII6sY9ZX_khX=?3om9c9K6)ng8b#>Isi1arkw-6t9|v5;@r`KQ9^?e6npJ z{W~BjQiZ2M4tVp&CCA~Y3*-E*IJ%>)tY3bpe@Ac2&}itgj$X~sXygUr`jV10%-m?u zDb^Ltjc{MQ(K=&fB_R#7osN4td8y||Bk`T>$gswTGHQ4lQIZTx3SHXSt0Bq9KvgQD z0SY%hg>LsM`lW>?pW9Y>_-Htl%CeL8NDCFEdRxXvBRc?UKu8NIT%Rp*m{&bMEj)%$Z?B?z zTI69uL;VyQ{RGy_keC;m+TBZ?5RJSDJU<4*3G6X`FN7whdo>fH;T-_x$oRBK8cjTs zJF*aO5ZM?^Hu<;lMtBt&Y2i*i=sYidNLqLap=o~TAfdskc%*O7Kwyl2GP*qS7T$Q@ z>oq&yy2 zls+LX@+u)F2@|W=o5^uFx^A+|_D%|o7~s|9MZ+5hut|G|^U}gU6Y^^n&R_uy^Vv;= z`Y06IGf+*96lQP(>X*@r>nn~eQ0MQ+Mwjik?;b)^{ZL2xd$>Z8MT9ir=zJQsdyrQ% zEgCv%u$P)2jbw1i(Y|QoZXN7x$&ZGA2A2ATOy#1Qt5E2rOs{-;G}Ls6w`F=XT*T0( zd)3p^!uJu%@k2+Q7YLl^hsF`g^A3+o3%^Y$!*5P&#$}|>t|m0ht03?DgeH3F`Dx+Q zVSWQ(h7j|T&^|(={Tw}*am7A+fY3;NVZ#PS8c*aA{h&>qKUom!6RpnM){zq|LlujCS=zuV!{M^v6Umbxt(YZ<1Gzluz=u z%!x*}F@aea+LpLNHpsCv!QZ4pBXYd*3!`BVnCzvOb+4Q2ub>Ol!rKT9@Tv(N!)(m( zLt_ZBzF@14urCPNbcuPfSbbD(b39Hc)6WriL5vL}!~~+0+XxXmw|iXzwve-@#*$SK zvL}1y2x~YkR@@juHpi`m2KmK(MaZ7^to&HId4w$6PAJ>&#N_EJS>0JdNFCcfINeLF zh=z`x;gwfJBY87oyEGTX(5e|;O+_^PE-=l{)ug~*vZZN}Y(i(ywcPq}F;H7WGUE2EJig?4kccR*Em$`H_9PWWj&_6e$Wm{5wBetuf0cagVcUNmxX zk^My)28}!LwRoxifaJ>f-DT;~BGTV@SyyD#BAr?#^iFCcM6Q{%t}e zTw=?#_gp>jTqkhZEydFgMM`e%*W-2b&$o4GavY9|$bUeYp`DX zGJB2jy>L0+STB81kMQ~Awkb|7w^uov;xfD`-eFR((p`$a7Y73W_U^!AJ8*G-)Q$^} zzl5`S6<2qUBQPqKDBPgJUGT$$38nd$+N%hu3HAbYxWY?a91Z7QN}F|u7Fnkd(cIw9 zNs1g*8S7L{&-3we{ptDWkx=|RZK_P!&6IhhuJX1Z*Iwn-EQ^L;yUHKnC25fkRYz(vFDY^v zo~8k}iWy0f7x3J0bG7{gZz#RiJ#OKV3+r#m!G+$I>!XoZVcp5ds-tfjT&=E7_qxXs z(9-muD#F*{ae<`TKl=$(?H-G~)ElGWS&JysZ_$Hz5RH46hp}s^e|_B{{g*OI^<6>a||^@@V+|Yc&QM&ZgI~QIUua zUY-;hb)A>Gf(_w1uY5%`{2g%<{ZkBC!Z7<4xSr4jex-7j#wxXy&?v8hsgs3PjJmz|C&wW-wsNAb2$8Urt_-y12+{)`5p%-uPCrZMNx>#$on@7m* ztB(`niU?R*s zp*tiMJD$)WKXeZvZdjPOf2{CQZ;M89Z=x2qtGFk=1y94s#+=DL;!R!+NgA)DL?3DZ zM&fAz>|@{Ucu_om+#?@3&+hWAZ??0_rW}c zR}$*s_xW3dk`#(`xFhxvk8;kyb3M{MDSSH~(;+u4^uZlo`I>0B;hk=NM9wFqI?_K} zu5Q6gwkM?T-o+c?$LVT2?k;c3+Gymv)i#6wSwy7E-LWR>sx`}b_Nukcd8E8PDRS~X zwtid$c&sbJJAkx6T8gD8x8-iW7evd@|y z;Av*n@juvTvDT}(KN^|3Hdf}=jN~|+o}^$ZB_%~Z#Vhf1tJ1mm+LC#ud@(r=M;*X} z9z%xH*DqK11kLY@%`4SlINsUB`48!ltMF6=8%Pp%a=La0N%&AJvd)%iZwS)y)J04fGF0FxgZ+T!c^5~W(~=^A2V(PBg+(9m zYSu?1vw`X&3ga@n1yA$A-c)_>JgUnMea3^fr~Iab3Lf-QH$)?AfkXW4tk%eHcxs=W zC4(Nay;X5$r?(k}M8 z58D>`Wrlh`?A1KVb3RaA4rI=}g{KqwGbr3)1DA4t(Nz*U56)f7Z^>~uBYj6b)8P@X zd}B1cK%rPySi>q)OrGoEXPwyTjra1 zO6hMWp|;gt`BTxzgzDIYR+a96odIVGa2;AEe#CnnPZQNP`-Ep~C)D${{*fH# z96u`j6yB*`^@6nUuY`v9SA)LK(t~25OP=*=o{dKC1*mNMne0A1bv{#trPS>?`$!V; zZ+{Qt^(M}C>7eJ2Oj3Gb5#C_p{9Xv}RGe3RsC(QCwyJiQ822CEt$5mGX*#3#KAswE z3y**C$ZQ^$6d8-B5#kDZCF>i{&JT6*CwS))$NZp_soQL&s0O#7_iXc0w?!lSfqhA0 zuV{(eV)8FjkjlAiRbnTI{j-r)i3Pt2OVFEwM2XG47>rv(Jpq5XYlM^$a)Pm zc-bp|DH`ebvdzGLKs6WQ+3i_#rW)^jvfJxRt5;%`(DpwD?=<3UZLh@B(y-npJS_&_ z3xBJ8+GE0}?$Ds7S9Omgp!V1c-phFE8*W3WbGKJ*>u4*zcqN_&(@xuM&ahk}pY%i1 zBG(Xdms`HTdF3sy{LN^j{@bzM@hgpYCRy!Fy&TVWbnv;P(B`+j^0$~rK-JNIA`W$Y z$EyM6ykmRE&XRZW>`d)NbsE20dj^%`X*4*)z@*4?c$59AYDYSGr!9riVA)N;(}|cm zy^|t0f(6(F8g664IY~mS%r5#aklwK@3#9q-UUhF(RkeQ59Na#LiY25FL!(8 zJENhK-}AQYj7F}1k4FJ=+c{8!r>^88JR~X9?R~F&7avNzUwdSqz_XKvkq!OvzL&b2 zC%g~rSDFkDYxPz<^_*SR@8H?_s6KAG$F`EYgm04T;&fB>BJ&BU>+SVrgE*euUH9Xu zLv3*#K8$&4d;y*&CUGp+buNx-e4iZWqU;1}`;pyo**}<&d3ZX3ZS_jLL_C(_Wl52b z@N_yqE}ZZ&OW6M)E}M`}#U{HhIS$9xNrU_v-Wf!3@tT(u`Safi*C)sAt^HBrOL!`S z8v#~h=o8y$JN^6M^(T&%IyyNHN4?A*{JQT%act+b`?Oa5BIEEhV)Q3>EC$bJqp!oe zmD0ZfY5SR-{FFd(IH!}PffwA$114S?9*rl<4|sf7&wi5rxh9Oh5vd?#hm%u;H{hk} zgQv)^3X#)x)EQs+6YP!daRmB;IahVEoFO=NBtu`?>Cft6>;~hhvi`Omz7}twf4;qh zlHha%3n6|VcSe3Hb(z5%fM+}KL%h*=T+%7;oc%VAuEF)@UKk&4a z?9iY8m5sCa{MX=V9OL|5tL@kJ+q`C8@ZqG;q_4g5FQbu_KvmjpHh5?Ip5}JzZ)`97 z;~Acc*WIhm=pIKvXS7@JZ+IGJJDbxF*vkgvyf--xM;*-!qp2_A^~9s8yOZO-wI}ko zrf?db{TQo~kV>O9%>UQ$?6lTS82&D{m^C$e;b~ZS@S|6+#k2EQDYxO-5!QbChl^wH zW?M@B-hRf$mM}gkauJ@UqOJXQyrFpZu{Pm{+FRBvyzzdV&b|ZBE}mXDCPmVIv^BFE z=0(o4Yj^{mnr%nxr+>%M!KeRZ^V!ayfj7#gByKC79pxa`i+|y%p3G_si?ei~Y z;bOf0Ud7Pv!Joa<@1v0e_--C^!_e^;ZwqkdFE$GkgGF*To-4d}Qut#$K8oUL`jlT| zb6xAL2#@WIC36=cx6WBIAK>|WZ{+yjEaxeOk2FT$*-CEZ>#b#Y-TZ~MjgZb}_l3~G zSfh0zjN)k+{ar0|*+DP$=VZ|uESGGp0ZgV`|?{b^u~Bv-?T4oxw7dSrcs8ZB!OeKu64-92-k*3w@a$uCCcgmp?=lzR>Ezr- zJ(C=Vqd~Q&`_*|23TvwqzZa)$_5ySP-Z^*;{eHU}PXlh( z|0^4z2kM#fU@#i_2-?SH;6tJI^{KnRf~FC&-P&tgQsjQTBzqDD;8Q$}ynPJMd?Uf=o;Pt@cn#d^rh<65_{|FU1i(k&O zx6r!zs=!m5?8zU;v!#$P)Ucr`57VfT4Q(aZ$SL~ zsAvvL3Qy~nr)~QC89e2nZtS^-@$60G{0EZb_(i>eL~;6ENs(4Z+icb=!c&pfdjxO7 z5w8uu=%;?M7tRTIt_LaQk^k^U@~eJFN(rrLY|0~4_fx1E>X#iltBI)*Ep8I@e=o_- z%KPzjapw0yn1?^%^}=H-IyWhD_OU_#m*MOv-HfLWw%#jvcCVrFp?XbCdBb38lamAf zAAafgoHd4lfH}}G*wQ?AaMIZEi3C1%$18x!F1HtI`~sD#ZSX@NR_WZ4u@3fiZP{|iRt_$ zn2x_wr7Mkf5COk{3sDW_HJA>m3cA+m5>$s&1uT`rQCr2AVNws%A(ekUriw4eRPIfF z)BONH{s&b7EB#desp2;~{&yNOi#yZNN_GdPj#`b$zZ=sbm4A;Mj=xjYcP*xL_hLGv z^6!(w@pl@AKctRU!u6O6+JNb(t?HPKm@2phQ~Jj-9a8Zp<#0$<&eNEVr!l49is`7W zn&B@6%$vsqk7dgY1YXAKn?0wnPQN)N824ZPs8r2&V#>4&(;=0=+v$6#j@qhdpa$K5%#gka+6M}B-67XAoyo+d!Dq~xx?VNUS@h75j#GmT?L>GTLss^2n zswb0Ne6ovAb@4r1eD5H82jM^;2l}HLqH|GIV6f96Xnp*VE3DpU69nW)o zy7LQA{sjshFOkDhq8Y1jsngl0PJAJ%7GA1Ir>ejLe4VJu=^|9=7dw9mss>!|bh(Sa z3FTj4B|nt^7N?cB6HtcL4y-|SNLBE?j;})%^?>6KIljU1M^N>|W*7fBs&bz~b;4)R zdgwM(4SOAB(pCm`6X0LqJ$|TwkDPvnD&v=^TK+An0)KS;7pDgu{~cAqb*Z)T)kl@S zA*%exq7Bj3j<@qUvSA?BD@miUtm5zRPig(2Ivx0 zC%zu-gg)W?r%_epMO6Fm9<zc9VpJ>LK z|G)dAQdMcf57nlr9H*+PUN5CXs`wVpudV8%R&YhNcJY6wDzA-;m#VxI>M(Ouz=;59 zCzn8~U>E2Azfle18RXLm&UE>us=!%J^|~wh=hQJ*pAkG(%Qw)?u_RQ7RAr<%p6a+% zyu0J+j@MRAynZgezl)dZJOi9BRXG{4PRIl_0*rMD{=ZX}k7_dB)ktp((;-zb$N5sV zV6xL(RHHT()uvF09)n(j>iF+eN&LAdJrZyk7VpK_&IDeV* zZ$OpqMn(Qb#f^)vT<#J`6bO+FJNTiNuR&GdTBrA-nuiZM{~=U|ROLK^ z3T?E!G8Xt>sS19AjB5Flwg6w%W83gmk?p8Xw8QzYI(^OQ>n{FnG^ES-yDnlUsza*a zdrtSDD)>WGC;r&+Pf;DURVV(^rQ44x-&ZdE0aQKq6RN{k&i@}kC-{W~(%)P{sR}yi z^pMjU7cbTAQyrcCFRFZX9hb@vJ722v{|ET1uXDt?j1iYns)8CgUn;*L>R+i)E$6l_ z{=ZSX33hM^Ypce(6Z~X!h>Mr%MCUnQsxBSre5rbFG^%uC9RIJC_A6nQ%Q((u_&Zg) z@h<*9m9ao=RRO<{Q1@cmgyjddj{cXb^dZu#2kN@~VV7U3_YMr7Ew5^J}S{1Ol2o$D`_{6I_ONE`wABw0FK#eh24E z<)7$$sqSJ4r(>OtbLpijXuR{KI`KqQ`Ewkvtx7-H@yYrCK*K-Hf!eBs`Ho9f z;0#n-#B5aYb6mVs{)MR0&2{{L(n=*z#xj>ds(ZmI=hs%HTj| z54wnlP>tONm*Brs75J!2S6h{TljHxLD*qOxi+2I_z>@$KuoZ1&)}_1afMydwXqCc6i{DV*TSYUPsso$8#&yL7ExI;pt+I$o7M!Evelc23(n?cm~TX{En0 zcX1I?WjxjSQU%X+{4B?7s~Vr|`2R+AA?QhZRlIjS+OKZy=T6Yy74&ziiVbk_QWcQl z{MxE?=Q=J`y1~wuDn8TsnU%2ss^D;bD8op{rP?aSqso}=;wPe-cLk_gG#k|+RgaW8 zU#fiN&abVScZrv}h}x&$ledgX5oXS-50s&HVE%i$5Ri zw=P;DA30z8&$levm;d>e<)3d^{`r>WpKn?Gjq#svS^oK!<)3d^^lghCMgIAg;Gb_<{%?KD(xmpcE9&w~HzxE7&TiQA zqM;XOp8v^!7nW6h(JlVRs~bJpcg&QR4rZ)wKlng!$#vt7Oa6WS3zy7Gs7h_PY;1Yt znI}Kr-eJ}q(|=ff#bnMQ)lHxUr!x=&x?NyIJ5oOFFxr#DR=XNR#zWC;nCI~WKG(# zAnwmi=k(fq<(+-afuX_p%8M^}wocx{^9FZa|9SQ&z1C%2F(NJF zyeqH$X=#Hgw-9NNLHaZl)VA$6w&mOq$liQb{k)8f-pLeNEF!0AQe{eB7Jytce zZnIw!mmQb!#+P3-9=3M)gs$IqT{*bz{X3ueddIQ9p32~-8)WryZ_p6HpP|C8+F3g z$p!n~Uw8So?l+F_kac%_sN2f2%*o3h-F@Aa;kRCYBQs|}-8Jhze4*Xl554x`V{2iS74JszafAmvvdex+6cfe0x71~d4Twl zfYs*#(##J6+XO}q1*DtXhXUq~0)&SFdYECu0G&ny9u??ig69Kv3FMs*=wsFkRE+^N z9}ehea)twvvjEQv3@}Yb0QL#Y83D*JTLqSn1)MYzFv!dr2^csI@Qy&HX*UXRP+-9* zzd+=ZppnGxJ9S#$*FN7Z`53jse6?0NgMJFw*Q5*d)*|3ozO&&Em(jiGW`O zvP`eBfcQy()nftU%nt(F1V)YnWSiT^0p{ia!s7uG&9L!+PLlzT3gnpen)bT{noj`a znw$xMs$9VH0(qv%L_qQsz?_MIsb;IdK7o@a0rJhPNr2^ffOiCDn07gUffoQ4|5^2Dl*?P-^xH#N`9}O##d?OQ!%f3H%~(k?EBO zm^K}-IuB50egFi^O~wVt#pZU&CFYQ%!VH^=Tx!-zDot=2SvwVyHE$YO=bQBcy9Ao& z11>i?`GBe-!1DqNOq1z=42-uRzR@IB+Nh-npu*o&34Hm)2;xy#*|4Gn>P@1 za28q5(e`wmSuqo^rUY=9ttGhBWXuALDFv*X1@O!vfw=M8YW^D;zlR%?VfH7I6 zfN668)dDL_WHuoFLO|hcz)G`OV4FbeIe=9re-2>oMSvXwx0-|t0iEUoDlP=vZng{T z5=guVaHlD|2vAi9*dwsobe;=HE(a{03%JMZ7T70{UItie7M1~)Uko@PaGyyj2MoLf zu&Nxe&g>UBD3EzE;6bzEV!)aTz+r*)CgT#om`efcE&*&XhXmp(0ofIRN6p#_z$Sr4 zmjX7LtV;pY<^ie&Hk(K#AbvieuoCdN*(|V4p!GaJwaK3cn0pyuhrm-NVLqVK<$#L$ zfURb`z%GHr%K*=svdaKfR{-`1Ja0N*4oF@ASbRC)MYCIApFsK*fbC}C6@cYe0uBhg zY*H2g23`eNwE(cg>=!sFka;EGHM8PMz?v$+VSzVH##MkZ3jyn{0=#7o3B+9u$gTps zW7bvyHVHIZ2-s<|76PU%0#pm^Hj%3V@z($fuLittHVbSMXuSxq$K)>p%v}uFA@Gq& zxCYSaT0q4$fW2nBz%GHr#eh#u*i7GDea((D%4Cy;&}V82;- z9boxVzyX1;P0A9$z-53{O8^JVeu0AmnM(oRnH5U`YdpYVfgeo9GQgPY0qd3lelmvy z;%)$Bdw^feS`V;Epwab!-%Qr^fN3`Zss#?2$PIva11P)!@GrAjV4Fbe8v%z+{*8dS z%KIYLq? zIMekeBw`jy8koJ3h9+eta+FyrX=L_GjyAn+MvgHnB#q4vk|ri&6>_Y(UDDJXk~A~J zZb6!xwUQPlcq`JlaU0|0$;+=pFrtD5Y)oQ>Vfs;(=Q^|4d`qZt_Cc>2XH{3 zi%Gc~FmMfE)!l$ZvtQt#K;}Jw)6I%|0BhC)4hwWO8EXJz?ggw{131eZ5{SDGki8ag zj#;}Fut}iNy?`W>buVDr{eWtL6cf1*5Wfykcpo6mY!=ui(E5Hry2-yEF!uq#4uKvf zVI82;gMf;4fL><1z%GHr2LOFc*#m&8hX8v7`kBrT0+QDQ7C#6WV0H`a6G(pukYN@+ z1X%ts;DEp&ld>K#a06h~dO)Vx4+su1y&gu+GbGSor+CV zC&z3T*d>s-8IWtrHUp|22ka5ZGo7~plAi!9-U66vb_?tiNPirVZx%idSY8b{ATYzE zJOLQ^Bw*DOfI_oh;GjTeHK5q6s0OTg3UF9pmdSV$Fy?8%x+ejp=8!<#RzUVsfH`LE zQ-DnZjh+TvWU`(HOnU}UEl_46TLJOU0t&YRE;gG5wh6R;22f%0p8?E$4zNR@(j+_! z==40G;#t6avt3}9K;m;%N`1{CfDJZ?4%Y!hg` z3s7zHcLC@fQU4hm#`2zbq`_zW58j7 zH%!JyfH8Xk>plX!Wey3%eFDh-81Rl+`!QgXK%>2YohEB9VA`jEYJuG*@(CdRGeF@d zfcMR2fo%eE`h|)0iT+(&jD3m0`>@e zZaRMfNZton`~~1kvs+-FK>C+}{bu2pfaUuE2L!%0Df<8ezXGh<2RLB%3mg>4+zto<6WNubd;fZt5kH-Ks10;&ZL znaBY^{C9xD1Au>-%>vs5T7L^TZ1TSa%>5p)L*P%7@ExGj4}gmAbng%{FMY=@y-OtV zdq|y-DgPc)^&?~tME4IrkSX~mz~UbO_04X9eFEt}0wQMNkAUSr0}co@G$}s;2L1wA z^%J0x*)MQVAoFLyF=oZjfHl7Y4hu9f8NUF={03O}3!tevBoKEHko_y5xmo)wV3RY!=ui(E1RdwaGsOnENll4uQ5Np$5?DcR)oA zpq<$+uuCBEUw{s#>|cPY!+<>kCz;N_1Csv$EdCwP(d-u3Cy;&^(Ag|J3|Rgr;DA6E zlk!J!^bG?;bjKg~iDtk2gZP<$;-79-{7Hs2LBL^wt~X?a=!{UP(GBZD++y5t2sf?{ z(b+-#bIjTxV3Rzv>j8EM z^e_owK&SeEiZGy;*)Fh4Ah8~xk14AMsEPyZ5$I<+*9Rm=0E_Db2AJIf`vlVC02yXs z9AJ3^zyX0lCM5zG*buNP0?0J`1r7>iHUOMwRx|*tISO!CV3^5h2pH1{u&yCsxH%*c zcQhdTD8NXw_9(z6fkur0qfJ&Lz_eok)dE>2ax@^mF`)2hz&Nv6V4FbeV*uGE{}{mB zCV(9R6HP*6K&N8?6^#KoX1l;Hfy5?&TvOHrP}LN$M<)-X-Kvf&S9)Sg>b8A3yTfpMhfUC@I zfqeq$Z2$|+!Zv{ACjbrzEHWu=0R!6sR<#8zHv0t*3S^!DxX!FN0kEb$;IP0_lhF<^ zrUPJIJAh{n3B;WU$Zij~!K`f$*d)-X1HhQ94uEMV0jdR7n8=BM_>%#JCjwTQ%>vs5 zTAu`1W%5q~%&OU*MoXW*5MNW^<`0-iNxX9B9u2J8`d z-gG_-kbDkc@mYWu&2E8x0_kT1wwr}#1D1CK91wWfq?`j7$p0W>R-FUbVfG6g6v*rb zc+ITn23V5}I4tmn;q7|#M+#tF65uU!NFXj1kev*8$E-~TY!Yac0@!J?QUKG^0M!D! zO(Yc%-yKkx3V7da7T6}xIt{SL313EPfb}m zpsEL8kHF`qGyj#LN$v?)%>RO@n-76~0_i;f`^~~0faSda2L!%0DLnxLdjnSW1ROB? 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 elaK$bo0p diff --git a/config/config.example.toml b/config/config.example.toml index 8c8e92bd..603eb05a 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -7,9 +7,16 @@ database = "lysand" [redis.queue] host = "localhost" -post = 6379 +port = 6379 password = "" -# database = 0 +database = 0 + +[redis.cache] +host = "localhost" +port = 6379 +password = "" +database = 1 +enabled = false [http] base_url = "https://lysand.social" diff --git a/index.ts b/index.ts index ce7ababe..38a9de04 100644 --- a/index.ts +++ b/index.ts @@ -11,6 +11,7 @@ import { mkdir } from "fs/promises"; import { client } from "~database/datasource"; import type { PrismaClientInitializationError } from "@prisma/client/runtime/library"; import { HookTypes, Server } from "~plugins/types"; +import { initializeRedisCache } from "@redis"; const timeAtStart = performance.now(); const server = new Server(); @@ -33,6 +34,12 @@ if (!(await requests_log.exists())) { await Bun.write(process.cwd() + "/logs/requests.log", ""); } +const redisCache = await initializeRedisCache(); + +if (redisCache) { + client.$use(redisCache); +} + // Check if database is reachable let postCount = 0; try { diff --git a/package.json b/package.json index 90da5362..224545b0 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "@microsoft/eslint-formatter-sarif": "^3.0.0", "@types/cli-table": "^0.3.4", "@types/html-to-text": "^9.0.4", + "@types/ioredis": "^5.0.0", "@types/jsonld": "^1.5.13", "@typescript-eslint/eslint-plugin": "^6.13.1", "@typescript-eslint/parser": "^6.13.1", @@ -78,12 +79,14 @@ "cli-table": "^0.3.11", "eventemitter3": "^5.0.1", "html-to-text": "^9.0.5", + "ioredis": "^5.3.2", "ip-matching": "^2.1.2", "iso-639-1": "^3.1.0", "isomorphic-dompurify": "^1.10.0", "jsonld": "^8.3.1", "marked": "^9.1.2", "prisma": "^5.6.0", + "prisma-redis-middleware": "^4.8.0", "semver": "^7.5.4", "sharp": "^0.33.0-rc.2" } diff --git a/utils/config.ts b/utils/config.ts index eddec0b6..92553c16 100644 --- a/utils/config.ts +++ b/utils/config.ts @@ -16,6 +16,13 @@ export interface ConfigType { password: string; database: number | null; }; + cache: { + host: string; + port: number; + password: string; + database: number | null; + enabled: boolean; + }; }; http: { @@ -159,7 +166,14 @@ export const configDefaults: ConfigType = { host: "localhost", port: 6379, password: "", - database: null, + database: 0, + }, + cache: { + host: "localhost", + port: 6379, + password: "", + database: 1, + enabled: false, }, }, instance: { diff --git a/utils/redis.ts b/utils/redis.ts new file mode 100644 index 00000000..a5715bc1 --- /dev/null +++ b/utils/redis.ts @@ -0,0 +1,60 @@ +import { getConfig } from "@config"; +import type { Prisma } from "@prisma/client"; +import chalk from "chalk"; +import Redis from "ioredis"; +import { createPrismaRedisCache } from "prisma-redis-middleware"; + +const config = getConfig(); + +const cacheRedis = config.redis.cache.enabled + ? new Redis({ + host: config.redis.cache.host, + port: Number(config.redis.cache.port), + password: config.redis.cache.password, + db: Number(config.redis.cache.database ?? 0), + }) + : null; + +cacheRedis?.on("error", e => { + console.log(e); +}); + +export { cacheRedis }; + +export const initializeRedisCache = async () => { + if (cacheRedis) { + // Test connection + try { + await cacheRedis.ping(); + } catch (e) { + console.error( + `${chalk.red(`✗`)} ${chalk.bold( + `Error while connecting to Redis` + )}` + ); + throw e; + } + + console.log(`${chalk.green(`✓`)} ${chalk.bold(`Connected to Redis`)}`); + + const cacheMiddleware: Prisma.Middleware = createPrismaRedisCache({ + storage: { + type: "redis", + options: { + client: cacheRedis, + invalidation: { + referencesTTL: 300, + }, + }, + }, + cacheTime: 300, + onError: e => { + console.error(e); + }, + }); + + return cacheMiddleware; + } + + return null; +}; From d7398e1f5fc9cbff9d07a5a3509fba0fb770c395 Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Fri, 1 Dec 2023 13:04:29 -1000 Subject: [PATCH 13/13] chore: Update Lockfile --- bun.lockb | Bin 345808 -> 345808 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/bun.lockb b/bun.lockb index 2cfe188606c5449d2ac4dc6e86915b3146d539f4..ae9680ac77bc0a147c4a52dc86b8a85ddf415c7e 100755 GIT binary patch delta 33 ocmcc6D|(?SS5A7XY&W3F-g<