Files
kochwas/src/lib/server/domains/repository.ts
hsiegeln 15c15c8494
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 1m17s
feat(domains): Inline-Edit + Favicon in Settings + Filter IN Suchmaske
Domain-Admin-Seite bekommt jetzt ein Favicon-Icon vor jedem Eintrag,
einen Pencil-Button zum Inline-Editieren von Domain und Anzeigename,
und Save/Cancel-Buttons. Beim Ändern des Domain-Namens wird das Favicon
zurückgesetzt und beim Speichern frisch nachgeladen (den Filter-Dropdown-
Icons reicht der neue favicon_path automatisch zu).

Der Filter-Button auf der Hauptseite sitzt jetzt IM weißen Suchfeld-
Container (neuer .search-box-Wrapper mit Border) statt daneben, analog
zum Referenz-Screenshot von rezeptwelt.de. Neue inline-Prop an
SearchFilter schaltet eigenen Border/Background ab und setzt stattdessen
einen vertikalen Divider nach rechts.

- Neuer PATCH /api/domains/[id] mit zod-Schema.
- Repository: updateDomain(id, patch) + getDomainById(id).
  domain-Change nullt favicon_path → Caller lädt neu.
- Tests für updateDomain-Fälle und getDomainById.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 08:28:02 +02:00

79 lines
2.4 KiB
TypeScript

import type Database from 'better-sqlite3';
import type { AllowedDomain } from '$lib/types';
export function normalizeDomain(raw: string): string {
return raw.trim().toLowerCase().replace(/^www\./, '');
}
export function listDomains(db: Database.Database): AllowedDomain[] {
return db
.prepare(
'SELECT id, domain, display_name, favicon_path FROM allowed_domain ORDER BY domain'
)
.all() as AllowedDomain[];
}
export function addDomain(
db: Database.Database,
domain: string,
displayName: string | null = null,
addedByProfileId: number | null = null
): AllowedDomain {
const normalized = normalizeDomain(domain);
const row = db
.prepare(
`INSERT INTO allowed_domain(domain, display_name, added_by_profile_id)
VALUES (?, ?, ?)
RETURNING id, domain, display_name, favicon_path`
)
.get(normalized, displayName, addedByProfileId) as AllowedDomain;
return row;
}
export function removeDomain(db: Database.Database, id: number): void {
db.prepare('DELETE FROM allowed_domain WHERE id = ?').run(id);
}
export function setDomainFavicon(
db: Database.Database,
id: number,
faviconPath: string | null
): void {
db.prepare('UPDATE allowed_domain SET favicon_path = ? WHERE id = ?').run(
faviconPath,
id
);
}
export function getDomainById(
db: Database.Database,
id: number
): AllowedDomain | null {
const row = db
.prepare(
'SELECT id, domain, display_name, favicon_path FROM allowed_domain WHERE id = ?'
)
.get(id) as AllowedDomain | undefined;
return row ?? null;
}
export function updateDomain(
db: Database.Database,
id: number,
patch: { domain?: string; display_name?: string | null }
): AllowedDomain | null {
const current = getDomainById(db, id);
if (!current) return null;
const nextDomain =
patch.domain !== undefined ? normalizeDomain(patch.domain) : current.domain;
const nextLabel =
patch.display_name !== undefined ? patch.display_name : current.display_name;
// Wenn sich die Domain ändert: favicon_path zurücksetzen, damit der Caller
// es neu laden kann. Sonst zeigen wir fälschlich das alte Icon.
const nextFavicon = nextDomain !== current.domain ? null : current.favicon_path;
db.prepare(
'UPDATE allowed_domain SET domain = ?, display_name = ?, favicon_path = ? WHERE id = ?'
).run(nextDomain, nextLabel, nextFavicon, id);
return getDomainById(db, id);
}