mirror of
https://gitlab.w-hs.de/an14051/backerup-website.git
synced 2026-07-27 17:35:29 +00:00
feat: add admin links management and updaterup page
- Implemented AdminLinksPage for managing link entries with admin role check. - Created API routes for fetching, updating, and deleting link entries. - Added ProductTabs component for navigation between Backerup and Updaterup. - Developed UpdaterupPage with detailed features, setup instructions, and statistics. - Introduced links database functionality for runtime-editable link overrides.
This commit is contained in:
@@ -65,6 +65,12 @@ async function applyMigrations(db: Client): Promise<void> {
|
||||
role TEXT NOT NULL DEFAULT 'user',
|
||||
created_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS links (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
`)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Runtime-editable link overrides stored in SQLite.
|
||||
* Falls back to the static LINKS defaults for any key without an override.
|
||||
*/
|
||||
import { getDb, ensureReady } from './db'
|
||||
import { LINKS } from './links'
|
||||
|
||||
export type LinkKey = keyof typeof LINKS
|
||||
|
||||
/** Human-readable labels for each link key (used in the admin UI). */
|
||||
export const LINK_META: Record<LinkKey, { label: string; description: string; group: string }> = {
|
||||
gitea: { label: 'Backerup — Gitea repo', description: 'Repository root used in nav, footer, and CTA buttons', group: 'Backerup' },
|
||||
giteaReleases: { label: 'Backerup — Releases', description: 'Releases / changelog page', group: 'Backerup' },
|
||||
giteaIssues: { label: 'Backerup — Issues', description: 'Issue tracker', group: 'Backerup' },
|
||||
giteaDiscussions: { label: 'Backerup — Discussions', description: 'Gitea Discussions forum', group: 'Backerup' },
|
||||
giteaContributing: { label: 'Backerup — Contributing', description: 'CONTRIBUTING.md guide', group: 'Backerup' },
|
||||
docs: { label: 'Backerup — Docs', description: 'Primary documentation URL', group: 'Backerup' },
|
||||
dockerImage: { label: 'Backerup — Docker image', description: 'Full image reference shown in install snippets', group: 'Backerup' },
|
||||
uiPort: { label: 'Backerup — UI port', description: 'Default port the Backerup container exposes', group: 'Backerup' },
|
||||
updaterupGitea: { label: 'Updaterup — Gitea repo', description: 'Repository root used in Updaterup pages', group: 'Updaterup' },
|
||||
updaterupDockerImage: { label: 'Updaterup — Docker image', description: 'Full image reference shown in Updaterup install snippets', group: 'Updaterup' },
|
||||
}
|
||||
|
||||
export interface LinkEntry {
|
||||
key: LinkKey
|
||||
label: string
|
||||
description: string
|
||||
group: string
|
||||
defaultValue: string
|
||||
override: string | null
|
||||
currentValue: string
|
||||
}
|
||||
|
||||
/** Returns merged links: DB overrides take precedence over static defaults. */
|
||||
export async function getLinks(): Promise<typeof LINKS> {
|
||||
await ensureReady()
|
||||
const { rows } = await getDb().execute('SELECT key, value FROM links')
|
||||
const overrides: Partial<Record<string, string>> = {}
|
||||
for (const row of rows) {
|
||||
overrides[row.key as string] = row.value as string
|
||||
}
|
||||
return { ...LINKS, ...overrides } as typeof LINKS
|
||||
}
|
||||
|
||||
/** Returns all link entries with defaults, overrides, and current values — for the admin UI. */
|
||||
export async function getAllLinkEntries(): Promise<LinkEntry[]> {
|
||||
await ensureReady()
|
||||
const { rows } = await getDb().execute('SELECT key, value FROM links')
|
||||
const overrides: Partial<Record<string, string>> = {}
|
||||
for (const row of rows) {
|
||||
overrides[row.key as string] = row.value as string
|
||||
}
|
||||
|
||||
return (Object.keys(LINK_META) as LinkKey[]).map((key) => ({
|
||||
key,
|
||||
...LINK_META[key],
|
||||
defaultValue: LINKS[key],
|
||||
override: overrides[key] ?? null,
|
||||
currentValue: overrides[key] ?? LINKS[key],
|
||||
}))
|
||||
}
|
||||
|
||||
/** Upserts a link override. Pass the static default to effectively reset it, or use resetLink(). */
|
||||
export async function setLink(key: LinkKey, value: string): Promise<void> {
|
||||
await ensureReady()
|
||||
await getDb().execute({
|
||||
sql: `INSERT INTO links (key, value, updated_at)
|
||||
VALUES (?, ?, datetime('now'))
|
||||
ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at`,
|
||||
args: [key, value],
|
||||
})
|
||||
}
|
||||
|
||||
/** Removes the DB override for a key, restoring the static default. */
|
||||
export async function resetLink(key: LinkKey): Promise<void> {
|
||||
await ensureReady()
|
||||
await getDb().execute({ sql: 'DELETE FROM links WHERE key = ?', args: [key] })
|
||||
}
|
||||
@@ -26,6 +26,12 @@ export const LINKS = {
|
||||
dockerImage: "gitea.doomlabs.de/kptltd00m/backerup:latest",
|
||||
/** Default UI port the container exposes */
|
||||
uiPort: "8080",
|
||||
|
||||
// ── Updaterup ─────────────────────────────────────────────
|
||||
/** Updaterup repository root */
|
||||
updaterupGitea: "https://gitea.doomlabs.de/KptltD00M/updaterup",
|
||||
/** Updaterup Docker image reference */
|
||||
updaterupDockerImage: "gitea.doomlabs.de/kptltd00m/updaterup:latest",
|
||||
} as const;
|
||||
|
||||
export type Links = typeof LINKS;
|
||||
|
||||
Reference in New Issue
Block a user