diff --git a/crates/post3-server/migrations/20260226000001_initial.sql b/crates/post3-server/migrations/20260226000001_initial.sql new file mode 100644 index 0000000..a8d65fd --- /dev/null +++ b/crates/post3-server/migrations/20260226000001_initial.sql @@ -0,0 +1,37 @@ +CREATE TABLE buckets ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + name TEXT NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); +CREATE UNIQUE INDEX idx_buckets_name ON buckets (name); + +CREATE TABLE objects ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + bucket_id UUID NOT NULL REFERENCES buckets(id) ON DELETE CASCADE, + key TEXT NOT NULL, + size BIGINT NOT NULL, + etag TEXT NOT NULL, + content_type TEXT NOT NULL DEFAULT 'application/octet-stream', + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); +CREATE UNIQUE INDEX idx_objects_bucket_key ON objects (bucket_id, key); +CREATE INDEX idx_objects_key_prefix ON objects (bucket_id, key text_pattern_ops); + +CREATE TABLE object_metadata ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + object_id UUID NOT NULL REFERENCES objects(id) ON DELETE CASCADE, + meta_key TEXT NOT NULL, + meta_value TEXT NOT NULL +); +CREATE UNIQUE INDEX idx_metadata_object_key ON object_metadata (object_id, meta_key); +CREATE INDEX idx_metadata_object_id ON object_metadata (object_id); + +CREATE TABLE blocks ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + object_id UUID NOT NULL REFERENCES objects(id) ON DELETE CASCADE, + block_index INT NOT NULL, + data BYTEA NOT NULL, + block_size INT NOT NULL +); +CREATE UNIQUE INDEX idx_blocks_object_index ON blocks (object_id, block_index); +CREATE INDEX idx_blocks_object_id ON blocks (object_id); diff --git a/crates/post3-server/migrations/20260227000001_multipart_uploads.sql b/crates/post3-server/migrations/20260227000001_multipart_uploads.sql new file mode 100644 index 0000000..6d01d0d --- /dev/null +++ b/crates/post3-server/migrations/20260227000001_multipart_uploads.sql @@ -0,0 +1,29 @@ +CREATE TABLE multipart_uploads ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + bucket_id UUID NOT NULL REFERENCES buckets(id) ON DELETE CASCADE, + key TEXT NOT NULL, + upload_id TEXT NOT NULL, + content_type TEXT NOT NULL DEFAULT 'application/octet-stream', + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); +CREATE UNIQUE INDEX idx_multipart_upload_id ON multipart_uploads (upload_id); +CREATE INDEX idx_multipart_bucket ON multipart_uploads (bucket_id); + +CREATE TABLE multipart_upload_metadata ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + upload_id UUID NOT NULL REFERENCES multipart_uploads(id) ON DELETE CASCADE, + meta_key TEXT NOT NULL, + meta_value TEXT NOT NULL +); +CREATE UNIQUE INDEX idx_mp_meta_key ON multipart_upload_metadata (upload_id, meta_key); + +CREATE TABLE upload_parts ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + upload_id UUID NOT NULL REFERENCES multipart_uploads(id) ON DELETE CASCADE, + part_number INT NOT NULL, + data BYTEA NOT NULL, + size BIGINT NOT NULL, + etag TEXT NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); +CREATE UNIQUE INDEX idx_upload_parts_num ON upload_parts (upload_id, part_number); diff --git a/crates/post3-server/src/cli.rs b/crates/post3-server/src/cli.rs index 9a2713e..14206f0 100644 --- a/crates/post3-server/src/cli.rs +++ b/crates/post3-server/src/cli.rs @@ -29,7 +29,7 @@ pub async fn execute() -> anyhow::Result<()> { std::env::var("DATABASE_URL").context("DATABASE_URL not set")?; let pool = PgPool::connect(&database_url).await?; - post3::MIGRATOR.run(&pool).await?; + sqlx::migrate!("./migrations").run(&pool).await?; tracing::info!("database migrations applied"); diff --git a/crates/post3-server/tests/common/mod.rs b/crates/post3-server/tests/common/mod.rs index ce300a0..85b96b0 100644 --- a/crates/post3-server/tests/common/mod.rs +++ b/crates/post3-server/tests/common/mod.rs @@ -44,7 +44,7 @@ impl TestServer { .unwrap(); // Run migrations - post3::MIGRATOR.run(&pool).await.unwrap(); + sqlx::migrate!("./migrations").run(&pool).await.unwrap(); // Clean slate sqlx::query("DELETE FROM upload_parts").execute(&pool).await.unwrap();