feat(db): add sqlx migrate feature and renumber migration files
- Add 'migrate' feature to sqlx dependency - Renumber migrations to fix duplicate version numbers (two 014 files) - Re-sequence migrations 009-012 for continuous ordering - Add ALTER TABLE ADD COLUMN IF NOT EXISTS baseline for notification table to handle existing databases missing newer columns - Remove deleted IM migration files (009-012) that were superseded
This commit is contained in:
Generated
+65
-277
@@ -359,12 +359,12 @@ dependencies = [
|
||||
"dotenvy",
|
||||
"etcd-client",
|
||||
"futures-util",
|
||||
"hex",
|
||||
"hkdf 0.12.4",
|
||||
"hmac 0.12.1",
|
||||
"object_store",
|
||||
"prost 0.14.3",
|
||||
"prost-types 0.14.3",
|
||||
"r2d2",
|
||||
"prost",
|
||||
"prost-types",
|
||||
"rand 0.8.6",
|
||||
"redis",
|
||||
"reqwest 0.13.4",
|
||||
@@ -377,7 +377,7 @@ dependencies = [
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tonic 0.14.6",
|
||||
"tonic",
|
||||
"tonic-prost",
|
||||
"tonic-prost-build",
|
||||
"tracing",
|
||||
@@ -402,6 +402,15 @@ version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
version = "1.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a3a1fd6f75306b68087b831f025c712524bcb19aad54e557b1129cfa0a2b207"
|
||||
dependencies = [
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arcstr"
|
||||
version = "1.2.0"
|
||||
@@ -493,28 +502,6 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-stream"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476"
|
||||
dependencies = [
|
||||
"async-stream-impl",
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-stream-impl"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.89"
|
||||
@@ -612,74 +599,27 @@ dependencies = [
|
||||
"fs_extra",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
version = "0.7.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum-core 0.4.5",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http 1.4.1",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"itoa",
|
||||
"matchit 0.7.3",
|
||||
"memchr",
|
||||
"mime",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rustversion",
|
||||
"serde",
|
||||
"sync_wrapper",
|
||||
"tower 0.5.3",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90"
|
||||
dependencies = [
|
||||
"axum-core 0.5.6",
|
||||
"axum-core",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http 1.4.1",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"itoa",
|
||||
"matchit 0.8.4",
|
||||
"matchit",
|
||||
"memchr",
|
||||
"mime",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"serde_core",
|
||||
"sync_wrapper",
|
||||
"tower 0.5.3",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-core"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http 1.4.1",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"mime",
|
||||
"pin-project-lite",
|
||||
"rustversion",
|
||||
"sync_wrapper",
|
||||
"tower",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
@@ -702,6 +642,15 @@ dependencies = [
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backon"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cffb0e931875b666fc4fcb20fee52e9bbd1ef836fd9e9e04ec21555f9f85f7ef"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.20.0"
|
||||
@@ -1441,17 +1390,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "etcd-client"
|
||||
version = "0.14.1"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc0452bcc559431b16f472b7ab86e2f9ccd5f3c2da3795afbd6b773665e047fe"
|
||||
checksum = "5ed900ba953ca6bf1fadb75e0c6b73d8463b9e2bb6bdb7b4573e8e7295852fbe"
|
||||
dependencies = [
|
||||
"http 1.4.1",
|
||||
"prost 0.13.5",
|
||||
"prost",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tonic 0.12.3",
|
||||
"tonic-build 0.12.3",
|
||||
"tower 0.4.13",
|
||||
"tonic",
|
||||
"tonic-build",
|
||||
"tonic-prost",
|
||||
"tonic-prost-build",
|
||||
"tower",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
@@ -1868,7 +1819,7 @@ dependencies = [
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"http 0.2.12",
|
||||
"indexmap 2.14.0",
|
||||
"indexmap",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
@@ -1887,7 +1838,7 @@ dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"http 1.4.1",
|
||||
"indexmap 2.14.0",
|
||||
"indexmap",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
@@ -1905,12 +1856,6 @@ dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
@@ -2351,16 +2296,6 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown 0.12.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.14.0"
|
||||
@@ -2603,12 +2538,6 @@ version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
||||
|
||||
[[package]]
|
||||
name = "matchit"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
|
||||
|
||||
[[package]]
|
||||
name = "matchit"
|
||||
version = "0.8.4"
|
||||
@@ -3045,16 +2974,6 @@ version = "2.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"indexmap 2.14.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.8.3"
|
||||
@@ -3063,7 +2982,7 @@ checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"hashbrown 0.15.5",
|
||||
"indexmap 2.14.0",
|
||||
"indexmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3232,16 +3151,6 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost"
|
||||
version = "0.13.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"prost-derive 0.13.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost"
|
||||
version = "0.14.3"
|
||||
@@ -3249,27 +3158,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"prost-derive 0.14.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-build"
|
||||
version = "0.13.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"itertools",
|
||||
"log",
|
||||
"multimap",
|
||||
"once_cell",
|
||||
"petgraph 0.7.1",
|
||||
"prettyplease",
|
||||
"prost 0.13.5",
|
||||
"prost-types 0.13.5",
|
||||
"regex",
|
||||
"syn",
|
||||
"tempfile",
|
||||
"prost-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3282,10 +3171,10 @@ dependencies = [
|
||||
"itertools",
|
||||
"log",
|
||||
"multimap",
|
||||
"petgraph 0.8.3",
|
||||
"petgraph",
|
||||
"prettyplease",
|
||||
"prost 0.14.3",
|
||||
"prost-types 0.14.3",
|
||||
"prost",
|
||||
"prost-types",
|
||||
"pulldown-cmark",
|
||||
"pulldown-cmark-to-cmark",
|
||||
"regex",
|
||||
@@ -3293,19 +3182,6 @@ dependencies = [
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-derive"
|
||||
version = "0.13.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-derive"
|
||||
version = "0.14.3"
|
||||
@@ -3319,22 +3195,13 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-types"
|
||||
version = "0.13.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16"
|
||||
dependencies = [
|
||||
"prost 0.13.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-types"
|
||||
version = "0.14.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8991c4cbdb8bc5b11f0b074ffe286c30e523de90fee5ba8132f1399f23cb3dd7"
|
||||
dependencies = [
|
||||
"prost 0.14.3",
|
||||
"prost",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3465,17 +3332,6 @@ version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
|
||||
|
||||
[[package]]
|
||||
name = "r2d2"
|
||||
version = "0.8.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93"
|
||||
dependencies = [
|
||||
"log",
|
||||
"parking_lot",
|
||||
"scheduled-thread-pool",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.6"
|
||||
@@ -3644,19 +3500,21 @@ version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a12e6b5f4d8ef33944e833e2b1859ad478deab6e431d7337b30ee2efe21f7543"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"arcstr",
|
||||
"async-lock",
|
||||
"backon",
|
||||
"bytes",
|
||||
"cfg-if",
|
||||
"combine",
|
||||
"crc16",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"itoa",
|
||||
"log",
|
||||
"num-bigint",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"r2d2",
|
||||
"rand 0.9.4",
|
||||
"ryu",
|
||||
"sha1_smol",
|
||||
@@ -3743,7 +3601,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tokio-util",
|
||||
"tower 0.5.3",
|
||||
"tower",
|
||||
"tower-http",
|
||||
"tower-service",
|
||||
"url",
|
||||
@@ -3784,7 +3642,7 @@ dependencies = [
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tower 0.5.3",
|
||||
"tower",
|
||||
"tower-http",
|
||||
"tower-service",
|
||||
"url",
|
||||
@@ -3891,6 +3749,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b"
|
||||
dependencies = [
|
||||
"aws-lc-rs",
|
||||
"log",
|
||||
"once_cell",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
@@ -3999,15 +3858,6 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scheduled-thread-pool"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19"
|
||||
dependencies = [
|
||||
"parking_lot",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
@@ -4355,7 +4205,7 @@ dependencies = [
|
||||
"futures-util",
|
||||
"hashbrown 0.16.1",
|
||||
"hashlink",
|
||||
"indexmap 2.14.0",
|
||||
"indexmap",
|
||||
"log",
|
||||
"memchr",
|
||||
"percent-encoding",
|
||||
@@ -4783,36 +4633,6 @@ dependencies = [
|
||||
"webpki-roots 0.26.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tonic"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52"
|
||||
dependencies = [
|
||||
"async-stream",
|
||||
"async-trait",
|
||||
"axum 0.7.9",
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"h2 0.4.14",
|
||||
"http 1.4.1",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-timeout",
|
||||
"hyper-util",
|
||||
"percent-encoding",
|
||||
"pin-project",
|
||||
"prost 0.13.5",
|
||||
"socket2 0.5.10",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tower 0.4.13",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tonic"
|
||||
version = "0.14.6"
|
||||
@@ -4820,7 +4640,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum 0.8.9",
|
||||
"axum",
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"h2 0.4.14",
|
||||
@@ -4835,27 +4655,14 @@ dependencies = [
|
||||
"socket2 0.6.4",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tokio-stream",
|
||||
"tower 0.5.3",
|
||||
"tower",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tonic-build"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9557ce109ea773b399c9b9e5dca39294110b74f1f342cb347a80d1fce8c26a11"
|
||||
dependencies = [
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"prost-build 0.13.5",
|
||||
"prost-types 0.13.5",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tonic-build"
|
||||
version = "0.14.6"
|
||||
@@ -4875,8 +4682,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50849f68853be452acf590cde0b146665b8d507b3b8af17261df47e02c209ea0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"prost 0.14.3",
|
||||
"tonic 0.14.6",
|
||||
"prost",
|
||||
"tonic",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4887,32 +4694,12 @@ checksum = "654e5643eff75d7f8c99197ce1440ed19a3474eada74c12bbac488b2cafdae27"
|
||||
dependencies = [
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"prost-build 0.14.3",
|
||||
"prost-types 0.14.3",
|
||||
"prost-build",
|
||||
"prost-types",
|
||||
"quote",
|
||||
"syn",
|
||||
"tempfile",
|
||||
"tonic-build 0.14.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"indexmap 1.9.3",
|
||||
"pin-project",
|
||||
"pin-project-lite",
|
||||
"rand 0.8.6",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
"tonic-build",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4923,7 +4710,7 @@ checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"indexmap 2.14.0",
|
||||
"indexmap",
|
||||
"pin-project-lite",
|
||||
"slab",
|
||||
"sync_wrapper",
|
||||
@@ -4946,7 +4733,7 @@ dependencies = [
|
||||
"http 1.4.1",
|
||||
"http-body",
|
||||
"pin-project-lite",
|
||||
"tower 0.5.3",
|
||||
"tower",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"url",
|
||||
@@ -5145,7 +4932,7 @@ version = "5.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8bde15df68e80b16c7d16b9616e80770ad158988daa56a27dccd1e55558b0160"
|
||||
dependencies = [
|
||||
"indexmap 2.14.0",
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"utoipa-gen",
|
||||
@@ -5173,6 +4960,7 @@ dependencies = [
|
||||
"getrandom 0.4.2",
|
||||
"js-sys",
|
||||
"serde_core",
|
||||
"sha1_smol",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
@@ -5320,7 +5108,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap 2.14.0",
|
||||
"indexmap",
|
||||
"wasm-encoder",
|
||||
"wasmparser",
|
||||
]
|
||||
@@ -5346,7 +5134,7 @@ checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"hashbrown 0.15.5",
|
||||
"indexmap 2.14.0",
|
||||
"indexmap",
|
||||
"semver",
|
||||
]
|
||||
|
||||
@@ -5688,7 +5476,7 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"indexmap 2.14.0",
|
||||
"indexmap",
|
||||
"prettyplease",
|
||||
"syn",
|
||||
"wasm-metadata",
|
||||
@@ -5719,7 +5507,7 @@ checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
"indexmap 2.14.0",
|
||||
"indexmap",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
@@ -5738,7 +5526,7 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"id-arena",
|
||||
"indexmap 2.14.0",
|
||||
"indexmap",
|
||||
"log",
|
||||
"semver",
|
||||
"serde",
|
||||
|
||||
+5
-5
@@ -16,19 +16,18 @@ path = "main.rs"
|
||||
name = "gen_openapi"
|
||||
path = "gen_openapi.rs"
|
||||
[dependencies]
|
||||
sqlx = { version = "0.9.0", features = ["postgres","runtime-tokio","chrono","uuid","json"] }
|
||||
sqlx = { version = "0.9.0", features = ["postgres","runtime-tokio","chrono","uuid","json","migrate"] }
|
||||
tokio = { version = "1.52.3", features = ["full"] }
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
serde_json = { version = "1.0.150", features = [] }
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
uuid = { version = "1.23.1", features = ["serde","v4","v7"] }
|
||||
uuid = { version = "1.23.1", features = ["serde","v4","v7","v5"] }
|
||||
reqwest = { version = "0.13.4", features = ["json"] }
|
||||
tracing = { version = "0.1.44", features = [] }
|
||||
tracing-subscriber = { version = "0.3.23", features = ["fmt"] }
|
||||
dotenvy = "0.15.7"
|
||||
thiserror = "2"
|
||||
redis = { version = "1.2.1", features = ["cluster","cluster-async","aio","tokio-comp","r2d2"] }
|
||||
r2d2 = { version = "0.8.10", features = [] }
|
||||
redis = { version = "1.2.1", features = ["cluster","cluster-async","aio","tokio-comp","connection-manager"] }
|
||||
dashmap = "6.1"
|
||||
object_store = { version = "0.13.2", features = ["tokio","aws","cloud"] }
|
||||
argon2 = "0.5"
|
||||
@@ -46,13 +45,14 @@ prost = "0.14.3"
|
||||
prost-types = "0.14.3"
|
||||
tonic-prost = "0.14.6"
|
||||
url = "2.5"
|
||||
etcd-client = "0.14"
|
||||
etcd-client = { version = "0.18.0", features = ["tls"] }
|
||||
tokio-stream = "0.1"
|
||||
async-nats = "0.49"
|
||||
futures-util = "0.3"
|
||||
utoipa = { version = "5.5.0", features = ["uuid","chrono","actix_extras","decimal","macros"]}
|
||||
actix-web = { version = "4", features = ["secure-cookies"] }
|
||||
actix-multipart = "0.7"
|
||||
hex = "0.4.3"
|
||||
|
||||
[build-dependencies]
|
||||
tonic-prost-build = "0.14.6"
|
||||
|
||||
@@ -1905,6 +1905,17 @@ CREATE TABLE IF NOT EXISTS notification (
|
||||
deleted_at TIMESTAMPTZ NULL
|
||||
|
||||
);
|
||||
ALTER TABLE notification ADD COLUMN IF NOT EXISTS repo_id UUID NULL REFERENCES repo(id) ON DELETE CASCADE;
|
||||
ALTER TABLE notification ADD COLUMN IF NOT EXISTS issue_id UUID NULL REFERENCES issue(id) ON DELETE CASCADE;
|
||||
ALTER TABLE notification ADD COLUMN IF NOT EXISTS pull_request_id UUID NULL REFERENCES pull_request(id) ON DELETE CASCADE;
|
||||
ALTER TABLE notification ADD COLUMN IF NOT EXISTS channel_id UUID NULL REFERENCES channel(id) ON DELETE CASCADE;
|
||||
ALTER TABLE notification ADD COLUMN IF NOT EXISTS message_id UUID NULL REFERENCES message(id) ON DELETE CASCADE;
|
||||
ALTER TABLE notification ADD COLUMN IF NOT EXISTS target_type TEXT NULL;
|
||||
ALTER TABLE notification ADD COLUMN IF NOT EXISTS target_id UUID NULL;
|
||||
ALTER TABLE notification ADD COLUMN IF NOT EXISTS action_url TEXT NULL;
|
||||
ALTER TABLE notification ADD COLUMN IF NOT EXISTS priority TEXT NOT NULL DEFAULT 'normal';
|
||||
ALTER TABLE notification ADD COLUMN IF NOT EXISTS metadata JSONB NULL;
|
||||
ALTER TABLE notification ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_notification_user_id ON notification (user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_notification_actor_id ON notification (actor_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_notification_workspace_id ON notification (workspace_id);
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
-- 013: Fix record_workspace_audit() for tables without 'id' column.
|
||||
-- workspace_billing, workspace_custom_branding, workspace_settings
|
||||
-- use workspace_id as their PRIMARY KEY and have no separate id column.
|
||||
-- The original function unconditionally referenced NEW.id, which crashed
|
||||
-- with "record 'new' has no field 'id'" (42703).
|
||||
|
||||
CREATE OR REPLACE FUNCTION record_workspace_audit()
|
||||
RETURNS TRIGGER AS $$
|
||||
DECLARE
|
||||
ws_id UUID;
|
||||
actor UUID := app_current_user_id();
|
||||
action_text TEXT;
|
||||
target_id_val UUID;
|
||||
BEGIN
|
||||
ws_id := COALESCE(NEW.workspace_id, OLD.workspace_id);
|
||||
|
||||
IF ws_id IS NULL THEN
|
||||
RETURN COALESCE(NEW, OLD);
|
||||
END IF;
|
||||
|
||||
action_text := CASE TG_OP
|
||||
WHEN 'INSERT' THEN 'created'
|
||||
WHEN 'UPDATE' THEN 'updated'
|
||||
WHEN 'DELETE' THEN 'deleted'
|
||||
END;
|
||||
|
||||
-- Attempt to read NEW.id / OLD.id. For tables whose PK is workspace_id
|
||||
-- there is no id column; catch the error and use ws_id instead.
|
||||
BEGIN
|
||||
target_id_val := COALESCE(NEW.id, OLD.id);
|
||||
EXCEPTION
|
||||
WHEN undefined_column THEN
|
||||
target_id_val := ws_id;
|
||||
END;
|
||||
|
||||
INSERT INTO workspace_audit_log (workspace_id, actor_id, action, target_type, target_id, created_at)
|
||||
VALUES (ws_id, actor, action_text, TG_TABLE_NAME, target_id_val, NOW());
|
||||
|
||||
RETURN COALESCE(NEW, OLD);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
@@ -1,320 +0,0 @@
|
||||
-- 009: IM Features — Discord/Slack-class messaging support
|
||||
--
|
||||
-- New tables:
|
||||
-- user_presence, user_activity,
|
||||
-- channel_category, channel_permission_overwrite, im_integration,
|
||||
-- message_attachment, message_embed, message_draft, message_pin,
|
||||
-- message_edit_history, saved_message, thread_read_state,
|
||||
-- custom_emoji
|
||||
|
||||
-- ============================================================
|
||||
-- 1. User Presence
|
||||
-- ============================================================
|
||||
|
||||
-- models/users/user_presence.rs → user_presence
|
||||
CREATE TABLE IF NOT EXISTS user_presence (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
status TEXT NOT NULL,
|
||||
custom_status_text TEXT NULL,
|
||||
custom_status_emoji TEXT NULL,
|
||||
device_type TEXT NULL,
|
||||
ip_address TEXT NULL,
|
||||
last_active_at TIMESTAMPTZ NOT NULL,
|
||||
last_seen_at TIMESTAMPTZ NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT uq_user_presence_user_id UNIQUE (user_id)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_presence_status ON user_presence (status);
|
||||
|
||||
-- ============================================================
|
||||
-- 2. User Activity (Rich Presence)
|
||||
-- ============================================================
|
||||
|
||||
-- models/users/user_activity.rs → user_activity
|
||||
CREATE TABLE IF NOT EXISTS user_activity (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
activity_type TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
details TEXT NULL,
|
||||
state TEXT NULL,
|
||||
application_id TEXT NULL,
|
||||
assets JSONB NULL,
|
||||
party_id TEXT NULL,
|
||||
party_current_size INTEGER NULL,
|
||||
party_max_size INTEGER NULL,
|
||||
large_image_url TEXT NULL,
|
||||
small_image_url TEXT NULL,
|
||||
start_at TIMESTAMPTZ NULL,
|
||||
end_at TIMESTAMPTZ NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_activity_user_id ON user_activity (user_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 3. Channel Categories
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/channel_categories.rs → channel_category
|
||||
CREATE TABLE IF NOT EXISTS channel_category (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
workspace_id UUID NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
position INTEGER NOT NULL,
|
||||
collapsed BOOLEAN NOT NULL,
|
||||
created_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_channel_category_workspace_id ON channel_category (workspace_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 4. ALTER channel — add category_id (after channel_category exists)
|
||||
-- ============================================================
|
||||
|
||||
ALTER TABLE channel ADD COLUMN IF NOT EXISTS category_id UUID NULL REFERENCES channel_category(id) ON DELETE SET NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_channel_category_id ON channel (category_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 5. Channel Permission Overwrites
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/channel_permission_overwrites.rs → channel_permission_overwrite
|
||||
CREATE TABLE IF NOT EXISTS channel_permission_overwrite (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
||||
target_type TEXT NOT NULL,
|
||||
target_id UUID NOT NULL,
|
||||
allow TEXT[] NOT NULL,
|
||||
deny TEXT[] NOT NULL,
|
||||
created_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT uq_channel_perm_overwrite UNIQUE (channel_id, target_type, target_id)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_channel_perm_overwrite_channel_id ON channel_permission_overwrite (channel_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_channel_perm_overwrite_target ON channel_permission_overwrite (target_type, target_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 6. IM Integrations (External Bridge)
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/im_integrations.rs → im_integration
|
||||
CREATE TABLE IF NOT EXISTS im_integration (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
workspace_id UUID NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
||||
provider TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
external_workspace_id TEXT NULL,
|
||||
internal_channel_id UUID NULL REFERENCES channel(id) ON DELETE SET NULL,
|
||||
external_channel_id TEXT NULL,
|
||||
bot_token_ciphertext TEXT NULL,
|
||||
webhook_url TEXT NULL,
|
||||
sync_direction TEXT NOT NULL,
|
||||
user_mapping JSONB NULL,
|
||||
enabled BOOLEAN NOT NULL,
|
||||
installed_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
last_sync_at TIMESTAMPTZ NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_im_integration_workspace_id ON im_integration (workspace_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_im_integration_internal_channel_id ON im_integration (internal_channel_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_im_integration_provider ON im_integration (provider);
|
||||
|
||||
-- ============================================================
|
||||
-- 7. Message Attachments
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/message_attachments.rs → message_attachment
|
||||
CREATE TABLE IF NOT EXISTS message_attachment (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
message_id UUID NOT NULL REFERENCES message(id) ON DELETE CASCADE,
|
||||
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
||||
filename TEXT NOT NULL,
|
||||
url TEXT NOT NULL,
|
||||
proxy_url TEXT NULL,
|
||||
size_bytes BIGINT NOT NULL,
|
||||
mime_type TEXT NOT NULL,
|
||||
width INTEGER NULL,
|
||||
height INTEGER NULL,
|
||||
duration_ms BIGINT NULL,
|
||||
thumbnail_url TEXT NULL,
|
||||
blurhash TEXT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_message_attachment_message_id ON message_attachment (message_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_message_attachment_channel_id ON message_attachment (channel_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 8. Message Embeds (Rich Text / Link Previews)
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/message_embeds.rs → message_embed
|
||||
CREATE TABLE IF NOT EXISTS message_embed (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
message_id UUID NOT NULL REFERENCES message(id) ON DELETE CASCADE,
|
||||
embed_type TEXT NOT NULL,
|
||||
title TEXT NULL,
|
||||
description TEXT NULL,
|
||||
url TEXT NULL,
|
||||
author_name TEXT NULL,
|
||||
author_url TEXT NULL,
|
||||
author_icon_url TEXT NULL,
|
||||
thumbnail_url TEXT NULL,
|
||||
thumbnail_width INTEGER NULL,
|
||||
thumbnail_height INTEGER NULL,
|
||||
image_url TEXT NULL,
|
||||
image_width INTEGER NULL,
|
||||
image_height INTEGER NULL,
|
||||
video_url TEXT NULL,
|
||||
video_width INTEGER NULL,
|
||||
video_height INTEGER NULL,
|
||||
color INTEGER NULL,
|
||||
fields JSONB NULL,
|
||||
footer_text TEXT NULL,
|
||||
footer_icon_url TEXT NULL,
|
||||
provider_name TEXT NULL,
|
||||
provider_url TEXT NULL,
|
||||
"timestamp" TIMESTAMPTZ NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_message_embed_message_id ON message_embed (message_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 9. Message Drafts
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/message_drafts.rs → message_draft
|
||||
CREATE TABLE IF NOT EXISTS message_draft (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
||||
thread_id UUID NULL,
|
||||
reply_to_message_id UUID NULL,
|
||||
content TEXT NOT NULL,
|
||||
attachments JSONB NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT uq_message_draft_user_channel UNIQUE (user_id, channel_id)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_message_draft_user_id ON message_draft (user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_message_draft_channel_id ON message_draft (channel_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 10. Custom Emojis
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/custom_emojis.rs → custom_emoji
|
||||
CREATE TABLE IF NOT EXISTS custom_emoji (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
workspace_id UUID NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
url TEXT NOT NULL,
|
||||
animated BOOLEAN NOT NULL,
|
||||
managed BOOLEAN NOT NULL,
|
||||
created_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT uq_custom_emoji_workspace_name UNIQUE (workspace_id, name)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_custom_emoji_workspace_id ON custom_emoji (workspace_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 11. Message Pins
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/message_pins.rs → message_pin
|
||||
CREATE TABLE IF NOT EXISTS message_pin (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
message_id UUID NOT NULL REFERENCES message(id) ON DELETE CASCADE,
|
||||
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
||||
pinned_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
pinned_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT uq_message_pin_message_id UNIQUE (message_id)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_message_pin_channel_id ON message_pin (channel_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_message_pin_message_id ON message_pin (message_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 12. Message Edit History
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/message_edit_history.rs → message_edit_history
|
||||
CREATE TABLE IF NOT EXISTS message_edit_history (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
message_id UUID NOT NULL REFERENCES message(id) ON DELETE CASCADE,
|
||||
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
||||
previous_body TEXT NOT NULL,
|
||||
edited_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
edited_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_message_edit_history_message_id ON message_edit_history (message_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_message_edit_history_channel_id ON message_edit_history (channel_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 13. Saved Messages
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/saved_messages.rs → saved_message
|
||||
CREATE TABLE IF NOT EXISTS saved_message (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
message_id UUID NOT NULL REFERENCES message(id) ON DELETE CASCADE,
|
||||
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
||||
note TEXT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT uq_saved_message_user_message UNIQUE (user_id, message_id)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_saved_message_user_id ON saved_message (user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_saved_message_message_id ON saved_message (message_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 14. Thread Read States
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/thread_read_states.rs → thread_read_state
|
||||
CREATE TABLE IF NOT EXISTS thread_read_state (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
thread_id UUID NOT NULL,
|
||||
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
||||
last_read_message_id UUID NULL,
|
||||
last_read_at TIMESTAMPTZ NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT uq_thread_read_state_user_thread UNIQUE (user_id, thread_id)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_thread_read_state_user_id ON thread_read_state (user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_thread_read_state_thread_id ON thread_read_state (thread_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_thread_read_state_channel_id ON thread_read_state (channel_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 15. Triggers — auto-refresh updated_at
|
||||
-- ============================================================
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_user_presence_updated_at ON user_presence;
|
||||
CREATE TRIGGER trg_user_presence_updated_at BEFORE UPDATE ON user_presence FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_user_activity_updated_at ON user_activity;
|
||||
CREATE TRIGGER trg_user_activity_updated_at BEFORE UPDATE ON user_activity FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_channel_category_updated_at ON channel_category;
|
||||
CREATE TRIGGER trg_channel_category_updated_at BEFORE UPDATE ON channel_category FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_channel_permission_overwrite_updated_at ON channel_permission_overwrite;
|
||||
CREATE TRIGGER trg_channel_permission_overwrite_updated_at BEFORE UPDATE ON channel_permission_overwrite FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_im_integration_updated_at ON im_integration;
|
||||
CREATE TRIGGER trg_im_integration_updated_at BEFORE UPDATE ON im_integration FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_message_draft_updated_at ON message_draft;
|
||||
CREATE TRIGGER trg_message_draft_updated_at BEFORE UPDATE ON message_draft FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_custom_emoji_updated_at ON custom_emoji;
|
||||
CREATE TRIGGER trg_custom_emoji_updated_at BEFORE UPDATE ON custom_emoji FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_thread_read_state_updated_at ON thread_read_state;
|
||||
CREATE TRIGGER trg_thread_read_state_updated_at BEFORE UPDATE ON thread_read_state FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
@@ -0,0 +1,24 @@
|
||||
-- Add deleted_at columns to user-related tables for soft-delete support.
|
||||
-- Previously, user_delete_account hard-deleted rows from these tables.
|
||||
-- After this migration, all deletions are soft (mark as deleted).
|
||||
|
||||
ALTER TABLE "user" ADD COLUMN IF NOT EXISTS restore_token_hash VARCHAR(64);
|
||||
ALTER TABLE "user" ADD COLUMN IF NOT EXISTS restore_token_expires_at TIMESTAMPTZ;
|
||||
|
||||
ALTER TABLE user_2fa ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;
|
||||
ALTER TABLE user_activity ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;
|
||||
ALTER TABLE user_appearance ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;
|
||||
ALTER TABLE user_block ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;
|
||||
ALTER TABLE user_device ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;
|
||||
ALTER TABLE user_follow ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;
|
||||
ALTER TABLE user_mail ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;
|
||||
ALTER TABLE user_notify_setting ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;
|
||||
ALTER TABLE user_oauth ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;
|
||||
ALTER TABLE user_password ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;
|
||||
ALTER TABLE user_password_reset ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;
|
||||
ALTER TABLE user_presence ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;
|
||||
ALTER TABLE user_profile ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;
|
||||
ALTER TABLE user_security_log ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_user_security_log_user_deleted ON user_security_log(user_id, deleted_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_activity_user_deleted ON user_activity(user_id, deleted_at);
|
||||
@@ -1,187 +0,0 @@
|
||||
-- 010: Channel Kinds — text, voice, stage, forum, announcement
|
||||
--
|
||||
-- ALTER:
|
||||
-- channel — add channel_kind + voice/forum/stage fields
|
||||
-- message_thread — add forum post fields (title, tags, pinned, locked)
|
||||
--
|
||||
-- New tables:
|
||||
-- forum_tag, voice_participant, stage,
|
||||
-- message_poll, message_poll_option, message_poll_vote
|
||||
|
||||
-- ============================================================
|
||||
-- 1. ALTER channel — add channel_kind + voice/forum/stage fields
|
||||
-- ============================================================
|
||||
|
||||
ALTER TABLE channel ADD COLUMN IF NOT EXISTS channel_kind TEXT NOT NULL DEFAULT 'text';
|
||||
ALTER TABLE channel ADD COLUMN IF NOT EXISTS position INTEGER NULL;
|
||||
ALTER TABLE channel ADD COLUMN IF NOT EXISTS nsfw BOOLEAN NOT NULL DEFAULT FALSE;
|
||||
|
||||
-- Voice / Stage specific
|
||||
ALTER TABLE channel ADD COLUMN IF NOT EXISTS bitrate INTEGER NULL;
|
||||
ALTER TABLE channel ADD COLUMN IF NOT EXISTS user_limit INTEGER NULL;
|
||||
ALTER TABLE channel ADD COLUMN IF NOT EXISTS rtc_region TEXT NULL;
|
||||
|
||||
-- Forum specific
|
||||
ALTER TABLE channel ADD COLUMN IF NOT EXISTS default_auto_archive_duration INTEGER NULL;
|
||||
ALTER TABLE channel ADD COLUMN IF NOT EXISTS default_reaction_emoji TEXT NULL;
|
||||
ALTER TABLE channel ADD COLUMN IF NOT EXISTS default_sort_order TEXT NULL;
|
||||
ALTER TABLE channel ADD COLUMN IF NOT EXISTS default_forum_layout TEXT NULL;
|
||||
ALTER TABLE channel ADD COLUMN IF NOT EXISTS require_tag BOOLEAN NULL;
|
||||
ALTER TABLE channel ADD COLUMN IF NOT EXISTS available_tags JSONB NULL;
|
||||
ALTER TABLE channel ADD COLUMN IF NOT EXISTS default_thread_rate_limit INTEGER NULL;
|
||||
|
||||
-- General
|
||||
ALTER TABLE channel ADD COLUMN IF NOT EXISTS rate_limit_per_user INTEGER NULL;
|
||||
ALTER TABLE channel ADD COLUMN IF NOT EXISTS parent_channel_id UUID NULL REFERENCES channel(id) ON DELETE SET NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_channel_channel_kind ON channel (channel_kind);
|
||||
CREATE INDEX IF NOT EXISTS idx_channel_parent_channel_id ON channel (parent_channel_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 2. ALTER message_thread — add forum post fields
|
||||
-- ============================================================
|
||||
|
||||
ALTER TABLE message_thread ADD COLUMN IF NOT EXISTS title TEXT NULL;
|
||||
ALTER TABLE message_thread ADD COLUMN IF NOT EXISTS tags TEXT[] NOT NULL DEFAULT '{}';
|
||||
ALTER TABLE message_thread ADD COLUMN IF NOT EXISTS pinned BOOLEAN NOT NULL DEFAULT FALSE;
|
||||
ALTER TABLE message_thread ADD COLUMN IF NOT EXISTS locked BOOLEAN NOT NULL DEFAULT FALSE;
|
||||
ALTER TABLE message_thread ADD COLUMN IF NOT EXISTS rate_limit_per_user INTEGER NULL;
|
||||
ALTER TABLE message_thread ADD COLUMN IF NOT EXISTS auto_archive_at TIMESTAMPTZ NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_message_thread_pinned ON message_thread (pinned) WHERE pinned;
|
||||
|
||||
-- ============================================================
|
||||
-- 3. Forum Tags
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/forum_tags.rs → forum_tag
|
||||
CREATE TABLE IF NOT EXISTS forum_tag (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
emoji_id TEXT NULL,
|
||||
emoji_name TEXT NULL,
|
||||
moderated BOOLEAN NOT NULL,
|
||||
position INTEGER NOT NULL,
|
||||
created_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT uq_forum_tag_channel_name UNIQUE (channel_id, name)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_forum_tag_channel_id ON forum_tag (channel_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 4. Voice Participants
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/voice_participants.rs → voice_participant
|
||||
CREATE TABLE IF NOT EXISTS voice_participant (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
||||
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
session_id TEXT NULL,
|
||||
deafened BOOLEAN NOT NULL,
|
||||
muted BOOLEAN NOT NULL,
|
||||
self_deafened BOOLEAN NOT NULL,
|
||||
self_muted BOOLEAN NOT NULL,
|
||||
self_video BOOLEAN NOT NULL,
|
||||
streaming BOOLEAN NOT NULL,
|
||||
speaking BOOLEAN NOT NULL,
|
||||
joined_at TIMESTAMPTZ NOT NULL,
|
||||
left_at TIMESTAMPTZ NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_voice_participant_channel_id ON voice_participant (channel_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_voice_participant_user_id ON voice_participant (user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_voice_participant_channel_user ON voice_participant (channel_id, user_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 5. Stages
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/stages.rs → stage
|
||||
CREATE TABLE IF NOT EXISTS stage (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
||||
topic TEXT NOT NULL,
|
||||
privacy_level TEXT NOT NULL,
|
||||
discoverable BOOLEAN NOT NULL,
|
||||
started_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
started_at TIMESTAMPTZ NOT NULL,
|
||||
ended_at TIMESTAMPTZ NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT uq_stage_active_channel UNIQUE (channel_id, ended_at)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_stage_channel_id ON stage (channel_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_stage_channel_active ON stage (channel_id) WHERE ended_at IS NULL;
|
||||
|
||||
-- ============================================================
|
||||
-- 6. Message Polls
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/message_polls.rs → message_poll
|
||||
CREATE TABLE IF NOT EXISTS message_poll (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
message_id UUID NOT NULL REFERENCES message(id) ON DELETE CASCADE,
|
||||
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
||||
question TEXT NOT NULL,
|
||||
description TEXT NULL,
|
||||
layout TEXT NOT NULL,
|
||||
allow_multiselect BOOLEAN NOT NULL,
|
||||
duration_hours INTEGER NULL,
|
||||
ends_at TIMESTAMPTZ NULL,
|
||||
total_votes BIGINT NOT NULL,
|
||||
metadata JSONB NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT uq_message_poll_message_id UNIQUE (message_id)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_message_poll_channel_id ON message_poll (channel_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_message_poll_ends_at ON message_poll (ends_at) WHERE ends_at IS NOT NULL;
|
||||
|
||||
-- ============================================================
|
||||
-- 7. Message Poll Options
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/message_poll_options.rs → message_poll_option
|
||||
CREATE TABLE IF NOT EXISTS message_poll_option (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
poll_id UUID NOT NULL REFERENCES message_poll(id) ON DELETE CASCADE,
|
||||
position INTEGER NOT NULL,
|
||||
text TEXT NOT NULL,
|
||||
emoji_id TEXT NULL,
|
||||
emoji_name TEXT NULL,
|
||||
vote_count BIGINT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_message_poll_option_poll_id ON message_poll_option (poll_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 8. Message Poll Votes
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/message_poll_votes.rs → message_poll_vote
|
||||
CREATE TABLE IF NOT EXISTS message_poll_vote (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
poll_id UUID NOT NULL REFERENCES message_poll(id) ON DELETE CASCADE,
|
||||
option_id UUID NOT NULL REFERENCES message_poll_option(id) ON DELETE CASCADE,
|
||||
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
voted_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT uq_message_poll_vote_user_option UNIQUE (poll_id, option_id, user_id)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_message_poll_vote_poll_id ON message_poll_vote (poll_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_message_poll_vote_user_id ON message_poll_vote (user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_message_poll_vote_option_id ON message_poll_vote (option_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 9. Triggers — auto-refresh updated_at
|
||||
-- ============================================================
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_forum_tag_updated_at ON forum_tag;
|
||||
CREATE TRIGGER trg_forum_tag_updated_at BEFORE UPDATE ON forum_tag FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_stage_updated_at ON stage;
|
||||
CREATE TRIGGER trg_stage_updated_at BEFORE UPDATE ON stage FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_message_poll_updated_at ON message_poll;
|
||||
CREATE TRIGGER trg_message_poll_updated_at BEFORE UPDATE ON message_poll FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
@@ -1,145 +0,0 @@
|
||||
-- 011: Announcement Channel — articles, comments, reactions, follows, cross-posts
|
||||
--
|
||||
-- New tables:
|
||||
-- article, article_comment, article_reaction,
|
||||
-- channel_follow, article_cross_post
|
||||
|
||||
-- ============================================================
|
||||
-- 1. Articles
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/articles.rs → article
|
||||
CREATE TABLE IF NOT EXISTS article (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
||||
author_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
title TEXT NOT NULL,
|
||||
slug TEXT NOT NULL,
|
||||
summary TEXT NULL,
|
||||
body TEXT NOT NULL,
|
||||
cover_image_url TEXT NULL,
|
||||
status TEXT NOT NULL,
|
||||
visibility TEXT NOT NULL,
|
||||
tags TEXT[] NOT NULL,
|
||||
published_at TIMESTAMPTZ NULL,
|
||||
published_by UUID NULL REFERENCES "user"(id) ON DELETE SET NULL,
|
||||
scheduled_at TIMESTAMPTZ NULL,
|
||||
unpublished_at TIMESTAMPTZ NULL,
|
||||
views_count BIGINT NOT NULL DEFAULT 0,
|
||||
comments_count BIGINT NOT NULL DEFAULT 0,
|
||||
reactions_count BIGINT NOT NULL DEFAULT 0,
|
||||
cross_posted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
cross_posted_from UUID NULL REFERENCES article(id) ON DELETE SET NULL,
|
||||
metadata JSONB NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL,
|
||||
deleted_at TIMESTAMPTZ NULL,
|
||||
CONSTRAINT uq_article_channel_slug UNIQUE (channel_id, slug)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_article_channel_id ON article (channel_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_article_author_id ON article (author_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_article_status ON article (status);
|
||||
CREATE INDEX IF NOT EXISTS idx_article_published_at ON article (published_at DESC) WHERE published_at IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_article_cross_posted_from ON article (cross_posted_from) WHERE cross_posted_from IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_article_deleted ON article (deleted_at) WHERE deleted_at IS NOT NULL;
|
||||
|
||||
-- ============================================================
|
||||
-- 2. Article Comments
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/article_comments.rs → article_comment
|
||||
CREATE TABLE IF NOT EXISTS article_comment (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
article_id UUID NOT NULL REFERENCES article(id) ON DELETE CASCADE,
|
||||
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
||||
author_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
parent_comment_id UUID NULL REFERENCES article_comment(id) ON DELETE CASCADE,
|
||||
body TEXT NOT NULL,
|
||||
edited_at TIMESTAMPTZ NULL,
|
||||
deleted_at TIMESTAMPTZ NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_article_comment_article_id ON article_comment (article_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_article_comment_author_id ON article_comment (author_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_article_comment_parent ON article_comment (parent_comment_id) WHERE parent_comment_id IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_article_comment_deleted ON article_comment (deleted_at) WHERE deleted_at IS NOT NULL;
|
||||
|
||||
-- ============================================================
|
||||
-- 3. Article Reactions
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/article_reactions.rs → article_reaction
|
||||
CREATE TABLE IF NOT EXISTS article_reaction (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
article_id UUID NOT NULL REFERENCES article(id) ON DELETE CASCADE,
|
||||
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
||||
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
content TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT uq_article_reaction UNIQUE (article_id, user_id, content)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_article_reaction_article_id ON article_reaction (article_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_article_reaction_user_id ON article_reaction (user_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 4. Channel Follows
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/channel_follows.rs → channel_follow
|
||||
CREATE TABLE IF NOT EXISTS channel_follow (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
source_channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
||||
target_workspace_id UUID NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
||||
target_channel_id UUID NULL REFERENCES channel(id) ON DELETE SET NULL,
|
||||
webhook_url TEXT NULL,
|
||||
webhook_secret_ciphertext TEXT NULL,
|
||||
enabled BOOLEAN NOT NULL,
|
||||
followed_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
unfollowed_at TIMESTAMPTZ NULL,
|
||||
last_delivery_at TIMESTAMPTZ NULL,
|
||||
last_delivery_status TEXT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT uq_channel_follow_source_target UNIQUE (source_channel_id, target_workspace_id, target_channel_id)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_channel_follow_source ON channel_follow (source_channel_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_channel_follow_target_ws ON channel_follow (target_workspace_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_channel_follow_target_channel ON channel_follow (target_channel_id) WHERE target_channel_id IS NOT NULL;
|
||||
|
||||
-- ============================================================
|
||||
-- 5. Article Cross-Posts
|
||||
-- ============================================================
|
||||
|
||||
-- models/channels/article_cross_posts.rs → article_cross_post
|
||||
CREATE TABLE IF NOT EXISTS article_cross_post (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
article_id UUID NOT NULL REFERENCES article(id) ON DELETE CASCADE,
|
||||
follow_id UUID NOT NULL REFERENCES channel_follow(id) ON DELETE CASCADE,
|
||||
target_workspace_id UUID NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
||||
target_channel_id UUID NULL REFERENCES channel(id) ON DELETE SET NULL,
|
||||
status TEXT NOT NULL,
|
||||
attempts INTEGER NOT NULL DEFAULT 0,
|
||||
last_error TEXT NULL,
|
||||
sent_at TIMESTAMPTZ NULL,
|
||||
delivered_at TIMESTAMPTZ NULL,
|
||||
failed_at TIMESTAMPTZ NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_article_cross_post_article_id ON article_cross_post (article_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_article_cross_post_follow_id ON article_cross_post (follow_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_article_cross_post_status ON article_cross_post (status);
|
||||
CREATE INDEX IF NOT EXISTS idx_article_cross_post_target_ws ON article_cross_post (target_workspace_id);
|
||||
|
||||
-- ============================================================
|
||||
-- 6. Triggers — auto-refresh updated_at
|
||||
-- ============================================================
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_article_updated_at ON article;
|
||||
CREATE TRIGGER trg_article_updated_at BEFORE UPDATE ON article FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_article_comment_updated_at ON article_comment;
|
||||
CREATE TRIGGER trg_article_comment_updated_at BEFORE UPDATE ON article_comment FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_channel_follow_updated_at ON channel_follow;
|
||||
CREATE TRIGGER trg_channel_follow_updated_at BEFORE UPDATE ON channel_follow FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
@@ -0,0 +1,13 @@
|
||||
-- Add repository-level feature toggles, merge settings, topics, and homepage.
|
||||
-- models/repos/repo.rs → repo
|
||||
|
||||
ALTER TABLE repo ADD COLUMN IF NOT EXISTS topics TEXT[] NOT NULL DEFAULT '{}';
|
||||
ALTER TABLE repo ADD COLUMN IF NOT EXISTS homepage TEXT;
|
||||
ALTER TABLE repo ADD COLUMN IF NOT EXISTS has_issues BOOLEAN NOT NULL DEFAULT true;
|
||||
ALTER TABLE repo ADD COLUMN IF NOT EXISTS has_wiki BOOLEAN NOT NULL DEFAULT true;
|
||||
ALTER TABLE repo ADD COLUMN IF NOT EXISTS has_pull_requests BOOLEAN NOT NULL DEFAULT true;
|
||||
ALTER TABLE repo ADD COLUMN IF NOT EXISTS allow_forking BOOLEAN NOT NULL DEFAULT true;
|
||||
ALTER TABLE repo ADD COLUMN IF NOT EXISTS allow_merge_commit BOOLEAN NOT NULL DEFAULT true;
|
||||
ALTER TABLE repo ADD COLUMN IF NOT EXISTS allow_squash_merge BOOLEAN NOT NULL DEFAULT true;
|
||||
ALTER TABLE repo ADD COLUMN IF NOT EXISTS allow_rebase_merge BOOLEAN NOT NULL DEFAULT true;
|
||||
ALTER TABLE repo ADD COLUMN IF NOT EXISTS delete_branch_on_merge BOOLEAN NOT NULL DEFAULT false;
|
||||
@@ -1,13 +0,0 @@
|
||||
ALTER TABLE message ADD COLUMN IF NOT EXISTS seq BIGINT NOT NULL DEFAULT 0;
|
||||
|
||||
WITH ranked AS (
|
||||
SELECT id, ROW_NUMBER() OVER (PARTITION BY channel_id ORDER BY created_at ASC, id ASC) AS rn
|
||||
FROM message
|
||||
WHERE seq = 0
|
||||
)
|
||||
UPDATE message m
|
||||
SET seq = ranked.rn
|
||||
FROM ranked
|
||||
WHERE m.id = ranked.id;
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_message_channel_seq ON message (channel_id, seq);
|
||||
@@ -0,0 +1,44 @@
|
||||
-- Release Assets - binary attachments for releases
|
||||
CREATE TABLE IF NOT EXISTS repo_release_asset (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
release_id UUID NOT NULL REFERENCES repo_release(id) ON DELETE CASCADE,
|
||||
filename TEXT NOT NULL,
|
||||
size_bytes BIGINT NOT NULL,
|
||||
mime_type TEXT NOT NULL DEFAULT 'application/octet-stream',
|
||||
storage_path TEXT NOT NULL,
|
||||
url TEXT,
|
||||
download_count BIGINT NOT NULL DEFAULT 0,
|
||||
uploaded_by UUID REFERENCES "user"(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
deleted_at TIMESTAMPTZ
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_repo_release_asset_release ON repo_release_asset(release_id, deleted_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_repo_release_asset_deleted ON repo_release_asset(deleted_at) WHERE deleted_at IS NOT NULL;
|
||||
|
||||
-- PR Templates (mirrors issue_template structure)
|
||||
CREATE TABLE IF NOT EXISTS pr_template (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
repo_id UUID NOT NULL REFERENCES repo(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
title_template TEXT,
|
||||
body_template TEXT NOT NULL DEFAULT '',
|
||||
labels TEXT[] NOT NULL DEFAULT '{}',
|
||||
active BOOLEAN NOT NULL DEFAULT true,
|
||||
created_by UUID REFERENCES "user"(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_pr_template_repo ON pr_template(repo_id);
|
||||
|
||||
-- PR Review Requests
|
||||
CREATE TABLE IF NOT EXISTS pr_review_request (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
pull_request_id UUID NOT NULL REFERENCES pull_request(id) ON DELETE CASCADE,
|
||||
reviewer_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
requested_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_pr_review_request_pr ON pr_review_request(pull_request_id);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_pr_review_request_pr_reviewer ON pr_review_request(pull_request_id, reviewer_id);
|
||||
Reference in New Issue
Block a user