migrate to v3 - remove backward comp

This commit is contained in:
m
2026-07-28 18:22:37 +02:00
parent bd6e7c38f9
commit d40ca87075
17 changed files with 208 additions and 288 deletions
@@ -1,10 +0,0 @@
-- VaultDrop 012 down: drop V3 new tables
DROP FUNCTION IF EXISTS resolve_effective_role;
DROP TABLE IF EXISTS rebac_relations;
DROP TABLE IF EXISTS sync_queue;
DROP TABLE IF EXISTS retention_policies;
DROP TABLE IF EXISTS resource_placements;
DROP TABLE IF EXISTS storage_locations;
DROP TABLE IF EXISTS resource_variants;
DROP TABLE IF EXISTS resources;
ALTER TABLE users DROP COLUMN IF EXISTS parent_user_id;
@@ -0,0 +1,73 @@
-- VaultDrop 012 down: revert to V1 schema
DROP FUNCTION IF EXISTS resolve_effective_role;
DROP TABLE IF EXISTS sync_queue CASCADE;
DROP TABLE IF EXISTS retention_policies CASCADE;
DROP TABLE IF EXISTS rebac_relations CASCADE;
DROP TABLE IF EXISTS resource_placements CASCADE;
DROP TABLE IF EXISTS resource_variants CASCADE;
DROP TABLE IF EXISTS resource_tags CASCADE;
DROP TABLE IF EXISTS refresh_tokens CASCADE;
DROP TABLE IF EXISTS storage_locations CASCADE;
DROP TABLE IF EXISTS resources CASCADE;
DROP TABLE IF EXISTS tags CASCADE;
DROP TABLE IF EXISTS users CASCADE;
-- Restore V1 tables
CREATE TABLE users (
id TEXT PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(),
username TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE tags (
id TEXT PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(),
parent_tag_id TEXT,
tag_name TEXT NOT NULL,
tag_type TEXT NOT NULL DEFAULT 'none',
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE files (
id TEXT PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
mime_type TEXT NOT NULL DEFAULT '',
size BIGINT NOT NULL DEFAULT 0,
storage_key TEXT NOT NULL DEFAULT '',
checksum TEXT NOT NULL DEFAULT '',
ocr_text TEXT NOT NULL DEFAULT '',
is_folder BOOLEAN NOT NULL DEFAULT false,
parent_file_id TEXT,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE file_tags (
id TEXT PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(),
tag_id TEXT,
file_id TEXT
);
CREATE TABLE thumbnails (
id TEXT PRIMARY KEY DEFAULT gen_random_uuid(),
file_id TEXT NOT NULL REFERENCES files(id) ON DELETE CASCADE,
page_number INTEGER NOT NULL,
resolution_label TEXT NOT NULL,
width INTEGER NOT NULL,
height INTEGER NOT NULL,
storage_key TEXT NOT NULL,
mime_type TEXT NOT NULL DEFAULT 'image/jpeg',
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE refresh_tokens (
id TEXT NOT NULL DEFAULT gen_random_uuid(),
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
token_hash TEXT NOT NULL,
expires_at TIMESTAMP NOT NULL,
revoked BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
@@ -1,9 +1,34 @@
-- VaultDrop 012: V3 new tables - resources, storage_locations, resource_placements, etc.
-- VaultDrop 012: Clean V3 schema
-- Drops all legacy V1 tables, recreates everything with UUIDs
-- Add parent_user_id to users for groups/organizations
ALTER TABLE users ADD COLUMN parent_user_id UUID REFERENCES users(id);
-- Drop legacy tables (order matters for FK dependencies)
DROP TABLE IF EXISTS file_tags CASCADE;
DROP TABLE IF EXISTS resource_tags CASCADE;
DROP TABLE IF EXISTS thumbnails CASCADE;
DROP TABLE IF EXISTS refresh_tokens CASCADE;
DROP TABLE IF EXISTS files CASCADE;
DROP TABLE IF EXISTS tags CASCADE;
DROP TABLE IF EXISTS users CASCADE;
-- Resources (replaces files)
-- Level 1: No dependencies
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
username TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
parent_user_id UUID REFERENCES users(id),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE tags (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
parent_tag_id UUID REFERENCES tags(id),
tag_name TEXT NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- Level 2: Depend on users
CREATE TABLE resources (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
@@ -22,7 +47,40 @@ CREATE INDEX idx_resources_owner ON resources(owner_id);
CREATE INDEX idx_resources_parent ON resources(parent_resource_id);
CREATE INDEX idx_resources_checksum_owner ON resources(checksum, owner_id);
-- Resource variants (replaces thumbnails)
CREATE TABLE storage_locations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id),
device_name TEXT NOT NULL,
role TEXT NOT NULL CHECK (role IN ('primary', 'device', 'backup', 'server')),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
last_seen_at TIMESTAMP
);
CREATE INDEX idx_locations_user ON storage_locations(user_id);
CREATE TABLE refresh_tokens (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
token_hash TEXT NOT NULL,
expires_at TIMESTAMP NOT NULL,
revoked BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_refresh_tokens_user_id ON refresh_tokens(user_id);
CREATE INDEX idx_refresh_tokens_token_hash ON refresh_tokens(token_hash);
-- Level 3: Depend on level 1-2
CREATE TABLE resource_tags (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tag_id UUID NOT NULL REFERENCES tags(id),
resource_id UUID NOT NULL REFERENCES resources(id),
UNIQUE(tag_id, resource_id)
);
CREATE INDEX idx_resource_tags_tag ON resource_tags(tag_id);
CREATE INDEX idx_resource_tags_resource ON resource_tags(resource_id);
CREATE TABLE resource_variants (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
resource_id UUID NOT NULL REFERENCES resources(id) ON DELETE CASCADE,
@@ -39,19 +97,6 @@ CREATE TABLE resource_variants (
CREATE INDEX idx_variants_resource ON resource_variants(resource_id);
CREATE UNIQUE INDEX idx_variants_resource_type_page ON resource_variants(resource_id, variant_type, page_number);
-- Storage locations (devices, server, backup)
CREATE TABLE storage_locations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id),
device_name TEXT NOT NULL,
role TEXT NOT NULL CHECK (role IN ('primary', 'device', 'backup', 'server')),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
last_seen_at TIMESTAMP
);
CREATE INDEX idx_locations_user ON storage_locations(user_id);
-- Resource placements (pivot resource × storage_location)
CREATE TABLE resource_placements (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
resource_id UUID NOT NULL REFERENCES resources(id) ON DELETE CASCADE,
@@ -67,7 +112,19 @@ CREATE INDEX idx_placements_resource ON resource_placements(resource_id);
CREATE INDEX idx_placements_location ON resource_placements(storage_location_id);
CREATE INDEX idx_placements_status ON resource_placements(status);
-- Retention policies
CREATE TABLE rebac_relations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
resource_id UUID NOT NULL REFERENCES resources(id) ON DELETE CASCADE,
subject_user_id UUID NOT NULL REFERENCES users(id),
role TEXT NOT NULL CHECK (role IN ('owner', 'admin', 'editor', 'viewer')),
granted_by UUID NOT NULL REFERENCES users(id),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE(resource_id, subject_user_id)
);
CREATE INDEX idx_rebac_resource ON rebac_relations(resource_id);
CREATE INDEX idx_rebac_subject ON rebac_relations(subject_user_id);
CREATE TABLE retention_policies (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id),
@@ -80,7 +137,6 @@ CREATE TABLE retention_policies (
CREATE INDEX idx_policies_user ON retention_policies(user_id);
CREATE INDEX idx_policies_location ON retention_policies(storage_location_id);
-- Sync queue
CREATE TABLE sync_queue (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
resource_id UUID NOT NULL REFERENCES resources(id) ON DELETE CASCADE,
@@ -96,21 +152,7 @@ CREATE INDEX idx_queue_status ON sync_queue(status);
CREATE INDEX idx_queue_resource ON sync_queue(resource_id);
CREATE INDEX idx_queue_location ON sync_queue(storage_location_id);
-- ReBAC relations
CREATE TABLE rebac_relations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
resource_id UUID NOT NULL REFERENCES resources(id) ON DELETE CASCADE,
subject_user_id UUID NOT NULL REFERENCES users(id),
role TEXT NOT NULL CHECK (role IN ('owner', 'admin', 'editor', 'viewer')),
granted_by UUID NOT NULL REFERENCES users(id),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE(resource_id, subject_user_id)
);
CREATE INDEX idx_rebac_resource ON rebac_relations(resource_id);
CREATE INDEX idx_rebac_subject ON rebac_relations(subject_user_id);
-- Function to resolve effective role with recursive parent/group traversal
-- ReBAC permission resolver
CREATE OR REPLACE FUNCTION resolve_effective_role(p_user_id UUID, p_resource_id UUID)
RETURNS TEXT AS $$
DECLARE
@@ -1,15 +0,0 @@
-- VaultDrop 013 down: revert data migration
-- Re-add tag_type to tags
ALTER TABLE tags ADD COLUMN IF NOT EXISTS tag_type TEXT NOT NULL DEFAULT 'none';
DROP TABLE IF EXISTS resource_tags;
DELETE FROM resource_variants;
DELETE FROM rebac_relations;
DELETE FROM resource_placements;
DELETE FROM storage_locations;
DELETE FROM resources;
@@ -1,100 +0,0 @@
-- VaultDrop 013: Migrate data from V1 to V3
-- Assigns all existing files to user 'pixel'
-- 1. Ensure user 'pixel' exists (create if not)
INSERT INTO users (username, password_hash)
SELECT 'pixel', '$argon2id$v=19$m=65536,t=1,p=4$placeholder$placeholder'
WHERE NOT EXISTS (SELECT 1 FROM users WHERE username = 'pixel');
-- 2. Migrate files -> resources
INSERT INTO resources (id, name, mime_type, size, checksum, ocr_text, is_folder, parent_resource_id, owner_id, created_at, updated_at)
SELECT
f.id::uuid,
f.name,
f.mime_type,
f.size,
f.checksum,
f.ocr_text,
f.is_folder,
f.parent_file_id::uuid,
(SELECT id FROM users WHERE username = 'pixel'),
f.created_at,
f.updated_at
FROM files f
ON CONFLICT (id) DO NOTHING;
-- 3. Create server storage_location for user 'pixel'
INSERT INTO storage_locations (id, user_id, device_name, role)
SELECT
gen_random_uuid(),
(SELECT id FROM users WHERE username = 'pixel'),
'VaultDrop Server',
'server'
WHERE NOT EXISTS (
SELECT 1 FROM storage_locations
WHERE user_id = (SELECT id FROM users WHERE username = 'pixel')
AND role = 'server'
);
-- 4. Create resource_placements for migrated resources
INSERT INTO resource_placements (resource_id, storage_location_id, status, storage_key, synced_at)
SELECT
r.id,
sl.id,
'synced',
f.storage_key,
CURRENT_TIMESTAMP
FROM resources r
JOIN files f ON f.id::uuid = r.id
CROSS JOIN storage_locations sl
WHERE sl.user_id = (SELECT id FROM users WHERE username = 'pixel')
AND sl.role = 'server'
ON CONFLICT (resource_id, storage_location_id) DO NOTHING;
-- 5. Create rebac_relations (owner) for all migrated resources
INSERT INTO rebac_relations (resource_id, subject_user_id, role, granted_by)
SELECT
r.id,
u.id,
'owner',
u.id
FROM resources r
CROSS JOIN (SELECT id FROM users WHERE username = 'pixel') u
ON CONFLICT (resource_id, subject_user_id) DO NOTHING;
-- 6. Migrate thumbnails -> resource_variants
INSERT INTO resource_variants (id, resource_id, variant_type, page_number, width, height, mime_type, generated_by, storage_key, created_at)
SELECT
t.id::uuid,
t.file_id::uuid,
CASE
WHEN t.resolution_label = 'thumbnail' THEN 'thumbnail_small'
WHEN t.resolution_label = 'full' THEN 'thumbnail_full'
ELSE t.resolution_label
END,
t.page_number,
t.width,
t.height,
t.mime_type,
'server',
t.storage_key,
t.created_at
FROM thumbnails t
ON CONFLICT (id) DO NOTHING;
-- 7. Create resource_tags from file_tags (keep old file_tags for now)
CREATE TABLE IF NOT EXISTS resource_tags (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tag_id TEXT,
resource_id TEXT
);
INSERT INTO resource_tags (id, tag_id, resource_id)
SELECT
gen_random_uuid(),
ft.tag_id,
ft.file_id
FROM file_tags ft;
-- 8. Remove tag_type column from tags
ALTER TABLE tags DROP COLUMN IF EXISTS tag_type;
@@ -1,33 +0,0 @@
-- VaultDrop 014 down: restore legacy tables
CREATE TABLE files (
id TEXT PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
mime_type TEXT NOT NULL DEFAULT '',
size BIGINT NOT NULL DEFAULT 0,
storage_key TEXT NOT NULL DEFAULT '',
checksum TEXT NOT NULL DEFAULT '',
ocr_text TEXT NOT NULL DEFAULT '',
is_folder BOOLEAN NOT NULL DEFAULT false,
parent_file_id TEXT,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE thumbnails (
id TEXT PRIMARY KEY DEFAULT gen_random_uuid(),
file_id TEXT NOT NULL REFERENCES files(id) ON DELETE CASCADE,
page_number INTEGER NOT NULL,
resolution_label TEXT NOT NULL,
width INTEGER NOT NULL,
height INTEGER NOT NULL,
storage_key TEXT NOT NULL,
mime_type TEXT NOT NULL DEFAULT 'image/jpeg',
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE file_tags (
id TEXT PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(),
tag_id TEXT,
file_id TEXT
);
@@ -1,10 +0,0 @@
-- VaultDrop 014: Drop legacy V1 tables
-- Tags now use resource_tags instead of file_tags
DROP TABLE IF EXISTS file_tags;
-- Thumbnails migrated to resource_variants
DROP TABLE IF EXISTS thumbnails;
-- Files migrated to resources
DROP TABLE IF EXISTS files;