feat(backend): migrations Postgres testées (users, devices, resources, operations, ocr_jobs) + migrate au boot
- golang-migrate embarqué via embed (db/migrations/*.sql), MigrateDatabase(url) au boot
- ids TEXT 32-hex avec CHECK ~ '^[0-9a-f]{32}$' ; operations idempotentes UNIQUE(device_id, operation_id)
- harness db/migrations_test.go (up → assertions schéma → down, skip si PG indisponible)
- DB dev fraîche vaultdrop_dev (la DB vaultdrop héberge un prototype V2 abandonné)
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
DROP INDEX IF EXISTS idx_users_parent;
|
||||
DROP INDEX IF EXISTS idx_users_email;
|
||||
DROP TABLE IF EXISTS users;
|
||||
@@ -0,0 +1,13 @@
|
||||
CREATE TABLE users (
|
||||
id TEXT PRIMARY KEY CHECK (id ~ '^[0-9a-f]{32}$'),
|
||||
email VARCHAR(255),
|
||||
display_name VARCHAR(255),
|
||||
parent_user_id TEXT REFERENCES users(id) ON DELETE SET NULL,
|
||||
storage_quota_bytes BIGINT DEFAULT 10737418240,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
deleted_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX idx_users_email ON users(email) WHERE email IS NOT NULL AND deleted_at IS NULL;
|
||||
CREATE INDEX idx_users_parent ON users(parent_user_id) WHERE deleted_at IS NULL;
|
||||
@@ -0,0 +1,2 @@
|
||||
DROP INDEX IF EXISTS idx_devices_user;
|
||||
DROP TABLE IF EXISTS devices;
|
||||
@@ -0,0 +1,9 @@
|
||||
CREATE TABLE devices (
|
||||
device_id TEXT PRIMARY KEY CHECK (device_id ~ '^[0-9a-f]{32}$'),
|
||||
user_id TEXT REFERENCES users(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
registered_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
last_seen_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE INDEX idx_devices_user ON devices(user_id) WHERE user_id IS NOT NULL;
|
||||
@@ -0,0 +1,4 @@
|
||||
DROP INDEX IF EXISTS idx_resources_category;
|
||||
DROP INDEX IF EXISTS idx_resources_parent;
|
||||
DROP INDEX IF EXISTS idx_resources_owner;
|
||||
DROP TABLE IF EXISTS resources;
|
||||
@@ -0,0 +1,21 @@
|
||||
CREATE TABLE resources (
|
||||
resource_id TEXT PRIMARY KEY CHECK (resource_id ~ '^[0-9a-f]{32}$'),
|
||||
type TEXT NOT NULL CHECK (type IN ('file', 'folder')),
|
||||
name TEXT NOT NULL,
|
||||
parent_id TEXT REFERENCES resources(resource_id) ON DELETE CASCADE,
|
||||
owner_id TEXT NOT NULL REFERENCES devices(device_id) ON DELETE CASCADE,
|
||||
content_hash VARCHAR(128),
|
||||
size_bytes BIGINT DEFAULT 0,
|
||||
mime_type VARCHAR(255),
|
||||
extension VARCHAR(64),
|
||||
category VARCHAR(50) CHECK (category IN ('photo', 'video', 'document', 'audio', 'other')),
|
||||
taken_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
deleted_at TIMESTAMPTZ,
|
||||
CONSTRAINT resources_unique_name_per_parent UNIQUE (parent_id, name)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_resources_owner ON resources(owner_id) WHERE deleted_at IS NULL;
|
||||
CREATE INDEX idx_resources_parent ON resources(parent_id);
|
||||
CREATE INDEX idx_resources_category ON resources(category) WHERE deleted_at IS NULL;
|
||||
@@ -0,0 +1,2 @@
|
||||
DROP INDEX IF EXISTS idx_operations_device_created;
|
||||
DROP TABLE IF EXISTS operations;
|
||||
@@ -0,0 +1,14 @@
|
||||
CREATE TABLE operations (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
device_id TEXT NOT NULL REFERENCES devices(device_id) ON DELETE CASCADE,
|
||||
operation_id TEXT NOT NULL CHECK (operation_id ~ '^[0-9a-f]{32}$'),
|
||||
op_type TEXT NOT NULL,
|
||||
payload JSONB NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'applied' CHECK (status IN ('applied', 'failed')),
|
||||
error_code TEXT,
|
||||
applied_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
CONSTRAINT operations_unique_per_device UNIQUE (device_id, operation_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_operations_device_created ON operations(device_id, created_at);
|
||||
@@ -0,0 +1,3 @@
|
||||
DROP INDEX IF EXISTS idx_ocr_jobs_file;
|
||||
DROP INDEX IF EXISTS idx_ocr_jobs_status;
|
||||
DROP TABLE IF EXISTS ocr_jobs;
|
||||
@@ -0,0 +1,15 @@
|
||||
CREATE TABLE ocr_jobs (
|
||||
job_id TEXT PRIMARY KEY CHECK (job_id ~ '^[0-9a-f]{32}$'),
|
||||
file_id TEXT NOT NULL REFERENCES resources(resource_id) ON DELETE CASCADE,
|
||||
device_id TEXT NOT NULL REFERENCES devices(device_id) ON DELETE CASCADE,
|
||||
status TEXT NOT NULL DEFAULT 'queued' CHECK (status IN ('queued', 'processing', 'done', 'failed')),
|
||||
language VARCHAR(64) DEFAULT 'fra+eng',
|
||||
text TEXT,
|
||||
error TEXT,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
started_at TIMESTAMPTZ,
|
||||
completed_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE INDEX idx_ocr_jobs_status ON ocr_jobs(status);
|
||||
CREATE INDEX idx_ocr_jobs_file ON ocr_jobs(file_id);
|
||||
Reference in New Issue
Block a user