diff options
author | TheArcaneBrony <myrainbowdash949@gmail.com> | 2022-08-06 02:33:12 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-08-06 02:33:12 +0200 |
commit | 88916865eec7812029ce9a150f6a7646ff09aea3 (patch) | |
tree | b9cf427d3c173cbe4029e5230eda6acc4fd61cf0 /api | |
parent | Merge branch 'master' into 2fa (diff) | |
parent | Merge pull request #804 from MaddyUnderStars/feat/notesTable (diff) | |
download | server-88916865eec7812029ce9a150f6a7646ff09aea3.tar.xz |
Merge branch 'master' into 2fa
Diffstat (limited to 'api')
-rw-r--r-- | api/assets/inline-plugins/autoRegister.js (renamed from api/assets/preload-plugins/autoRegister.js) | 0 | ||||
-rw-r--r-- | api/assets/inline-plugins/fosscord-login.js (renamed from api/assets/preload-plugins/fosscord-login.js) | 0 | ||||
-rw-r--r-- | api/assets/schemas.json | 27 | ||||
-rw-r--r-- | api/client_test/index.html | 1 | ||||
-rw-r--r-- | api/locales/he/auth.json | 20 | ||||
-rw-r--r-- | api/locales/he/common.json | 22 | ||||
-rw-r--r-- | api/package.json | 2 | ||||
-rw-r--r-- | api/src/middlewares/TestClient.ts | 164 | ||||
-rw-r--r-- | api/src/routes/users/@me/index.ts | 1 | ||||
-rw-r--r-- | api/src/routes/users/@me/notes.ts | 43 | ||||
-rw-r--r-- | api/src/util/entities/AssetCacheItem.ts | 3 | ||||
-rw-r--r-- | api/src/util/index.ts | 1 |
12 files changed, 189 insertions, 95 deletions
diff --git a/api/assets/preload-plugins/autoRegister.js b/api/assets/inline-plugins/autoRegister.js index bb0b903d..bb0b903d 100644 --- a/api/assets/preload-plugins/autoRegister.js +++ b/api/assets/inline-plugins/autoRegister.js diff --git a/api/assets/preload-plugins/fosscord-login.js b/api/assets/inline-plugins/fosscord-login.js index 38f82200..38f82200 100644 --- a/api/assets/preload-plugins/fosscord-login.js +++ b/api/assets/inline-plugins/fosscord-login.js diff --git a/api/assets/schemas.json b/api/assets/schemas.json index 8f0f31be..c7aadd98 100644 --- a/api/assets/schemas.json +++ b/api/assets/schemas.json @@ -13828,6 +13828,33 @@ "TotpEnableSchema": { "type": "object", "properties": { + "username": { + "minLength": 1, + "maxLength": 100, + "type": "string" + }, + "discriminator": { + "type": "string" + }, + "avatar": { + "type": [ + "null", + "string" + ] + }, + "bio": { + "maxLength": 1024, + "type": "string" + }, + "accent_color": { + "type": "integer" + }, + "banner": { + "type": [ + "null", + "string" + ] + }, "password": { "type": "string" }, diff --git a/api/client_test/index.html b/api/client_test/index.html index b438b492..7a4fe627 100644 --- a/api/client_test/index.html +++ b/api/client_test/index.html @@ -7,6 +7,7 @@ <link rel="stylesheet" href="/assets/fosscord.css" /> <link id="logincss" rel="stylesheet" href="/assets/fosscord-login.css" /> <link id="customcss" rel="stylesheet" href="/assets/user.css" /> + <!-- inline plugin marker --> <!-- preload plugin marker --> </head> diff --git a/api/locales/he/auth.json b/api/locales/he/auth.json index b7296868..e19547a0 100644 --- a/api/locales/he/auth.json +++ b/api/locales/he/auth.json @@ -1,16 +1,16 @@ { "login": { - "INVALID_LOGIN": "מייל או מספר טלפון לא נמצאים במאגר", - "INVALID_PASSWORD": "סיסמא שגויה", - "ACCOUNT_DISABLED": "משתמש זה חסום / מבוטל" + "INVALID_LOGIN": "E-Mail or Phone not found", + "INVALID_PASSWORD": "Invalid Password", + "ACCOUNT_DISABLED": "This account is disabled" }, "register": { - "REGISTRATION_DISABLED": "לא ניתן לאפשר רישום משתמשים חדשים", - "INVITE_ONLY": "עליך להיות מוזמן בכדי להרשם", - "EMAIL_INVALID": "מייל שגוי", - "EMAIL_ALREADY_REGISTERED": "מייל זה כבר רשום", - "DATE_OF_BIRTH_UNDERAGE": "{{years}} עלייך להיות מעל גיל", - "CONSENT_REQUIRED": ".עליך להסכים לתנאי השירות ולמדיניות הפרטיות", - "USERNAME_TOO_MANY_USERS": "ליותר מדי משתמשים יש שם משתמש זהה, אנא נסה אחר" + "REGISTRATION_DISABLED": "New user registration is disabled", + "INVITE_ONLY": "You must be invited to register", + "EMAIL_INVALID": "Invalid Email", + "EMAIL_ALREADY_REGISTERED": "Email is already registered", + "DATE_OF_BIRTH_UNDERAGE": "You need to be {{years}} years or older", + "CONSENT_REQUIRED": "You must agree to the Terms of Service and Privacy Policy.", + "USERNAME_TOO_MANY_USERS": "Too many users have this username, please try another" } } diff --git a/api/locales/he/common.json b/api/locales/he/common.json index 4101eac4..9e72e941 100644 --- a/api/locales/he/common.json +++ b/api/locales/he/common.json @@ -1,18 +1,18 @@ { "field": { - "BASE_TYPE_REQUIRED": "שדה זה חובה", - "BASE_TYPE_STRING": "שדה זה חייב להיות כטקסט", - "BASE_TYPE_NUMBER": "שדה זה חייב להיות מספר", - "BASE_TYPE_BIGINT": "השדה הזה חייב להיות ביגינט", - "BASE_TYPE_BOOLEAN": "השדה הזה חייב להיות בוליאני", - "BASE_TYPE_CHOICES": "({{types}}) שדה זה חייב להיות אחד מ", - "BASE_TYPE_CLASS": "{{type}} מסוג instance שדה זה חייב להיות", + "BASE_TYPE_REQUIRED": "This field is required", + "BASE_TYPE_STRING": "This field must be a string", + "BASE_TYPE_NUMBER": "This field must be a number", + "BASE_TYPE_BIGINT": "This field must be a bigint", + "BASE_TYPE_BOOLEAN": "This field must be a boolean", + "BASE_TYPE_CHOICES": "This field must be one of ({{types}})", + "BASE_TYPE_CLASS": "This field must be an instance of {{type}}", "BASE_TYPE_OBJECT": "שדה זה חייב להיות אובייקט", "BASE_TYPE_ARRAY": "שדה זה חייב להיות מערך", - "UNKOWN_FIELD": "{{key}} :מפתח לא ידוע", - "BASE_TYPE_CONSTANT": "{{value}} שדה זה חייב להיות", + "UNKOWN_FIELD": "מפתח לא ידוע: {{key}}", + "BASE_TYPE_CONSTANT": "שדה זה להיות {{value}}", "EMAIL_TYPE_INVALID_EMAIL": "כתובת דואר אלקטרוני לא חוקית", - "DATE_TYPE_PARSE": "ISO8601 אמור להיות {{date}} לא ניתן לאתר", - "BASE_TYPE_BAD_LENGTH": "{{length}} האורך חייב להיות בין" + "DATE_TYPE_PARSE": "לא ניתן לנתח {{date}}. צריך להיות ISO8601", + "BASE_TYPE_BAD_LENGTH": "האורך חייב להיות בין {{length}}" } } diff --git a/api/package.json b/api/package.json index 07208d60..58f35697 100644 --- a/api/package.json +++ b/api/package.json @@ -48,7 +48,7 @@ "@types/jsonwebtoken": "^8.5.0", "@types/morgan": "^1.9.3", "@types/multer": "^1.4.5", - "@types/node": "^14.17.9", + "@types/node": "^14.18.22", "@types/node-fetch": "^2.5.5", "@types/supertest": "^2.0.11", "@zerollup/ts-transform-paths": "^1.7.18", diff --git a/api/src/middlewares/TestClient.ts b/api/src/middlewares/TestClient.ts index ecf87681..7292868c 100644 --- a/api/src/middlewares/TestClient.ts +++ b/api/src/middlewares/TestClient.ts @@ -1,54 +1,46 @@ import express, { Request, Response, Application } from "express"; -import fs from "fs"; +import fs, { writeFile } from "fs"; import path from "path"; -import fetch, { Response as FetchResponse } from "node-fetch"; +import fetch, { Response as FetchResponse, Headers } from "node-fetch"; import ProxyAgent from 'proxy-agent'; import { Config } from "@fosscord/util"; +import { AssetCacheItem } from "../util/entities/AssetCacheItem" +import { FileLogger } from "typeorm"; export default function TestClient(app: Application) { const agent = new ProxyAgent(); - const assetCache = new Map<string, { response: FetchResponse; buffer: Buffer }>(); - const indexHTML = fs.readFileSync(path.join(__dirname, "..", "..", "client_test", "index.html"), { encoding: "utf8" }); - - var html = indexHTML; - const CDN_ENDPOINT = (Config.get().cdn.endpointClient || Config.get()?.cdn.endpointPublic || process.env.CDN || "").replace( - /(https?)?(:\/\/?)/g, - "" - ); - const GATEWAY_ENDPOINT = Config.get().gateway.endpointClient || Config.get()?.gateway.endpointPublic || process.env.GATEWAY || ""; + + //build client page + let html = fs.readFileSync(path.join(__dirname, "..", "..", "client_test", "index.html"), { encoding: "utf8" }); + html = applyEnv(html); + html = applyInlinePlugins(html); + html = applyPlugins(html); + html = applyPreloadPlugins(html); - if (CDN_ENDPOINT) { - html = html.replace(/CDN_HOST: .+/, `CDN_HOST: \`${CDN_ENDPOINT}\`,`); + //load asset cache + let newAssetCache: Map<string, AssetCacheItem> = new Map<string, AssetCacheItem>(); + if(!fs.existsSync(path.join(__dirname, "..", "..", "assets", "cache"))) { + fs.mkdirSync(path.join(__dirname, "..", "..", "assets", "cache")); } - if (GATEWAY_ENDPOINT) { - html = html.replace(/GATEWAY_ENDPOINT: .+/, `GATEWAY_ENDPOINT: \`${GATEWAY_ENDPOINT}\`,`); + if(fs.existsSync(path.join(__dirname, "..", "..", "assets", "cache", "index.json"))) { + let rawdata = fs.readFileSync(path.join(__dirname, "..", "..", "assets", "cache", "index.json")); + newAssetCache = new Map<string, AssetCacheItem>(Object.entries(JSON.parse(rawdata.toString()))); } - // inline plugins - var files = fs.readdirSync(path.join(__dirname, "..", "..", "assets", "preload-plugins")); - var plugins = ""; - files.forEach(x =>{if(x.endsWith(".js")) plugins += `<script>${fs.readFileSync(path.join(__dirname, "..", "..", "assets", "preload-plugins", x))}</script>\n`; }); - html = html.replaceAll("<!-- preload plugin marker -->", plugins); - // plugins - files = fs.readdirSync(path.join(__dirname, "..", "..", "assets", "plugins")); - plugins = ""; - files.forEach(x =>{if(x.endsWith(".js")) plugins += `<script src='/assets/plugins/${x}'></script>\n`; }); - html = html.replaceAll("<!-- plugin marker -->", plugins); - //preload plugins - files = fs.readdirSync(path.join(__dirname, "..", "..", "assets", "preload-plugins")); - plugins = ""; - files.forEach(x =>{if(x.endsWith(".js")) plugins += `<script>${fs.readFileSync(path.join(__dirname, "..", "..", "assets", "preload-plugins", x))}</script>\n`; }); - html = html.replaceAll("<!-- preload plugin marker -->", plugins); - - - app.use("/assets", express.static(path.join(__dirname, "..", "..", "assets"))); - + app.use("/assets", express.static(path.join(__dirname, "..", "..", "assets"))); app.get("/assets/:file", async (req: Request, res: Response) => { delete req.headers.host; - var response: FetchResponse; - var buffer: Buffer; - const cache = assetCache.get(req.params.file); - if (!cache) { + let response: FetchResponse; + let buffer: Buffer; + let assetCacheItem: AssetCacheItem = new AssetCacheItem(req.params.file); + if(newAssetCache.has(req.params.file)){ + assetCacheItem = newAssetCache.get(req.params.file)!; + assetCacheItem.Headers.forEach((value: any, name: any) => { + res.set(name, value); + }); + } + else { + console.log(`CACHE MISS! Asset file: ${req.params.file}`); response = await fetch(`https://discord.com/assets/${req.params.file}`, { agent, // @ts-ignore @@ -56,34 +48,24 @@ export default function TestClient(app: Application) { ...req.headers } }); - buffer = await response.buffer(); - } else { - response = cache.response; - buffer = cache.buffer; + + //set cache info + assetCacheItem.Headers = Object.fromEntries(stripHeaders(response.headers)); + assetCacheItem.FilePath = path.join(__dirname, "..", "..", "assets", "cache", req.params.file); + assetCacheItem.Key = req.params.file; + //add to cache and save + newAssetCache.set(req.params.file, assetCacheItem); + fs.writeFileSync(path.join(__dirname, "..", "..", "assets", "cache", "index.json"), JSON.stringify(Object.fromEntries(newAssetCache), null, 4)); + //download file + fs.writeFileSync(assetCacheItem.FilePath, await response.buffer()); } - - response.headers.forEach((value, name) => { - if ( - [ - "content-length", - "content-security-policy", - "strict-transport-security", - "set-cookie", - "transfer-encoding", - "expect-ct", - "access-control-allow-origin", - "content-encoding" - ].includes(name.toLowerCase()) - ) { - return; - } + + assetCacheItem.Headers.forEach((value: string, name: string) => { res.set(name, value); }); - assetCache.set(req.params.file, { buffer, response }); - - return res.send(buffer); + return res.send(fs.readFileSync(assetCacheItem.FilePath)); }); - app.get("/developers*", (req: Request, res: Response) => { + app.get("/developers*", (_req: Request, res: Response) => { const { useTestClient } = Config.get().client; res.set("Cache-Control", "public, max-age=" + 60 * 60 * 24); res.set("content-type", "text/html"); @@ -104,4 +86,62 @@ export default function TestClient(app: Application) { res.send(html); }); + + +} + +function applyEnv(html: string): string { + const CDN_ENDPOINT = (Config.get().cdn.endpointClient || Config.get()?.cdn.endpointPublic || process.env.CDN || "").replace( + /(https?)?(:\/\/?)/g, + "" + ); + const GATEWAY_ENDPOINT = Config.get().gateway.endpointClient || Config.get()?.gateway.endpointPublic || process.env.GATEWAY || ""; + + if (CDN_ENDPOINT) { + html = html.replace(/CDN_HOST: .+/, `CDN_HOST: \`${CDN_ENDPOINT}\`,`); + } + if (GATEWAY_ENDPOINT) { + html = html.replace(/GATEWAY_ENDPOINT: .+/, `GATEWAY_ENDPOINT: \`${GATEWAY_ENDPOINT}\`,`); + } + return html; +} + +function applyPlugins(html: string): string { + // plugins + let files = fs.readdirSync(path.join(__dirname, "..", "..", "assets", "plugins")); + let plugins = ""; + files.forEach(x =>{if(x.endsWith(".js")) plugins += `<script src='/assets/plugins/${x}'></script>\n`; }); + return html.replaceAll("<!-- plugin marker -->", plugins); +} + +function applyInlinePlugins(html: string): string{ + // inline plugins + let files = fs.readdirSync(path.join(__dirname, "..", "..", "assets", "inline-plugins")); + let plugins = ""; + files.forEach(x =>{if(x.endsWith(".js")) plugins += `<script src='/assets/inline-plugins/${x}'></script>\n\n`; }); + return html.replaceAll("<!-- inline plugin marker -->", plugins); +} + +function applyPreloadPlugins(html: string): string{ + //preload plugins + let files = fs.readdirSync(path.join(__dirname, "..", "..", "assets", "preload-plugins")); + let plugins = ""; + files.forEach(x =>{if(x.endsWith(".js")) plugins += `<script>${fs.readFileSync(path.join(__dirname, "..", "..", "assets", "preload-plugins", x))}</script>\n`; }); + return html.replaceAll("<!-- preload plugin marker -->", plugins); +} + +function stripHeaders(headers: Headers): Headers { + [ + "content-length", + "content-security-policy", + "strict-transport-security", + "set-cookie", + "transfer-encoding", + "expect-ct", + "access-control-allow-origin", + "content-encoding" + ].forEach(headerName => { + headers.delete(headerName); + }); + return headers; } \ No newline at end of file diff --git a/api/src/routes/users/@me/index.ts b/api/src/routes/users/@me/index.ts index 1af413c4..7fc20457 100644 --- a/api/src/routes/users/@me/index.ts +++ b/api/src/routes/users/@me/index.ts @@ -11,6 +11,7 @@ export interface UserModifySchema { * @maxLength 100 */ username?: string; + discriminator?: string; avatar?: string | null; /** * @maxLength 1024 diff --git a/api/src/routes/users/@me/notes.ts b/api/src/routes/users/@me/notes.ts index 4887b191..3c503942 100644 --- a/api/src/routes/users/@me/notes.ts +++ b/api/src/routes/users/@me/notes.ts @@ -1,37 +1,58 @@ import { Request, Response, Router } from "express"; import { route } from "@fosscord/api"; -import { User, emitEvent } from "@fosscord/util"; +import { User, Note, emitEvent, Snowflake } from "@fosscord/util"; const router: Router = Router(); router.get("/:id", route({}), async (req: Request, res: Response) => { const { id } = req.params; - const user = await User.findOneOrFail({ where: { id: req.user_id }, select: ["notes"] }); - const note = user.notes[id]; + const note = await Note.findOneOrFail({ + where: { + owner: { id: req.user_id }, + target: { id: id }, + } + }); + return res.json({ - note: note, + note: note?.content, note_user_id: id, - user_id: user.id, + user_id: req.user_id, }); }); router.put("/:id", route({}), async (req: Request, res: Response) => { const { id } = req.params; - const user = await User.findOneOrFail({ where: { id: req.user_id } }); - const noteUser = await User.findOneOrFail({ where: { id: id }}); //if noted user does not exist throw + const owner = await User.findOneOrFail({ where: { id: req.user_id } }); + const target = await User.findOneOrFail({ where: { id: id } }); //if noted user does not exist throw const { note } = req.body; - await User.update({ id: req.user_id }, { notes: { ...user.notes, [noteUser.id]: note } }); + if (note && note.length) { + // upsert a note + if (await Note.findOne({ owner: { id: owner.id }, target: { id: target.id } })) { + Note.update( + { owner: { id: owner.id }, target: { id: target.id } }, + { owner, target, content: note } + ); + } + else { + Note.insert( + { id: Snowflake.generate(), owner, target, content: note } + ); + } + } + else { + await Note.delete({ owner: { id: owner.id }, target: { id: target.id } }); + } await emitEvent({ event: "USER_NOTE_UPDATE", data: { note: note, - id: noteUser.id + id: target.id }, - user_id: user.id, - }) + user_id: owner.id, + }); return res.status(204); }); diff --git a/api/src/util/entities/AssetCacheItem.ts b/api/src/util/entities/AssetCacheItem.ts new file mode 100644 index 00000000..160dece6 --- /dev/null +++ b/api/src/util/entities/AssetCacheItem.ts @@ -0,0 +1,3 @@ +export class AssetCacheItem { + constructor(public Key: string, public FilePath: string = "", public Headers: any = null as any) {} +} \ No newline at end of file diff --git a/api/src/util/index.ts b/api/src/util/index.ts index ffbcf24e..ac439371 100644 --- a/api/src/util/index.ts +++ b/api/src/util/index.ts @@ -6,3 +6,4 @@ export * from "./utility/RandomInviteID"; export * from "./handlers/route"; export * from "./utility/String"; export * from "./handlers/Voice"; +export * from "./entities/AssetCacheItem"; \ No newline at end of file |