feat: implement unified editor with content persistence and server-side upsert

- Replace dual update systems with single markdown-first editor architecture
- Add server-side upsert to eliminate 404 errors on PUT operations
- Fix content persistence race condition between preview and save operations
- Remove legacy updateElementContent system entirely
- Add comprehensive authentication with JWT scaffolding and dev mode
- Implement EditContext.updateOriginalContent() for proper baseline management
- Enable markdown formatting in all text elements (h1-h6, p, div, etc)
- Clean terminology: remove 'unified' references from codebase

Technical changes:
* core/editor.js: Remove legacy update system, unify content types as markdown
* ui/Editor.js: Add updateOriginalContent() method to fix save persistence
* ui/Previewer.js: Clean live preview system for all content types
* api/handlers.go: Implement UpsertContent for idempotent PUT operations
* auth/*: Complete authentication service with OAuth scaffolding
* db/queries/content.sql: Add upsert query with ON CONFLICT handling
* Schema: Remove type constraints, rely on server-side validation

Result: Clean content editing with persistent saves, no 404 errors, markdown support in all text elements
This commit is contained in:
2025-09-10 20:19:54 +02:00
parent c572428e45
commit b0c4a33a7c
23 changed files with 1658 additions and 3585 deletions

View File

@@ -4,7 +4,7 @@ CREATE TABLE content (
id TEXT NOT NULL,
site_id TEXT NOT NULL,
value TEXT NOT NULL,
type TEXT NOT NULL CHECK (type IN ('text', 'markdown', 'link')),
type TEXT NOT NULL,
created_at BIGINT DEFAULT EXTRACT(EPOCH FROM NOW()) NOT NULL,
updated_at BIGINT DEFAULT EXTRACT(EPOCH FROM NOW()) NOT NULL,
last_edited_by TEXT DEFAULT 'system' NOT NULL,

View File

@@ -25,6 +25,15 @@ SET value = sqlc.arg(value), type = sqlc.arg(type), last_edited_by = sqlc.arg(la
WHERE id = sqlc.arg(id) AND site_id = sqlc.arg(site_id)
RETURNING id, site_id, value, type, created_at, updated_at, last_edited_by;
-- name: UpsertContent :one
INSERT INTO content (id, site_id, value, type, last_edited_by)
VALUES (sqlc.arg(id), sqlc.arg(site_id), sqlc.arg(value), sqlc.arg(type), sqlc.arg(last_edited_by))
ON CONFLICT(id, site_id) DO UPDATE SET
value = EXCLUDED.value,
type = EXCLUDED.type,
last_edited_by = EXCLUDED.last_edited_by
RETURNING id, site_id, value, type, created_at, updated_at, last_edited_by;
-- name: DeleteContent :exec
DELETE FROM content
WHERE id = sqlc.arg(id) AND site_id = sqlc.arg(site_id);

View File

@@ -4,7 +4,7 @@ CREATE TABLE content (
id TEXT NOT NULL,
site_id TEXT NOT NULL,
value TEXT NOT NULL,
type TEXT NOT NULL CHECK (type IN ('text', 'markdown', 'link')),
type TEXT NOT NULL,
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
updated_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
last_edited_by TEXT DEFAULT 'system' NOT NULL,