diff --git a/backend/src/config/rules.ts b/backend/src/config/rules.ts new file mode 100644 index 0000000..68a1342 --- /dev/null +++ b/backend/src/config/rules.ts @@ -0,0 +1,29 @@ +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 new file mode 100644 index 0000000..5d4710c --- /dev/null +++ b/backend/src/config/server.ts @@ -0,0 +1,49 @@ +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/frontend/mock-db/init.ts b/frontend/mock-db/init.ts index 37f852d..87dd4f7 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/functions/api/wb/settings/server/rules' -import { upsertServerSettings } from 'wildebeest/functions/api/wb/settings/server/server' +import { upsertRule } from 'wildebeest/backend/src/config/rules' +import { upsertServerSettings } from 'wildebeest/backend/src/config/server' /** * Run helper commands to initialize the database with actors, statuses, etc. 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 c5e515d..db74357 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 { handleRequestPost } from 'wildebeest/functions/api/wb/settings/server/server' +import { updateSettings } from 'wildebeest/backend/src/config/server' import { TextArea } from '~/components/Settings/TextArea' import { serverSettingsLoader } from '../layout' @@ -12,16 +12,12 @@ const zodSchema = zod$({ export type ServerAboutData = Awaited['_type'] -export const action = action$(async (data, { request, platform }) => { +export const action = action$(async (data, { platform }) => { + const db = await getDatabase(platform) let success = false try { - const response = await handleRequestPost( - await getDatabase(platform), - new Request(request, { body: JSON.stringify(data) }), - platform.ACCESS_AUTH_DOMAIN, - platform.ACCESS_AUD - ) - success = response.ok + await updateSettings(db, data) + success = true } catch (e: unknown) { success = false } 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 a6e8961..34f201f 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 { handleRequestPost } from 'wildebeest/functions/api/wb/settings/server/server' +import { updateSettings } from 'wildebeest/backend/src/config/server' import { TextArea } from '~/components/Settings/TextArea' import { TextInput } from '~/components/Settings/TextInput' import { serverSettingsLoader } from '../layout' @@ -13,16 +13,12 @@ const zodSchema = zod$({ export type ServerBrandingData = Awaited['_type'] -export const action = action$(async (data, { request, platform }) => { +export const action = action$(async (data, { platform }) => { + const db = await getDatabase(platform) let success = false try { - const response = await handleRequestPost( - await getDatabase(platform), - new Request(request, { body: JSON.stringify(data) }), - platform.ACCESS_AUTH_DOMAIN, - platform.ACCESS_AUD - ) - success = response.ok + await updateSettings(db, data) + success = true } catch (e: unknown) { success = false } 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 89c1c71..cba57a4 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 { handleRequestGet } from 'wildebeest/functions/api/wb/settings/server/server' +import { getSettings } from 'wildebeest/backend/src/config/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 handleRequestGet(database) + const settingsResp = await getSettings(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 a6b3f60..57c098f 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,8 +1,7 @@ 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 { handleRequestGet } from 'wildebeest/functions/api/v1/instance/rules' -import { upsertRule } from 'wildebeest/functions/api/wb/settings/server/rules' +import { getRules, upsertRule } from 'wildebeest/backend/src/config/rules' import { TextArea } from '~/components/Settings/TextArea' import { getErrorHtml } from '~/utils/getErrorHtml/getErrorHtml' @@ -33,14 +32,7 @@ export const editAction = action$( export const ruleLoader = loader$>(async ({ params, platform, html }) => { const database = await getDatabase(platform) - - const settingsResp = await handleRequestGet(database) - let rules: { id: number; text: string }[] = [] - try { - rules = await settingsResp.json() - } catch { - rules = [] - } + const rules = await getRules(database) const rule: { id: number; text: string } | undefined = rules.find((r) => r.id === +params['id']) 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 eac772c..338a364 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,8 +1,7 @@ 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 { handleRequestGet } from 'wildebeest/functions/api/v1/instance/rules' -import { deleteRule, upsertRule } from 'wildebeest/functions/api/wb/settings/server/rules' +import { getRules, deleteRule, upsertRule } from 'wildebeest/backend/src/config/rules' import { TextArea } from '~/components/Settings/TextArea' export type ServerSettingsData = { rules: string[] } @@ -48,15 +47,7 @@ export const deleteAction = action$( export const rulesLoader = loader$>(async ({ platform }) => { const database = await getDatabase(platform) - - const settingsResp = await handleRequestGet(database) - let rules: { id: number; text: string }[] = [] - try { - rules = await settingsResp.json() - } catch { - rules = [] - } - + const rules = await getRules(database) return JSON.parse(JSON.stringify(rules)) }) diff --git a/frontend/src/routes/(frontend)/about/index.tsx b/frontend/src/routes/(frontend)/about/index.tsx index ea43c82..94dbc6e 100644 --- a/frontend/src/routes/(frontend)/about/index.tsx +++ b/frontend/src/routes/(frontend)/about/index.tsx @@ -2,11 +2,10 @@ 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 { handleRequestGet as settingsHandleRequestGet } from 'wildebeest/functions/api/wb/settings/server/server' -import { handleRequestGet as rulesHandleRequestGet } from 'wildebeest/functions/api/v1/instance/rules' +import { getSettings } from 'wildebeest/backend/src/config/server' +import { getRules } from 'wildebeest/backend/src/config/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' @@ -28,25 +27,9 @@ 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 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 brandingData = await getSettings(database) + const rules = await getRules(database) const admins = await getAdmins(database) let adminAccount: Account | null = null diff --git a/functions/api/wb/settings/server/admins.ts b/functions/api/wb/settings/server/admins.ts deleted file mode 100644 index e69de29..0000000 diff --git a/functions/api/wb/settings/server/rules.ts b/functions/api/wb/settings/server/rules.ts deleted file mode 100644 index 99fc679..0000000 --- a/functions/api/wb/settings/server/rules.ts +++ /dev/null @@ -1,80 +0,0 @@ -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/api/wb/settings/server/server.ts b/functions/api/wb/settings/server/server.ts deleted file mode 100644 index e81e839..0000000 --- a/functions/api/wb/settings/server/server.ts +++ /dev/null @@ -1,73 +0,0 @@ -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 { ServerSettingsData } from 'wildebeest/frontend/src/routes/(admin)/settings/(admin)/server-settings/layout' -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_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) { - return new Response('SQL error: ' + result.error, { status: 500 }) - } - - return new Response(JSON.stringify(data), { 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 settings') - } - - const data = await request.json() - - const result = await upsertServerSettings(db, data) - - if (result && !result.success) { - return new Response('SQL error: ' + result.error, { status: 500 }) - } - - 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() -}