diff --git a/api/assets/schemas.json b/api/assets/schemas.json index 03a2db07..5d78c602 100644 --- a/api/assets/schemas.json +++ b/api/assets/schemas.json @@ -2887,6 +2887,329 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, + "EmojiListResponse": { + "type": "array", + "items": { + "type": "object", + "properties": { + "animated": { + "type": "boolean" + }, + "available": { + "type": "boolean" + }, + "id": { + "type": "string" + }, + "managed": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "require_colons": { + "type": "boolean" + }, + "guild_id": { + "type": "string" + }, + "roles": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "animated", + "available", + "id", + "managed", + "name", + "require_colons" + ] + }, + "definitions": { + "ChannelPermissionOverwriteType": { + "enum": [ + 0, + 1 + ], + "type": "number" + }, + "Embed": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "type": { + "enum": [ + "article", + "gifv", + "image", + "link", + "rich", + "video" + ], + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "color": { + "type": "integer" + }, + "footer": { + "type": "object", + "properties": { + "text": { + "type": "string" + }, + "icon_url": { + "type": "string" + }, + "proxy_icon_url": { + "type": "string" + } + }, + "required": [ + "text" + ] + }, + "image": { + "$ref": "#/definitions/EmbedImage" + }, + "thumbnail": { + "$ref": "#/definitions/EmbedImage" + }, + "video": { + "$ref": "#/definitions/EmbedImage" + }, + "provider": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string" + } + } + }, + "author": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string" + }, + "icon_url": { + "type": "string" + }, + "proxy_icon_url": { + "type": "string" + } + } + }, + "fields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + }, + "inline": { + "type": "boolean" + } + }, + "required": [ + "name", + "value" + ] + } + } + } + }, + "EmbedImage": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "proxy_url": { + "type": "string" + }, + "height": { + "type": "integer" + }, + "width": { + "type": "integer" + } + } + }, + "ChannelModifySchema": { + "type": "object", + "properties": { + "name": { + "maxLength": 100, + "type": "string" + }, + "type": { + "enum": [ + 0, + 1, + 10, + 11, + 12, + 13, + 2, + 3, + 4, + 5, + 6 + ], + "type": "number" + }, + "topic": { + "type": "string" + }, + "icon": { + "type": [ + "null", + "string" + ] + }, + "bitrate": { + "type": "integer" + }, + "user_limit": { + "type": "integer" + }, + "rate_limit_per_user": { + "type": "integer" + }, + "position": { + "type": "integer" + }, + "permission_overwrites": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/ChannelPermissionOverwriteType" + }, + "allow": { + "type": "string" + }, + "deny": { + "type": "string" + } + }, + "required": [ + "allow", + "deny", + "id", + "type" + ] + } + }, + "parent_id": { + "type": "string" + }, + "id": { + "type": "string" + }, + "nsfw": { + "type": "boolean" + }, + "rtc_region": { + "type": "string" + }, + "default_auto_archive_duration": { + "type": "integer" + } + } + }, + "UserPublic": { + "type": "object", + "properties": { + "username": { + "type": "string" + }, + "discriminator": { + "type": "string" + }, + "id": { + "type": "string" + }, + "public_flags": { + "type": "integer" + }, + "avatar": { + "type": "string" + }, + "accent_color": { + "type": "integer" + }, + "banner": { + "type": "string" + }, + "bio": { + "type": "string" + }, + "bot": { + "type": "boolean" + } + }, + "required": [ + "bio", + "bot", + "discriminator", + "id", + "public_flags", + "username" + ] + }, + "PublicConnectedAccount": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string" + }, + "verifie": { + "type": "boolean" + } + }, + "required": [ + "name", + "type", + "verifie" + ] + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" + }, "GuildCreateSchema": { "type": "object", "properties": { diff --git a/api/package.json b/api/package.json index 8fc55e94..4fee791c 100644 --- a/api/package.json +++ b/api/package.json @@ -6,6 +6,7 @@ "types": "dist/Server.d.ts", "scripts": { "test:only": "jest --coverage --verbose --forceExit ./tests", + "test:routes": "jest --coverage --verbose --forceExit ./routes.test.ts", "test": "npm run build && npm run test:only", "test:watch": "jest --watch", "start": "npm run build && node dist/start", diff --git a/api/src/Server.ts b/api/src/Server.ts index 4a226d12..a16a61f8 100644 --- a/api/src/Server.ts +++ b/api/src/Server.ts @@ -1,4 +1,4 @@ -import { OptionsJson } from 'body-parser'; +import { OptionsJson } from "body-parser"; import "missing-native-js-functions"; import { Connection } from "mongoose"; import { Server, ServerOptions } from "lambert-server"; @@ -38,7 +38,6 @@ export class FosscordServer extends Server { await Config.init(); await initEvent(); - /* DOCUMENTATION: uses LOG_REQUESTS environment variable @@ -51,14 +50,16 @@ export class FosscordServer extends Server { */ let logRequests = process.env["LOG_REQUESTS"] != undefined; - if(logRequests) { - this.app.use(morgan("combined", { - skip: (req, res) => { - var skip = !(process.env["LOG_REQUESTS"]?.includes(res.statusCode.toString()) ?? false); - if(process.env["LOG_REQUESTS"]?.charAt(0) == '-') skip = !skip; - return skip; - } - })); + if (logRequests) { + this.app.use( + morgan("combined", { + skip: (req, res) => { + var skip = !(process.env["LOG_REQUESTS"]?.includes(res.statusCode.toString()) ?? false); + if (process.env["LOG_REQUESTS"]?.charAt(0) == "-") skip = !skip; + return skip; + } + }) + ); } this.app.use(CORS); @@ -90,8 +91,10 @@ export class FosscordServer extends Server { this.app.use(ErrorHandler); TestClient(this.app); - if(logRequests){ - console.log("Warning: Request logging is enabled! This will spam your console!\nTo disable this, unset the 'LOG_REQUESTS' environment variable!"); + if (logRequests) { + console.log( + "Warning: Request logging is enabled! This will spam your console!\nTo disable this, unset the 'LOG_REQUESTS' environment variable!" + ); } return super.start(); } diff --git a/api/tests/routes.test.ts b/api/tests/routes.test.ts index fcaa3124..ed391dfb 100644 --- a/api/tests/routes.test.ts +++ b/api/tests/routes.test.ts @@ -7,7 +7,7 @@ import fs from "fs"; import Ajv from "ajv"; import addFormats from "ajv-formats"; import fetch from "node-fetch"; -import { Event, User, events } from "@fosscord/util"; +import { Event, User, events, Guild, Channel } from "@fosscord/util"; const SchemaPath = join(__dirname, "..", "assets", "schemas.json"); const schemas = JSON.parse(fs.readFileSync(SchemaPath, { encoding: "utf8" })); @@ -25,11 +25,36 @@ addFormats(ajv); var token: string; var user: User; +var guild: Guild; +var channel: Channel; + +const request = async (path: string, opts: any = {}): Promise => { + const response = await fetch(`http://localhost:3001/api${path}`, { + ...opts, + method: opts.method || opts.body ? "POST" : "GET", + body: opts.body && JSON.stringify(opts.body), + headers: { + authorization: token, + ...(opts.body ? { "content-type": "application/json" } : {}), + ...(opts.header || {}) + } + }); + if (response.status === 204) return; + + var data = await response.text(); + try { + data = JSON.stringify(data); + if (response.status >= 400) throw data; + return data; + } catch (error) { + throw data; + } +}; + beforeAll(async (done) => { try { - const response = await fetch("http://localhost:3001/api/auth/register", { - method: "POST", - body: JSON.stringify({ + const response = await request("/auth/register", { + body: { fingerprint: "805826570869932034.wR8vi8lGlFBJerErO9LG5NViJFw", email: "test@example.com", username: "tester", @@ -39,18 +64,13 @@ beforeAll(async (done) => { date_of_birth: "2000-01-01", gift_code_sku_id: null, captcha_key: null - }), - headers: { - "content-type": "application/json" } }); - const json = await response.json(); - token = json.token; - user = await ( - await fetch(`http://localhost:3001/api/users/@me`, { - headers: { authorization: token } - }) - ).json(); + token = response.token; + user = await request(`/users/@me`); + const { id: guild_id } = await request("/guilds", { body: { name: "test server" } }); + guild = await request(`/guilds/${guild_id}`); + channel = (await request(`/guilds/${guild_id}/channels`))[0]; done(); } catch (error) { @@ -75,7 +95,8 @@ describe("Automatic unit tests with route description middleware", () => { console.log(`${(route as any).file}\nrouter.${method} is missing the test property`); return done(); } - const urlPath = path.replace(":id", user.id) || route.test?.path; + const urlPath = + path.replace(":id", user.id).replace(":guild_id", guild.id).replace(":channel_id", channel.id) || route.test?.path; var validate: any; if (route.test.body) { validate = ajv.getSchema(route.test.body); diff --git a/bundle/.gitignore b/bundle/.gitignore index 7d2cb508..cf073d9c 100644 --- a/bundle/.gitignore +++ b/bundle/.gitignore @@ -1 +1,2 @@ -files/ \ No newline at end of file +files/ +.env \ No newline at end of file diff --git a/bundle/package-lock.json b/bundle/package-lock.json index aaa0c1ee..090935f4 100644 --- a/bundle/package-lock.json +++ b/bundle/package-lock.json @@ -15,6 +15,7 @@ "@fosscord/gateway": "file:../gateway", "@fosscord/util": "file:../util", "async-exit-hook": "^2.0.1", + "dotenv": "^10.0.0", "express": "^4.17.1", "missing-native-js-functions": "^1.2.15", "node-os-utils": "^1.3.5", @@ -195,6 +196,9 @@ "ts-node-dev": "^1.1.6", "ts-patch": "^1.4.4", "typescript": "^4.2.3" + }, + "optionalDependencies": { + "@yukikaze-bot/erlpack": "^1.0.1" } }, "../util": { @@ -858,6 +862,14 @@ "node": ">=0.3.1" } }, + "node_modules/dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "engines": { + "node": ">=10" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -1810,6 +1822,7 @@ "@types/node-fetch": "^2.5.12", "@types/uuid": "^8.3.0", "@types/ws": "^7.4.0", + "@yukikaze-bot/erlpack": "^1.0.1", "@zerollup/ts-transform-paths": "^1.7.18", "ajv": "^8.5.0", "amqplib": "^0.8.0", @@ -2336,6 +2349,11 @@ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true }, + "dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==" + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", diff --git a/bundle/package.json b/bundle/package.json index fa6fe669..f68ff0bd 100644 --- a/bundle/package.json +++ b/bundle/package.json @@ -54,6 +54,7 @@ "@fosscord/gateway": "file:../gateway", "@fosscord/util": "file:../util", "async-exit-hook": "^2.0.1", + "dotenv": "^10.0.0", "express": "^4.17.1", "missing-native-js-functions": "^1.2.15", "node-os-utils": "^1.3.5", diff --git a/bundle/src/start.ts b/bundle/src/start.ts index f68a65bf..be67a122 100644 --- a/bundle/src/start.ts +++ b/bundle/src/start.ts @@ -2,6 +2,8 @@ import cluster from "cluster"; import os from "os"; import { initStats } from "./stats"; +import { config } from "dotenv"; +config(); // TODO: add tcp socket event transmission const cores = 1 || Number(process.env.threads) || os.cpus().length; diff --git a/bundle/src/stats.ts b/bundle/src/stats.ts index e6941db2..d5ceeff7 100644 --- a/bundle/src/stats.ts +++ b/bundle/src/stats.ts @@ -5,7 +5,6 @@ export function initStats() { console.log(`[Path] running in ${__dirname}`); console.log(`[CPU] ${osu.cpu.model()} Cores x${osu.cpu.count()}`); console.log(`[System] ${os.platform()} ${os.arch()}`); - console.log(`[Database] started`); console.log(`[Process] running with pid: ${process.pid}`); setInterval(async () => { diff --git a/dashboard/README.md b/dashboard/README.md index c94ae96c..df1157a1 100644 --- a/dashboard/README.md +++ b/dashboard/README.md @@ -1,8 +1,12 @@ # Fosscord-dashboard + A admin dashboard for fosscord-server ## (planned) Features -- [ ] Overview usage (registered users, concurrent connections, voice usage, reports) -- [ ] Reports -- [ ] Member managment (edit (disable, delete, premium, username, discriminator)) -- [ ] Configuration ([Config.ts](https://github.com/fosscord/fosscord-server-util/blob/master/src/util/Config.ts)) + +- [ ] Overview usage (registered users, concurrent connections, voice usage, reports) +- [ ] Reports +- [ ] Member managment (edit (disable, delete, premium, username, discriminator)) +- [ ] Configuration ([Config.ts](https://github.com/fosscord/fosscord-server-util/blob/master/src/util/Config.ts)) + +port = 3005 diff --git a/util/src/util/Database.ts b/util/src/util/Database.ts index ab7c70c5..369a403c 100644 --- a/util/src/util/Database.ts +++ b/util/src/util/Database.ts @@ -1,6 +1,6 @@ import path from "path"; import "reflect-metadata"; -import { Connection, createConnection, ValueTransformer } from "typeorm"; +import { Connection, createConnection } from "typeorm"; import * as Models from "../entities"; // UUID extension option is only supported with postgres @@ -8,18 +8,20 @@ import * as Models from "../entities"; var promise: Promise; var dbConnection: Connection | undefined; +let dbConnectionString = process.env.DATABASE || path.join(process.cwd(), "database.db"); export function initDatabase() { if (promise) return promise; // prevent initalizing multiple times - console.log("[Database] connecting ..."); + const type = dbConnectionString.includes(":") ? dbConnectionString.split(":")[0]?.replace("+srv", "") : "sqlite"; + const isSqlite = type.includes("sqlite"); + + console.log(`[Database] connecting to ${type} db`); // @ts-ignore promise = createConnection({ - type: "sqlite", - database: path.join(process.cwd(), "database.db"), - // type: "postgres", - // url: "postgres://fosscord:wb94SmuURM2Syv&@localhost/fosscord", - // + type, + url: isSqlite ? undefined : dbConnectionString, + database: isSqlite ? dbConnectionString : undefined, entities: Object.values(Models).filter((x) => x.constructor.name !== "Object"), synchronize: true, logging: false,