diff --git a/.gitignore b/.gitignore index f028f274..5dd1bb5e 100644 --- a/.gitignore +++ b/.gitignore @@ -173,4 +173,5 @@ uploads/ pages/dist log.txt *.log -build \ No newline at end of file +build +config/extended_description_test.md \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 090ba8f9..15ae06eb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,5 +2,5 @@ "typescript.tsdk": "node_modules/typescript/lib", "jest.jestCommandLine": "/home/jessew/.bun/bin/bun test", "jest.rootPath": ".", - "conventionalCommits.scopes": ["database", "frontend", "build"] + "conventionalCommits.scopes": ["database", "frontend", "build", "api"] } diff --git a/config/config.example.toml b/config/config.example.toml index f123bd8f..0335c88e 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -286,6 +286,9 @@ avatars = [] [instance] name = "Lysand" description = "A test instance of Lysand" +# Path to a file containing a longer description of your instance +# This will be parsed as Markdown +extended_description_path = "" # URL to your instance logo (jpg files should be renamed to jpeg) logo = "" # URL to your instance banner (jpg files should be renamed to jpeg) diff --git a/entrypoint.sh b/entrypoint.sh index f8bf9060..31dcb813 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -13,7 +13,7 @@ cd /app/dist # Parse first argument case "$1" in "start") - NITRO_PORT=5173 bun run ./frontend/server/index.mjs & NODE_ENV=production bun run ./index.js --prod + NODE_ENV=production bun run ./index.js --prod ;; "cli") # Start the CLI diff --git a/index.ts b/index.ts index 4f99267c..b96293e7 100644 --- a/index.ts +++ b/index.ts @@ -1,14 +1,23 @@ -import { dualLogger } from "@loggers"; import { connectMeili } from "@meilisearch"; import { config } from "config-manager"; import { count } from "drizzle-orm"; -import { LogLevel } from "log-manager"; +import { LogLevel, LogManager, type MultiLogManager } from "log-manager"; import { db, setupDatabase } from "~drizzle/db"; import { status } from "~drizzle/schema"; import { createServer } from "~server"; const timeAtStart = performance.now(); +const isEntry = import.meta.path === Bun.main; + +let dualLogger: LogManager | MultiLogManager = new LogManager( + Bun.file("/dev/null"), +); + +if (isEntry) { + dualLogger = (await import("@loggers")).dualLogger; +} + await dualLogger.log(LogLevel.INFO, "Lysand", "Starting Lysand..."); await setupDatabase(dualLogger); diff --git a/packages/config-manager/config.type.ts b/packages/config-manager/config.type.ts index 9f6e7097..4483f10e 100644 --- a/packages/config-manager/config.type.ts +++ b/packages/config-manager/config.type.ts @@ -315,6 +315,9 @@ export interface Config { /** @default "A test instance of Lysand" */ description: string; + /** @default "" */ + extended_description_path: string; + /** @default "" */ logo: string; @@ -589,6 +592,7 @@ export const defaultConfig: Config = { instance: { name: "Lysand", description: "A test instance of Lysand", + extended_description_path: "", logo: "", banner: "", }, diff --git a/server/api/api/v1/instance/extended_description.test.ts b/server/api/api/v1/instance/extended_description.test.ts new file mode 100644 index 00000000..d8c59296 --- /dev/null +++ b/server/api/api/v1/instance/extended_description.test.ts @@ -0,0 +1,23 @@ +import { describe, expect, test } from "bun:test"; +import { meta } from "./extended_description"; +import { sendTestRequest } from "~tests/utils"; +import { config } from "config-manager"; + +// /api/v1/instance/extended_description +describe(meta.route, () => { + test("should return extended description", async () => { + const response = await sendTestRequest( + new Request(new URL(meta.route, config.http.base_url)), + ); + + expect(response.status).toBe(200); + + const json = await response.json(); + expect(json).toEqual({ + updated_at: new Date(2024, 0, 0).toISOString(), + // This is a [Lysand](https://lysand.org) server with the default extended description. + content: + '
This is a Lysand server with the default extended description.
\n', + }); + }); +}); diff --git a/server/api/api/v1/instance/extended_description.ts b/server/api/api/v1/instance/extended_description.ts new file mode 100644 index 00000000..01c063ad --- /dev/null +++ b/server/api/api/v1/instance/extended_description.ts @@ -0,0 +1,47 @@ +import { apiRoute, applyConfig } from "@api"; +import { dualLogger } from "@loggers"; +import { jsonResponse } from "@response"; +import { parse } from "marked"; +import { LogLevel } from "~packages/log-manager"; + +export const meta = applyConfig({ + allowedMethods: ["GET"], + route: "/api/v1/instance/extended_description", + ratelimits: { + max: 300, + duration: 60, + }, + auth: { + required: false, + }, +}); + +export default apiRoute(async (req, matchedRoute, extraData) => { + const config = await extraData.configManager.getConfig(); + + let extended_description = parse( + "This is a [Lysand](https://lysand.org) server with the default extended description.", + ); + let lastModified = new Date(2024, 0, 0); + + const extended_description_file = Bun.file( + config.instance.extended_description_path, + ); + + if (await extended_description_file.exists()) { + extended_description = + (await parse( + (await extended_description_file.text().catch(async (e) => { + await dualLogger.logError(LogLevel.ERROR, "Routes", e); + return ""; + })) || + "This is a [Lysand](https://lysand.org) server with the default extended description.", + )) || ""; + lastModified = new Date(extended_description_file.lastModified); + } + + return jsonResponse({ + updated_at: lastModified.toISOString(), + content: extended_description, + }); +}); diff --git a/server/api/api/v1/instance/rules.test.ts b/server/api/api/v1/instance/rules.test.ts new file mode 100644 index 00000000..cb00e9b1 --- /dev/null +++ b/server/api/api/v1/instance/rules.test.ts @@ -0,0 +1,23 @@ +import { describe, expect, test } from "bun:test"; +import { meta } from "./rules"; +import { sendTestRequest } from "~tests/utils"; +import { config } from "config-manager"; + +// /api/v1/instance/rules +describe(meta.route, () => { + test("should return rules", async () => { + const response = await sendTestRequest( + new Request(new URL(meta.route, config.http.base_url)), + ); + + expect(response.status).toBe(200); + + const json = await response.json(); + expect(json).toEqual( + config.signups.rules.map((rule, index) => ({ + id: String(index), + text: rule, + })), + ); + }); +}); diff --git a/server/api/api/v1/instance/rules.ts b/server/api/api/v1/instance/rules.ts new file mode 100644 index 00000000..a26a31fc --- /dev/null +++ b/server/api/api/v1/instance/rules.ts @@ -0,0 +1,25 @@ +import { apiRoute, applyConfig } from "@api"; +import { jsonResponse } from "@response"; + +export const meta = applyConfig({ + allowedMethods: ["GET"], + route: "/api/v1/instance/rules", + ratelimits: { + max: 300, + duration: 60, + }, + auth: { + required: false, + }, +}); + +export default apiRoute(async (req, matchedRoute, extraData) => { + const config = await extraData.configManager.getConfig(); + + return jsonResponse( + config.signups.rules.map((rule, index) => ({ + id: String(index), + text: rule, + })), + ); +}); diff --git a/tests/utils.ts b/tests/utils.ts index 42571a73..68ab26b8 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -9,7 +9,6 @@ import { import { db } from "~drizzle/db"; import { status, token, user } from "~drizzle/schema"; import { server } from "~index"; - /** * This allows us to send a test request to the server even when it isnt running * CURRENTLY NOT WORKING, NEEDS TO BE FIXED