From b7b8651384b71145b38bcdcbaaaf3c326ed8cebe Mon Sep 17 00:00:00 2001 From: "Jorge Caballero (DataDrivenMD)" <116459476+DataDrivenMD@users.noreply.github.com> Date: Tue, 7 Mar 2023 12:41:39 -0800 Subject: [PATCH] Revert "Merge remote-tracking branch 'upstream/main' into fix-spread-and-rest-based-binding" This reverts commit 85b0ac44a7a8079661943620985a76ec399a5a2c, reversing changes made to 05c993151baf16eb0947648a54dbdb6cf5e31cd2. --- .github/workflows/PRs.yml | 3 - backend/src/activitypub/objects/index.ts | 3 +- backend/src/config/rules.ts | 29 ------- backend/src/config/server.ts | 49 ------------ backend/src/database/neon.ts | 2 +- backend/src/utils/httpsigjs/parser.ts | 3 +- backend/src/utils/sentry.ts | 3 +- backend/src/webpush/util.ts | 4 +- .../adaptors/cloudflare-pages/vite.config.ts | 4 +- frontend/mock-db/init.ts | 4 +- frontend/package.json | 2 - .../layout/LeftColumn/LeftColumn.tsx | 7 -- .../layout/RightColumn/RightColumn.tsx | 9 ++- .../(admin)/server-settings/about/index.tsx | 16 ++-- .../server-settings/branding/index.tsx | 16 ++-- .../(admin)/server-settings/layout.tsx | 4 +- .../server-settings/rules/edit/[id]/index.tsx | 14 +++- .../(admin)/server-settings/rules/index.tsx | 19 +++-- .../src/routes/(frontend)/about/index.tsx | 29 +++++-- .../api/v1/accounts/update_credentials.ts | 4 +- functions/api/wb/settings/server/admins.ts | 0 functions/api/wb/settings/server/rules.ts | 80 +++++++++++++++++++ functions/first-login.ts | 4 +- package.json | 8 +- playwright.config.ts | 6 +- 25 files changed, 176 insertions(+), 146 deletions(-) delete mode 100644 backend/src/config/rules.ts delete mode 100644 backend/src/config/server.ts create mode 100644 functions/api/wb/settings/server/admins.ts create mode 100644 functions/api/wb/settings/server/rules.ts diff --git a/.github/workflows/PRs.yml b/.github/workflows/PRs.yml index 807b7b4..2ebf305 100644 --- a/.github/workflows/PRs.yml +++ b/.github/workflows/PRs.yml @@ -53,9 +53,6 @@ jobs: - name: Check frontend linting run: yarn lint:frontend - - name: Check frontend types - run: yarn --cwd types-check - test-ui: runs-on: ubuntu-latest steps: diff --git a/backend/src/activitypub/objects/index.ts b/backend/src/activitypub/objects/index.ts index 782a161..99f38c4 100644 --- a/backend/src/activitypub/objects/index.ts +++ b/backend/src/activitypub/objects/index.ts @@ -325,8 +325,7 @@ function getContentRewriter() { contentRewriter.on('*', { element(el) { if (!['p', 'span', 'br', 'a'].includes(el.tagName)) { - const element = el as { tagName: string } - element.tagName = 'p' + el.tagName = 'p' } if (el.hasAttribute('class')) { diff --git a/backend/src/config/rules.ts b/backend/src/config/rules.ts deleted file mode 100644 index 68a1342..0000000 --- a/backend/src/config/rules.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { type Database } from 'wildebeest/backend/src/database' - -export async function getRules(db: Database): Promise> { - const query = `SELECT * from server_rules;` - const result = await db.prepare(query).all<{ id: string; text: string }>() - - if (!result.success) { - throw new Error('SQL error: ' + result.error) - } - - return result.results ?? [] -} - -export async function upsertRule(db: Database, rule: { id?: number; text: string } | string) { - const id = typeof rule === 'string' ? null : rule.id ?? null - const text = typeof rule === 'string' ? rule : rule.text - return await db - .prepare( - `INSERT INTO server_rules (id, text) - VALUES (?, ?) - ON CONFLICT(id) DO UPDATE SET text=excluded.text;` - ) - .bind(id, text) - .run() -} - -export async function deleteRule(db: Database, ruleId: number) { - return await db.prepare('DELETE FROM server_rules WHERE id=?').bind(ruleId).run() -} diff --git a/backend/src/config/server.ts b/backend/src/config/server.ts deleted file mode 100644 index 5d4710c..0000000 --- a/backend/src/config/server.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { type Database } from 'wildebeest/backend/src/database' -import { type ServerSettingsData } from 'wildebeest/frontend/src/routes/(admin)/settings/(admin)/server-settings/layout' - -export async function getSettings(db: Database): Promise { - const query = `SELECT * from server_settings` - const result = await db.prepare(query).all<{ setting_name: string; setting_value: string }>() - - const data = (result.results ?? []).reduce( - (settings, { setting_name, setting_value }) => ({ - ...settings, - [setting_name]: setting_value, - }), - {} as Object - ) - - if (!result.success) { - throw new Error('SQL Error: ' + result.error) - } - - return data -} - -export async function updateSettings(db: Database, data: ServerSettingsData) { - const result = await upsertServerSettings(db, data) - if (result && !result.success) { - throw new Error('SQL Error: ' + result.error) - } - - return new Response('', { status: 200 }) -} - -export async function upsertServerSettings(db: Database, settings: Partial) { - const settingsEntries = Object.entries(settings) - - if (!settingsEntries.length) { - return null - } - - const query = ` - INSERT INTO server_settings (setting_name, setting_value) - VALUES ${settingsEntries.map(() => `(?, ?)`).join(', ')} - ON CONFLICT(setting_name) DO UPDATE SET setting_value=excluded.setting_value - ` - - return await db - .prepare(query) - .bind(...settingsEntries.flat()) - .run() -} diff --git a/backend/src/database/neon.ts b/backend/src/database/neon.ts index 0e41b0a..960c5d4 100644 --- a/backend/src/database/neon.ts +++ b/backend/src/database/neon.ts @@ -4,7 +4,7 @@ import type { Env } from 'wildebeest/backend/src/types/env' function sqliteToPsql(query: string): string { let c = 0 - return query.replace(/\?([0-9])?/g, (match: string, p1: string) => { + return query.replaceAll(/\?([0-9])?/g, (match: string, p1: string) => { c += 1 return `$${p1 || c}` }) diff --git a/backend/src/utils/httpsigjs/parser.ts b/backend/src/utils/httpsigjs/parser.ts index cc0a8cd..fa39d2b 100644 --- a/backend/src/utils/httpsigjs/parser.ts +++ b/backend/src/utils/httpsigjs/parser.ts @@ -281,12 +281,11 @@ export function parseRequest(request: Request, options?: Options): ParsedSignatu if (h === 'request-line') { if (!options.strict) { - const cf = (request as { cf?: IncomingRequestCfProperties }).cf /* * We allow headers from the older spec drafts if strict parsing isn't * specified in options. */ - parsed.signingString += request.method + ' ' + request.url + ' ' + cf?.httpProtocol + parsed.signingString += request.method + ' ' + request.url + ' ' + request.cf?.httpProtocol } else { /* Strict parsing doesn't allow older draft headers. */ throw new StrictParsingError('request-line is not a valid header ' + 'with strict parsing enabled.') diff --git a/backend/src/utils/sentry.ts b/backend/src/utils/sentry.ts index 019eabd..ff69e4b 100644 --- a/backend/src/utils/sentry.ts +++ b/backend/src/utils/sentry.ts @@ -19,8 +19,7 @@ export function initSentry(request: Request, env: Env, context: any) { request, transportOptions: { headers }, }) - const cf = (request as { cf?: IncomingRequestCfProperties }).cf - const colo = cf?.colo ? cf.colo : 'UNKNOWN' + const colo = request.cf && request.cf.colo ? request.cf.colo : 'UNKNOWN' sentry.setTag('colo', colo) // cf-connecting-ip should always be present, but if not we can fallback to XFF. diff --git a/backend/src/webpush/util.ts b/backend/src/webpush/util.ts index cc38599..25378af 100644 --- a/backend/src/webpush/util.ts +++ b/backend/src/webpush/util.ts @@ -26,12 +26,12 @@ export function arrayBufferToBase64(buffer: ArrayBuffer): string { } export function b64ToUrlEncoded(str: string): string { - return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+/g, '') + return str.replaceAll(/\+/g, '-').replaceAll(/\//g, '_').replace(/=+/g, '') } export function urlEncodedToB64(str: string): string { const padding = '='.repeat((4 - (str.length % 4)) % 4) - return str.replace(/-/g, '+').replace(/_/g, '/') + padding + return str.replaceAll(/-/g, '+').replaceAll(/_/g, '/') + padding } export function stringToU8Array(str: string): Uint8Array { diff --git a/frontend/adaptors/cloudflare-pages/vite.config.ts b/frontend/adaptors/cloudflare-pages/vite.config.ts index af2dd5a..34baa4b 100644 --- a/frontend/adaptors/cloudflare-pages/vite.config.ts +++ b/frontend/adaptors/cloudflare-pages/vite.config.ts @@ -1,4 +1,4 @@ -import { cloudflarePagesAdapter } from '@builder.io/qwik-city/adapters/cloudflare-pages/vite' +import { cloudflarePagesAdaptor } from '@builder.io/qwik-city/adaptors/cloudflare-pages/vite' import { extendConfig } from '@builder.io/qwik-city/vite' import baseConfig from '../../vite.config' @@ -11,7 +11,7 @@ export default extendConfig(baseConfig, () => { }, }, plugins: [ - cloudflarePagesAdapter({ + cloudflarePagesAdaptor({ // Do not SSG as the D1 database is not available at build time, I think. // staticGenerate: true, }), diff --git a/frontend/mock-db/init.ts b/frontend/mock-db/init.ts index 87dd4f7..37f852d 100644 --- a/frontend/mock-db/init.ts +++ b/frontend/mock-db/init.ts @@ -7,8 +7,8 @@ import { createReply as createReplyInBackend } from 'wildebeest/backend/test/sha import { createStatus } from 'wildebeest/backend/src/mastodon/status' import type { APObject } from 'wildebeest/backend/src/activitypub/objects' import { type Database } from 'wildebeest/backend/src/database' -import { upsertRule } from 'wildebeest/backend/src/config/rules' -import { upsertServerSettings } from 'wildebeest/backend/src/config/server' +import { upsertRule } from 'wildebeest/functions/api/wb/settings/server/rules' +import { upsertServerSettings } from 'wildebeest/functions/api/wb/settings/server/server' /** * Run helper commands to initialize the database with actors, statuses, etc. diff --git a/frontend/package.json b/frontend/package.json index 5e4c646..48d8ceb 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -7,8 +7,6 @@ }, "private": true, "scripts": { - "pretypes-check": "yarn build", - "types-check": "tsc", "lint": "eslint src mock-db adaptors", "build": "vite build && vite build -c adaptors/cloudflare-pages/vite.config.ts", "dev": "vite --mode ssr", diff --git a/frontend/src/components/layout/LeftColumn/LeftColumn.tsx b/frontend/src/components/layout/LeftColumn/LeftColumn.tsx index 45b816d..d82b22d 100644 --- a/frontend/src/components/layout/LeftColumn/LeftColumn.tsx +++ b/frontend/src/components/layout/LeftColumn/LeftColumn.tsx @@ -1,5 +1,4 @@ import { component$, useContext } from '@builder.io/qwik' -import { Link } from '@builder.io/qwik-city' import { InstanceConfigContext } from '~/utils/instanceConfig' import { useDomain } from '~/utils/useDomain' @@ -17,12 +16,6 @@ export default component$(() => { Wildebeest instance thumbnail

{config.description}

- - Learn More - ) }) diff --git a/frontend/src/components/layout/RightColumn/RightColumn.tsx b/frontend/src/components/layout/RightColumn/RightColumn.tsx index 12c16d7..e7a12a8 100644 --- a/frontend/src/components/layout/RightColumn/RightColumn.tsx +++ b/frontend/src/components/layout/RightColumn/RightColumn.tsx @@ -37,7 +37,7 @@ export default component$(() => { { iconName: 'fa-globe', linkText: 'Federated', linkTarget: '/public', linkActiveRegex: /^\/public\/?$/ }, ] - const aboutLink = { iconName: 'fa-ellipsis', linkText: 'About', linkTarget: '/about', linkActiveRegex: /^\/about/ } + // const aboutLink = { iconName: 'fa-ellipsis', linkText: 'About', linkTarget: '/about', linkActiveRegex: /^\/about/ } return (
@@ -49,15 +49,16 @@ export default component$(() => {
{links.map((link) => renderNavLink(link))} -
+ {/* *********** Hiding the about link until the backend support is available ***************** */} + {/*

{renderNavLink(aboutLink)} -
+
*/} {!isAuthorized && ( Sign in diff --git a/frontend/src/routes/(admin)/settings/(admin)/server-settings/about/index.tsx b/frontend/src/routes/(admin)/settings/(admin)/server-settings/about/index.tsx index 0389cde..c5e515d 100644 --- a/frontend/src/routes/(admin)/settings/(admin)/server-settings/about/index.tsx +++ b/frontend/src/routes/(admin)/settings/(admin)/server-settings/about/index.tsx @@ -1,7 +1,7 @@ import { component$ } from '@builder.io/qwik' import { action$, Form, Link, z, zod$ } from '@builder.io/qwik-city' import { getDatabase } from 'wildebeest/backend/src/database' -import { updateSettings } from 'wildebeest/backend/src/config/server' +import { handleRequestPost } from 'wildebeest/functions/api/wb/settings/server/server' import { TextArea } from '~/components/Settings/TextArea' import { serverSettingsLoader } from '../layout' @@ -12,12 +12,16 @@ const zodSchema = zod$({ export type ServerAboutData = Awaited['_type'] -export const action = action$(async (data, { platform }) => { - const db = await getDatabase(platform) +export const action = action$(async (data, { request, platform }) => { let success = false try { - await updateSettings(db, data) - success = true + const response = await handleRequestPost( + await getDatabase(platform), + new Request(request, { body: JSON.stringify(data) }), + platform.ACCESS_AUTH_DOMAIN, + platform.ACCESS_AUD + ) + success = response.ok } catch (e: unknown) { success = false } @@ -32,7 +36,7 @@ export default component$(() => { const saveAction = action() return ( -
+

Provide in-depth information about how the server is operated, moderated, funded.

diff --git a/frontend/src/routes/(admin)/settings/(admin)/server-settings/branding/index.tsx b/frontend/src/routes/(admin)/settings/(admin)/server-settings/branding/index.tsx index c59016b..a6e8961 100644 --- a/frontend/src/routes/(admin)/settings/(admin)/server-settings/branding/index.tsx +++ b/frontend/src/routes/(admin)/settings/(admin)/server-settings/branding/index.tsx @@ -1,7 +1,7 @@ import { component$ } from '@builder.io/qwik' import { action$, Form, zod$, z } from '@builder.io/qwik-city' import { getDatabase } from 'wildebeest/backend/src/database' -import { updateSettings } from 'wildebeest/backend/src/config/server' +import { handleRequestPost } from 'wildebeest/functions/api/wb/settings/server/server' import { TextArea } from '~/components/Settings/TextArea' import { TextInput } from '~/components/Settings/TextInput' import { serverSettingsLoader } from '../layout' @@ -13,12 +13,16 @@ const zodSchema = zod$({ export type ServerBrandingData = Awaited['_type'] -export const action = action$(async (data, { platform }) => { - const db = await getDatabase(platform) +export const action = action$(async (data, { request, platform }) => { let success = false try { - await updateSettings(db, data) - success = true + const response = await handleRequestPost( + await getDatabase(platform), + new Request(request, { body: JSON.stringify(data) }), + platform.ACCESS_AUTH_DOMAIN, + platform.ACCESS_AUD + ) + success = response.ok } catch (e: unknown) { success = false } @@ -33,7 +37,7 @@ export default component$(() => { const saveAction = action() return ( - +

Your server's branding differentiates it from other servers in the network. This information may be displayed across a variety of environments, such as Mastodon's web interface, native applications, in link previews on diff --git a/frontend/src/routes/(admin)/settings/(admin)/server-settings/layout.tsx b/frontend/src/routes/(admin)/settings/(admin)/server-settings/layout.tsx index cba57a4..89c1c71 100644 --- a/frontend/src/routes/(admin)/settings/(admin)/server-settings/layout.tsx +++ b/frontend/src/routes/(admin)/settings/(admin)/server-settings/layout.tsx @@ -1,7 +1,7 @@ import { component$, Slot } from '@builder.io/qwik' import { Link, loader$, useLocation } from '@builder.io/qwik-city' import { getDatabase } from 'wildebeest/backend/src/database' -import { getSettings } from 'wildebeest/backend/src/config/server' +import { handleRequestGet } from 'wildebeest/functions/api/wb/settings/server/server' import { ServerAboutData } from './about' import { ServerBrandingData } from './branding' @@ -10,7 +10,7 @@ export type ServerSettingsData = ServerBrandingData & ServerAboutData export const serverSettingsLoader = loader$>>(async ({ platform }) => { const database = await getDatabase(platform) - const settingsResp = await getSettings(database) + const settingsResp = await handleRequestGet(database) let settingsData: Partial = {} try { settingsData = await settingsResp.json() diff --git a/frontend/src/routes/(admin)/settings/(admin)/server-settings/rules/edit/[id]/index.tsx b/frontend/src/routes/(admin)/settings/(admin)/server-settings/rules/edit/[id]/index.tsx index a8a05e5..a6b3f60 100644 --- a/frontend/src/routes/(admin)/settings/(admin)/server-settings/rules/edit/[id]/index.tsx +++ b/frontend/src/routes/(admin)/settings/(admin)/server-settings/rules/edit/[id]/index.tsx @@ -1,7 +1,8 @@ import { component$ } from '@builder.io/qwik' import { action$, Form, loader$, useNavigate, z, zod$ } from '@builder.io/qwik-city' import { getDatabase } from 'wildebeest/backend/src/database' -import { getRules, upsertRule } from 'wildebeest/backend/src/config/rules' +import { handleRequestGet } from 'wildebeest/functions/api/v1/instance/rules' +import { upsertRule } from 'wildebeest/functions/api/wb/settings/server/rules' import { TextArea } from '~/components/Settings/TextArea' import { getErrorHtml } from '~/utils/getErrorHtml/getErrorHtml' @@ -32,7 +33,14 @@ export const editAction = action$( export const ruleLoader = loader$>(async ({ params, platform, html }) => { const database = await getDatabase(platform) - const rules = await getRules(database) + + const settingsResp = await handleRequestGet(database) + let rules: { id: number; text: string }[] = [] + try { + rules = await settingsResp.json() + } catch { + rules = [] + } const rule: { id: number; text: string } | undefined = rules.find((r) => r.id === +params['id']) @@ -55,7 +63,7 @@ export default component$(() => { return ( <> - +

While most claim to have read and agree to the terms of service, usually people do not read through until after a problem arises. Make it easier to see your server's rules at a glance by providing them in a flat diff --git a/frontend/src/routes/(admin)/settings/(admin)/server-settings/rules/index.tsx b/frontend/src/routes/(admin)/settings/(admin)/server-settings/rules/index.tsx index 08ec5a4..eac772c 100644 --- a/frontend/src/routes/(admin)/settings/(admin)/server-settings/rules/index.tsx +++ b/frontend/src/routes/(admin)/settings/(admin)/server-settings/rules/index.tsx @@ -1,7 +1,8 @@ import { component$ } from '@builder.io/qwik' import { action$, Form, Link, loader$, z, zod$ } from '@builder.io/qwik-city' import { getDatabase } from 'wildebeest/backend/src/database' -import { getRules, deleteRule, upsertRule } from 'wildebeest/backend/src/config/rules' +import { handleRequestGet } from 'wildebeest/functions/api/v1/instance/rules' +import { deleteRule, upsertRule } from 'wildebeest/functions/api/wb/settings/server/rules' import { TextArea } from '~/components/Settings/TextArea' export type ServerSettingsData = { rules: string[] } @@ -47,7 +48,15 @@ export const deleteAction = action$( export const rulesLoader = loader$>(async ({ platform }) => { const database = await getDatabase(platform) - const rules = await getRules(database) + + const settingsResp = await handleRequestGet(database) + let rules: { id: number; text: string }[] = [] + try { + rules = await settingsResp.json() + } catch { + rules = [] + } + return JSON.parse(JSON.stringify(rules)) }) @@ -85,11 +94,11 @@ export default component$(() => {

{rules.value.map(({ id, text }, idx) => { - const ruleNumber = idx + 1 - const ruleBtnText = `${ruleNumber}. ${text.slice(0, 27)}${text.length > 27 ? '...' : ''}` + const ruleId = idx + 1 + const ruleBtnText = `${ruleId}. ${text.slice(0, 27)}${text.length > 27 ? '...' : ''}` return (
- + {ruleBtnText}
diff --git a/frontend/src/routes/(frontend)/about/index.tsx b/frontend/src/routes/(frontend)/about/index.tsx index 6502fe3..ea43c82 100644 --- a/frontend/src/routes/(frontend)/about/index.tsx +++ b/frontend/src/routes/(frontend)/about/index.tsx @@ -2,10 +2,11 @@ import { component$ } from '@builder.io/qwik' import { DocumentHead, loader$ } from '@builder.io/qwik-city' import { getDatabase } from 'wildebeest/backend/src/database' import { getDomain } from 'wildebeest/backend/src/utils/getDomain' -import { getSettings } from 'wildebeest/backend/src/config/server' -import { getRules } from 'wildebeest/backend/src/config/rules' +import { handleRequestGet as settingsHandleRequestGet } from 'wildebeest/functions/api/wb/settings/server/server' +import { handleRequestGet as rulesHandleRequestGet } from 'wildebeest/functions/api/v1/instance/rules' import { Accordion } from '~/components/Accordion/Accordion' import { HtmlContent } from '~/components/HtmlContent/HtmlContent' +import { ServerSettingsData } from '~/routes/(admin)/settings/(admin)/server-settings/layout' import { Account } from '~/types' import { getDocumentHead } from '~/utils/getDocumentHead' import { instanceLoader } from '../layout' @@ -27,9 +28,25 @@ type AboutInfo = { export const aboutInfoLoader = loader$>(async ({ resolveValue, request, platform }) => { // TODO: fetching the instance for the thumbnail, but that should be part of the settings const instance = await resolveValue(instanceLoader) + const database = await getDatabase(platform) - const brandingData = await getSettings(database) - const rules = await getRules(database) + + const brandingDataResp = await settingsHandleRequestGet(database) + let brandingData: ServerSettingsData | null + try { + brandingData = await brandingDataResp.json() + } catch { + brandingData = null + } + + const rulesResp = await rulesHandleRequestGet(database) + let rules: { id: number; text: string }[] = [] + try { + rules = await rulesResp.json() + } catch { + rules = [] + } + const admins = await getAdmins(database) let adminAccount: Account | null = null @@ -105,10 +122,10 @@ export default component$(() => {
    - {aboutInfo.rules.map(({ id, text }, idx) => ( + {aboutInfo.rules.map(({ id, text }) => (
  1. - {idx + 1} + {id} {text}
  2. diff --git a/functions/api/v1/accounts/update_credentials.ts b/functions/api/v1/accounts/update_credentials.ts index d93d5f4..175d57f 100644 --- a/functions/api/v1/accounts/update_credentials.ts +++ b/functions/api/v1/accounts/update_credentials.ts @@ -60,12 +60,12 @@ export async function handleRequest( if (formData.has('display_name')) { const value = formData.get('display_name')! - await updateActorProperty(db, connectedActor.id, 'name', value as string) + await updateActorProperty(db, connectedActor.id, 'name', value) } if (formData.has('note')) { const value = formData.get('note')! - await updateActorProperty(db, connectedActor.id, 'summary', value as string) + await updateActorProperty(db, connectedActor.id, 'summary', value) } if (formData.has('avatar')) { diff --git a/functions/api/wb/settings/server/admins.ts b/functions/api/wb/settings/server/admins.ts new file mode 100644 index 0000000..e69de29 diff --git a/functions/api/wb/settings/server/rules.ts b/functions/api/wb/settings/server/rules.ts new file mode 100644 index 0000000..99fc679 --- /dev/null +++ b/functions/api/wb/settings/server/rules.ts @@ -0,0 +1,80 @@ +import type { Env } from 'wildebeest/backend/src/types/env' +import type { ContextData } from 'wildebeest/backend/src/types/context' +import * as errors from 'wildebeest/backend/src/errors' +import { type Database, getDatabase } from 'wildebeest/backend/src/database' +import { parse } from 'cookie' +import { isUserAdmin } from 'wildebeest/backend/src/utils/auth/isUserAdmin' + +export const onRequestGet: PagesFunction = async ({ env, request }) => { + return handleRequestPost(await getDatabase(env), request, env.ACCESS_AUTH_DOMAIN, env.ACCESS_AUD) +} + +export async function handleRequestGet(db: Database) { + const query = `SELECT * from server_rules;` + const result = await db.prepare(query).all<{ id: string; text: string }>() + + if (!result.success) { + return new Response('SQL error: ' + result.error, { status: 500 }) + } + + return new Response(JSON.stringify(result.results ?? []), { status: 200 }) +} + +export const onRequestPost: PagesFunction = async ({ env, request }) => { + return handleRequestPost(await getDatabase(env), request, env.ACCESS_AUTH_DOMAIN, env.ACCESS_AUD) +} + +export async function handleRequestPost(db: Database, request: Request, accessAuthDomain: string, accessAud: string) { + const cookie = parse(request.headers.get('Cookie') || '') + const jwt = cookie['CF_Authorization'] + const isAdmin = await isUserAdmin(request, jwt, accessAuthDomain, accessAud, db) + + if (!isAdmin) { + return errors.notAuthorized('Lacking authorization rights to edit server rules') + } + + const rule = await request.json<{ id?: number; text: string }>() + const result = await upsertRule(db, rule) + + if (!result.success) { + return new Response('SQL error: ' + result.error, { status: 500 }) + } + + return new Response('', { status: 200 }) +} + +export async function upsertRule(db: Database, rule: { id?: number; text: string } | string) { + const id = typeof rule === 'string' ? null : rule.id ?? null + const text = typeof rule === 'string' ? rule : rule.text + return await db + .prepare( + `INSERT INTO server_rules (id, text) + VALUES (?, ?) + ON CONFLICT(id) DO UPDATE SET text=excluded.text;` + ) + .bind(id, text) + .run() +} + +export async function handleRequestDelete(db: Database, request: Request, accessAuthDomain: string, accessAud: string) { + const cookie = parse(request.headers.get('Cookie') || '') + const jwt = cookie['CF_Authorization'] + const isAdmin = await isUserAdmin(request, jwt, accessAuthDomain, accessAud, db) + + if (!isAdmin) { + return errors.notAuthorized('Lacking authorization rights to edit server rules') + } + + const rule = await request.json<{ id: number }>() + const result = await deleteRule(db, rule.id) + + if (!result.success) { + return new Response('SQL error: ' + result.error, { status: 500 }) + } + + return new Response('', { status: 200 }) +} + +export async function deleteRule(db: Database, ruleId: number) { + return await db.prepare('DELETE FROM server_rules WHERE id=?').bind(ruleId).run() +} diff --git a/functions/first-login.ts b/functions/first-login.ts index 1a9c9e2..abccf82 100644 --- a/functions/first-login.ts +++ b/functions/first-login.ts @@ -42,11 +42,11 @@ export async function handlePostRequest( const properties: Record = {} if (formData.has('username')) { - properties.preferredUsername = (formData.get('username') as string) || '' + properties.preferredUsername = formData.get('username') || '' } if (formData.has('name')) { - properties.name = (formData.get('name') as string) || '' + properties.name = formData.get('name') || '' } await createPerson(domain, db, userKEK, email, properties) diff --git a/package.json b/package.json index 2d2bab7..136d527 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "http-message-signatures": "^0.1.2", "toucan-js": "^3.1.0" }, - "simple-git-hooks": { - "pre-commit": "yarn lint" - } -} + "simple-git-hooks": { + "pre-commit": "yarn lint" + } +} \ No newline at end of file diff --git a/playwright.config.ts b/playwright.config.ts index ec8c91f..20a8b7b 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -19,14 +19,14 @@ const config: PlaywrightTestConfig = { * Maximum time expect() should wait for the condition to be met. * For example in `await expect(locator).toHaveText();` */ - timeout: (process.env.CI ? 30 : 5) * 1000, + timeout: process.env.CI ? 5000 : 500, }, /* Run tests in files in parallel */ fullyParallel: true, /* Fail the build on CI if you accidentally left test.only in the source code. */ forbidOnly: !!process.env.CI, /* Retry on CI only */ - retries: process.env.CI ? 1 : 0, + retries: process.env.CI ? 3 : 0, /* Opt out of parallel tests on CI. */ workers: process.env.CI ? 1 : undefined, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ @@ -34,7 +34,7 @@ const config: PlaywrightTestConfig = { /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ - actionTimeout: (process.env.CI ? 30 : 10) * 1000, + actionTimeout: 0, /* Base URL to use in actions like `await page.goto('/')`. */ // baseURL: 'http://localhost:3000',