A decentralized music tracking and discovery platform built on AT Protocol 🎵
listenbrainz spotify atproto lastfm musicbrainz scrobbling

Merge pull request #3 from tsirysndr/feat/pgpull

Add `pgpull` command to sync Rocksky Postgres database to local instance

authored by tsiry-sandratraina.com and committed by GitHub 77d05524 26a45e7f

+2 -1
.dockerignore
··· 5 5 .DS_Store 6 6 Dockerfile 7 7 *.ddb 8 - *.ddb.wal 8 + *.ddb.wal 9 + rocksky-backup.sql
+3 -1
.env.example
··· 13 13 14 14 JETSTREAM_SERVER=wss://jetstream2.us-east.bsky.network 15 15 16 - SCROBBLER_ORIGIN_URL="http://localhost:7882" 16 + SCROBBLER_ORIGIN_URL="http://localhost:7882" 17 + 18 + SOURCE_POSTGRES_URL=
+2 -1
.gitignore
··· 6 6 *.ddb 7 7 *.ddb.wal 8 8 .vscode/ 9 - *.DS_Store 9 + *.DS_Store 10 + rocksky-backup.sql
+15
Cargo.lock
··· 4968 4968 ] 4969 4969 4970 4970 [[package]] 4971 + name = "rocksky-pgpull" 4972 + version = "0.1.0" 4973 + dependencies = [ 4974 + "anyhow", 4975 + "chrono", 4976 + "owo-colors", 4977 + "serde", 4978 + "serde_json", 4979 + "sqlx", 4980 + "tokio", 4981 + "tracing", 4982 + ] 4983 + 4984 + [[package]] 4971 4985 name = "rocksky-playlists" 4972 4986 version = "0.1.0" 4973 4987 dependencies = [ ··· 5126 5140 "rocksky-dropbox", 5127 5141 "rocksky-googledrive", 5128 5142 "rocksky-jetstream", 5143 + "rocksky-pgpull", 5129 5144 "rocksky-playlists", 5130 5145 "rocksky-scrobbler", 5131 5146 "rocksky-spotify",
+1 -1
apps/api/drizzle.config.ts
··· 5 5 dialect: "postgresql", 6 6 schema: "./src/schema", 7 7 dbCredentials: { 8 - url: process.env.POSTGRES_URL!, 8 + url: process.env.XATA_POSTGRES_URL!, 9 9 }, 10 10 });
+19 -20
apps/api/drizzle/0000_quiet_mister_sinister.sql apps/api/drizzle/0000_left_swordsman.sql
··· 4 4 "track_id" text NOT NULL, 5 5 "xata_createdat" timestamp DEFAULT now() NOT NULL, 6 6 "xata_updatedat" timestamp DEFAULT now() NOT NULL, 7 - "xata_version" integer NOT NULL 7 + "xata_version" integer 8 8 ); 9 9 --> statement-breakpoint 10 10 CREATE TABLE "albums" ( ··· 50 50 "album_id" text NOT NULL, 51 51 "xata_createdat" timestamp DEFAULT now() NOT NULL, 52 52 "xata_updatedat" timestamp DEFAULT now() NOT NULL, 53 - "xata_version" integer NOT NULL 53 + "xata_version" integer 54 54 ); 55 55 --> statement-breakpoint 56 56 CREATE TABLE "artist_tracks" ( ··· 59 59 "track_id" text NOT NULL, 60 60 "xata_createdat" timestamp DEFAULT now() NOT NULL, 61 61 "xata_updatedat" timestamp DEFAULT now() NOT NULL, 62 - "xata_version" integer NOT NULL 62 + "xata_version" integer 63 63 ); 64 64 --> statement-breakpoint 65 65 CREATE TABLE "artists" ( ··· 76 76 "spotify_link" text, 77 77 "tidal_link" text, 78 78 "youtube_link" text, 79 + "genres" text[], 79 80 "xata_createdat" timestamp DEFAULT now() NOT NULL, 80 81 "xata_updatedat" timestamp DEFAULT now() NOT NULL, 81 82 "xata_version" integer, ··· 88 89 "email" text NOT NULL, 89 90 "is_beta_user" boolean DEFAULT false NOT NULL, 90 91 "user_id" text NOT NULL, 91 - "xata_version" text NOT NULL, 92 + "xata_version" text, 92 93 "xata_createdat" timestamp DEFAULT now() NOT NULL, 93 94 "xata_updatedat" timestamp DEFAULT now() NOT NULL, 94 95 CONSTRAINT "dropbox_accounts_email_unique" UNIQUE("email") ··· 101 102 "parent_id" text, 102 103 "dropbox_id" text NOT NULL, 103 104 "file_id" text NOT NULL, 104 - "xata_version" text NOT NULL, 105 + "xata_version" text, 105 106 "xata_createdat" timestamp DEFAULT now() NOT NULL, 106 107 "xata_updatedat" timestamp DEFAULT now() NOT NULL, 107 108 CONSTRAINT "dropbox_directories_file_id_unique" UNIQUE("file_id") ··· 115 116 "track_id" text NOT NULL, 116 117 "directory_id" text, 117 118 "file_id" text NOT NULL, 118 - "xata_version" text NOT NULL, 119 + "xata_version" text, 119 120 "xata_createdat" timestamp DEFAULT now() NOT NULL, 120 121 "xata_updatedat" timestamp DEFAULT now() NOT NULL, 121 122 CONSTRAINT "dropbox_paths_file_id_unique" UNIQUE("file_id") ··· 132 133 "xata_id" text PRIMARY KEY DEFAULT xata_id(), 133 134 "user_id" text NOT NULL, 134 135 "dropbox_token_id" text NOT NULL, 135 - "xata_version" text NOT NULL, 136 + "xata_version" text, 136 137 "xata_createdat" timestamp DEFAULT now() NOT NULL, 137 138 "xata_updatedat" timestamp DEFAULT now() NOT NULL 138 139 ); ··· 142 143 "email" text NOT NULL, 143 144 "is_beta_user" boolean DEFAULT false NOT NULL, 144 145 "user_id" text NOT NULL, 145 - "xata_version" text NOT NULL, 146 + "xata_version" text, 146 147 "xata_createdat" timestamp DEFAULT now() NOT NULL, 147 148 "xata_updatedat" timestamp DEFAULT now() NOT NULL, 148 149 CONSTRAINT "google_drive_accounts_email_unique" UNIQUE("email") ··· 155 156 "parent_id" text, 156 157 "google_drive_id" text NOT NULL, 157 158 "file_id" text NOT NULL, 158 - "xata_version" text NOT NULL, 159 + "xata_version" text, 159 160 "xata_createdat" timestamp DEFAULT now() NOT NULL, 160 161 "xata_updatedat" timestamp DEFAULT now() NOT NULL, 161 162 CONSTRAINT "google_drive_directories_file_id_unique" UNIQUE("file_id") ··· 168 169 "name" text NOT NULL, 169 170 "directory_id" text, 170 171 "file_id" text NOT NULL, 171 - "xata_version" text NOT NULL, 172 + "xata_version" text, 172 173 "xata_createdat" timestamp DEFAULT now() NOT NULL, 173 174 "xata_updatedat" timestamp DEFAULT now() NOT NULL, 174 175 CONSTRAINT "google_drive_paths_file_id_unique" UNIQUE("file_id") ··· 185 186 "xata_id" text PRIMARY KEY DEFAULT xata_id(), 186 187 "google_drive_token_id" text NOT NULL, 187 188 "user_id" text NOT NULL, 188 - "xata_version" text NOT NULL, 189 + "xata_version" text, 189 190 "xata_createdat" timestamp DEFAULT now() NOT NULL, 190 191 "xata_updatedat" timestamp DEFAULT now() NOT NULL 191 192 ); ··· 286 287 --> statement-breakpoint 287 288 CREATE TABLE "spotify_accounts" ( 288 289 "xata_id" text PRIMARY KEY DEFAULT xata_id(), 289 - "xata_version" integer NOT NULL, 290 + "xata_version" integer, 290 291 "email" text NOT NULL, 291 292 "user_id" text NOT NULL, 292 293 "is_beta_user" boolean DEFAULT false NOT NULL, ··· 296 297 --> statement-breakpoint 297 298 CREATE TABLE "spotify_tokens" ( 298 299 "xata_id" text PRIMARY KEY DEFAULT xata_id(), 299 - "xata_version" integer NOT NULL, 300 + "xata_version" integer, 300 301 "access_token" text NOT NULL, 301 302 "refresh_token" text NOT NULL, 302 303 "user_id" text NOT NULL, ··· 337 338 CONSTRAINT "tracks_apple_music_link_unique" UNIQUE("apple_music_link"), 338 339 CONSTRAINT "tracks_tidal_link_unique" UNIQUE("tidal_link"), 339 340 CONSTRAINT "tracks_sha256_unique" UNIQUE("sha256"), 340 - CONSTRAINT "tracks_uri_unique" UNIQUE("uri"), 341 - CONSTRAINT "tracks_album_uri_unique" UNIQUE("album_uri"), 342 - CONSTRAINT "tracks_artist_uri_unique" UNIQUE("artist_uri") 341 + CONSTRAINT "tracks_uri_unique" UNIQUE("uri") 343 342 ); 344 343 --> statement-breakpoint 345 344 CREATE TABLE "user_albums" ( ··· 348 347 "album_id" text NOT NULL, 349 348 "xata_createdat" timestamp DEFAULT now() NOT NULL, 350 349 "xata_updatedat" timestamp DEFAULT now() NOT NULL, 351 - "xata_version" integer NOT NULL, 350 + "xata_version" integer, 352 351 "scrobbles" integer, 353 352 "uri" text NOT NULL, 354 353 CONSTRAINT "user_albums_uri_unique" UNIQUE("uri") ··· 360 359 "artist_id" text NOT NULL, 361 360 "xata_createdat" timestamp DEFAULT now() NOT NULL, 362 361 "xata_updatedat" timestamp DEFAULT now() NOT NULL, 363 - "xata_version" integer NOT NULL, 362 + "xata_version" integer, 364 363 "scrobbles" integer, 365 364 "uri" text NOT NULL, 366 365 CONSTRAINT "user_artists_uri_unique" UNIQUE("uri") ··· 381 380 "track_id" text NOT NULL, 382 381 "xata_createdat" timestamp DEFAULT now() NOT NULL, 383 382 "xata_updatedat" timestamp DEFAULT now() NOT NULL, 384 - "xata_version" integer NOT NULL, 383 + "xata_version" integer, 385 384 "uri" text NOT NULL, 386 385 "scrobbles" integer, 387 386 CONSTRAINT "user_tracks_uri_unique" UNIQUE("uri") ··· 390 389 CREATE TABLE "users" ( 391 390 "xata_id" text PRIMARY KEY DEFAULT xata_id(), 392 391 "did" text NOT NULL, 393 - "display_name" text NOT NULL, 392 + "display_name" text, 394 393 "handle" text NOT NULL, 395 394 "avatar" text NOT NULL, 396 395 "xata_createdat" timestamp DEFAULT now() NOT NULL,
+1
apps/api/drizzle/0001_fluffy_epoch.sql
··· 1 + ALTER TABLE "user_playlists" ALTER COLUMN "uri" DROP NOT NULL;
+3
apps/api/drizzle/0002_sweet_randall_flagg.sql
··· 1 + ALTER TABLE "user_playlists" DROP CONSTRAINT "user_playlists_playlist_id_tracks_xata_id_fk"; 2 + --> statement-breakpoint 3 + ALTER TABLE "user_playlists" ADD CONSTRAINT "user_playlists_playlist_id_playlists_xata_id_fk" FOREIGN KEY ("playlist_id") REFERENCES "public"."playlists"("xata_id") ON DELETE no action ON UPDATE no action;
+24 -32
apps/api/drizzle/meta/0000_snapshot.json
··· 1 1 { 2 - "id": "49adc24b-83c8-4065-8e13-80045e7f0f27", 2 + "id": "6b33dcd0-52df-4403-bdc0-31517769923d", 3 3 "prevId": "00000000-0000-0000-0000-000000000000", 4 4 "version": "7", 5 5 "dialect": "postgresql", ··· 44 44 "name": "xata_version", 45 45 "type": "integer", 46 46 "primaryKey": false, 47 - "notNull": true 47 + "notNull": false 48 48 } 49 49 }, 50 50 "indexes": {}, ··· 360 360 "name": "xata_version", 361 361 "type": "integer", 362 362 "primaryKey": false, 363 - "notNull": true 363 + "notNull": false 364 364 } 365 365 }, 366 366 "indexes": {}, ··· 438 438 "name": "xata_version", 439 439 "type": "integer", 440 440 "primaryKey": false, 441 - "notNull": true 441 + "notNull": false 442 442 } 443 443 }, 444 444 "indexes": {}, ··· 555 555 "youtube_link": { 556 556 "name": "youtube_link", 557 557 "type": "text", 558 + "primaryKey": false, 559 + "notNull": false 560 + }, 561 + "genres": { 562 + "name": "genres", 563 + "type": "text[]", 558 564 "primaryKey": false, 559 565 "notNull": false 560 566 }, ··· 635 641 "name": "xata_version", 636 642 "type": "text", 637 643 "primaryKey": false, 638 - "notNull": true 644 + "notNull": false 639 645 }, 640 646 "xata_createdat": { 641 647 "name": "xata_createdat", ··· 726 732 "name": "xata_version", 727 733 "type": "text", 728 734 "primaryKey": false, 729 - "notNull": true 735 + "notNull": false 730 736 }, 731 737 "xata_createdat": { 732 738 "name": "xata_createdat", ··· 823 829 "name": "xata_version", 824 830 "type": "text", 825 831 "primaryKey": false, 826 - "notNull": true 832 + "notNull": false 827 833 }, 828 834 "xata_createdat": { 829 835 "name": "xata_createdat", ··· 935 941 "name": "xata_version", 936 942 "type": "text", 937 943 "primaryKey": false, 938 - "notNull": true 944 + "notNull": false 939 945 }, 940 946 "xata_createdat": { 941 947 "name": "xata_createdat", ··· 1020 1026 "name": "xata_version", 1021 1027 "type": "text", 1022 1028 "primaryKey": false, 1023 - "notNull": true 1029 + "notNull": false 1024 1030 }, 1025 1031 "xata_createdat": { 1026 1032 "name": "xata_createdat", ··· 1111 1117 "name": "xata_version", 1112 1118 "type": "text", 1113 1119 "primaryKey": false, 1114 - "notNull": true 1120 + "notNull": false 1115 1121 }, 1116 1122 "xata_createdat": { 1117 1123 "name": "xata_createdat", ··· 1202 1208 "name": "xata_version", 1203 1209 "type": "text", 1204 1210 "primaryKey": false, 1205 - "notNull": true 1211 + "notNull": false 1206 1212 }, 1207 1213 "xata_createdat": { 1208 1214 "name": "xata_createdat", ··· 1314 1320 "name": "xata_version", 1315 1321 "type": "text", 1316 1322 "primaryKey": false, 1317 - "notNull": true 1323 + "notNull": false 1318 1324 }, 1319 1325 "xata_createdat": { 1320 1326 "name": "xata_createdat", ··· 2232 2238 "name": "xata_version", 2233 2239 "type": "integer", 2234 2240 "primaryKey": false, 2235 - "notNull": true 2241 + "notNull": false 2236 2242 }, 2237 2243 "email": { 2238 2244 "name": "email", ··· 2304 2310 "name": "xata_version", 2305 2311 "type": "integer", 2306 2312 "primaryKey": false, 2307 - "notNull": true 2313 + "notNull": false 2308 2314 }, 2309 2315 "access_token": { 2310 2316 "name": "access_token", ··· 2576 2582 "columns": [ 2577 2583 "uri" 2578 2584 ] 2579 - }, 2580 - "tracks_album_uri_unique": { 2581 - "name": "tracks_album_uri_unique", 2582 - "nullsNotDistinct": false, 2583 - "columns": [ 2584 - "album_uri" 2585 - ] 2586 - }, 2587 - "tracks_artist_uri_unique": { 2588 - "name": "tracks_artist_uri_unique", 2589 - "nullsNotDistinct": false, 2590 - "columns": [ 2591 - "artist_uri" 2592 - ] 2593 2585 } 2594 2586 }, 2595 2587 "policies": {}, ··· 2636 2628 "name": "xata_version", 2637 2629 "type": "integer", 2638 2630 "primaryKey": false, 2639 - "notNull": true 2631 + "notNull": false 2640 2632 }, 2641 2633 "scrobbles": { 2642 2634 "name": "scrobbles", ··· 2734 2726 "name": "xata_version", 2735 2727 "type": "integer", 2736 2728 "primaryKey": false, 2737 - "notNull": true 2729 + "notNull": false 2738 2730 }, 2739 2731 "scrobbles": { 2740 2732 "name": "scrobbles", ··· 2911 2903 "name": "xata_version", 2912 2904 "type": "integer", 2913 2905 "primaryKey": false, 2914 - "notNull": true 2906 + "notNull": false 2915 2907 }, 2916 2908 "uri": { 2917 2909 "name": "uri", ··· 2989 2981 "name": "display_name", 2990 2982 "type": "text", 2991 2983 "primaryKey": false, 2992 - "notNull": true 2984 + "notNull": false 2993 2985 }, 2994 2986 "handle": { 2995 2987 "name": "handle",
+3132
apps/api/drizzle/meta/0001_snapshot.json
··· 1 + { 2 + "id": "6ebb9775-b900-418c-bbd8-b704571a20d5", 3 + "prevId": "6b33dcd0-52df-4403-bdc0-31517769923d", 4 + "version": "7", 5 + "dialect": "postgresql", 6 + "tables": { 7 + "public.album_tracks": { 8 + "name": "album_tracks", 9 + "schema": "", 10 + "columns": { 11 + "xata_id": { 12 + "name": "xata_id", 13 + "type": "text", 14 + "primaryKey": true, 15 + "notNull": true 16 + }, 17 + "album_id": { 18 + "name": "album_id", 19 + "type": "text", 20 + "primaryKey": false, 21 + "notNull": true 22 + }, 23 + "track_id": { 24 + "name": "track_id", 25 + "type": "text", 26 + "primaryKey": false, 27 + "notNull": true 28 + }, 29 + "xata_createdat": { 30 + "name": "xata_createdat", 31 + "type": "timestamp", 32 + "primaryKey": false, 33 + "notNull": true, 34 + "default": "now()" 35 + }, 36 + "xata_updatedat": { 37 + "name": "xata_updatedat", 38 + "type": "timestamp", 39 + "primaryKey": false, 40 + "notNull": true, 41 + "default": "now()" 42 + }, 43 + "xata_version": { 44 + "name": "xata_version", 45 + "type": "integer", 46 + "primaryKey": false, 47 + "notNull": false 48 + } 49 + }, 50 + "indexes": {}, 51 + "foreignKeys": { 52 + "album_tracks_album_id_albums_xata_id_fk": { 53 + "name": "album_tracks_album_id_albums_xata_id_fk", 54 + "tableFrom": "album_tracks", 55 + "tableTo": "albums", 56 + "columnsFrom": [ 57 + "album_id" 58 + ], 59 + "columnsTo": [ 60 + "xata_id" 61 + ], 62 + "onDelete": "no action", 63 + "onUpdate": "no action" 64 + }, 65 + "album_tracks_track_id_tracks_xata_id_fk": { 66 + "name": "album_tracks_track_id_tracks_xata_id_fk", 67 + "tableFrom": "album_tracks", 68 + "tableTo": "tracks", 69 + "columnsFrom": [ 70 + "track_id" 71 + ], 72 + "columnsTo": [ 73 + "xata_id" 74 + ], 75 + "onDelete": "no action", 76 + "onUpdate": "no action" 77 + } 78 + }, 79 + "compositePrimaryKeys": {}, 80 + "uniqueConstraints": {}, 81 + "policies": {}, 82 + "checkConstraints": {}, 83 + "isRLSEnabled": false 84 + }, 85 + "public.albums": { 86 + "name": "albums", 87 + "schema": "", 88 + "columns": { 89 + "xata_id": { 90 + "name": "xata_id", 91 + "type": "text", 92 + "primaryKey": true, 93 + "notNull": true 94 + }, 95 + "title": { 96 + "name": "title", 97 + "type": "text", 98 + "primaryKey": false, 99 + "notNull": true 100 + }, 101 + "artist": { 102 + "name": "artist", 103 + "type": "text", 104 + "primaryKey": false, 105 + "notNull": true 106 + }, 107 + "release_date": { 108 + "name": "release_date", 109 + "type": "text", 110 + "primaryKey": false, 111 + "notNull": false 112 + }, 113 + "year": { 114 + "name": "year", 115 + "type": "integer", 116 + "primaryKey": false, 117 + "notNull": false 118 + }, 119 + "album_art": { 120 + "name": "album_art", 121 + "type": "text", 122 + "primaryKey": false, 123 + "notNull": false 124 + }, 125 + "uri": { 126 + "name": "uri", 127 + "type": "text", 128 + "primaryKey": false, 129 + "notNull": false 130 + }, 131 + "artist_uri": { 132 + "name": "artist_uri", 133 + "type": "text", 134 + "primaryKey": false, 135 + "notNull": false 136 + }, 137 + "apple_music_link": { 138 + "name": "apple_music_link", 139 + "type": "text", 140 + "primaryKey": false, 141 + "notNull": false 142 + }, 143 + "spotify_link": { 144 + "name": "spotify_link", 145 + "type": "text", 146 + "primaryKey": false, 147 + "notNull": false 148 + }, 149 + "tidal_link": { 150 + "name": "tidal_link", 151 + "type": "text", 152 + "primaryKey": false, 153 + "notNull": false 154 + }, 155 + "youtube_link": { 156 + "name": "youtube_link", 157 + "type": "text", 158 + "primaryKey": false, 159 + "notNull": false 160 + }, 161 + "sha256": { 162 + "name": "sha256", 163 + "type": "text", 164 + "primaryKey": false, 165 + "notNull": true 166 + }, 167 + "xata_createdat": { 168 + "name": "xata_createdat", 169 + "type": "timestamp", 170 + "primaryKey": false, 171 + "notNull": true, 172 + "default": "now()" 173 + }, 174 + "xata_updatedat": { 175 + "name": "xata_updatedat", 176 + "type": "timestamp", 177 + "primaryKey": false, 178 + "notNull": true, 179 + "default": "now()" 180 + }, 181 + "xata_version": { 182 + "name": "xata_version", 183 + "type": "integer", 184 + "primaryKey": false, 185 + "notNull": false 186 + } 187 + }, 188 + "indexes": {}, 189 + "foreignKeys": {}, 190 + "compositePrimaryKeys": {}, 191 + "uniqueConstraints": { 192 + "albums_uri_unique": { 193 + "name": "albums_uri_unique", 194 + "nullsNotDistinct": false, 195 + "columns": [ 196 + "uri" 197 + ] 198 + }, 199 + "albums_apple_music_link_unique": { 200 + "name": "albums_apple_music_link_unique", 201 + "nullsNotDistinct": false, 202 + "columns": [ 203 + "apple_music_link" 204 + ] 205 + }, 206 + "albums_spotify_link_unique": { 207 + "name": "albums_spotify_link_unique", 208 + "nullsNotDistinct": false, 209 + "columns": [ 210 + "spotify_link" 211 + ] 212 + }, 213 + "albums_tidal_link_unique": { 214 + "name": "albums_tidal_link_unique", 215 + "nullsNotDistinct": false, 216 + "columns": [ 217 + "tidal_link" 218 + ] 219 + }, 220 + "albums_youtube_link_unique": { 221 + "name": "albums_youtube_link_unique", 222 + "nullsNotDistinct": false, 223 + "columns": [ 224 + "youtube_link" 225 + ] 226 + }, 227 + "albums_sha256_unique": { 228 + "name": "albums_sha256_unique", 229 + "nullsNotDistinct": false, 230 + "columns": [ 231 + "sha256" 232 + ] 233 + } 234 + }, 235 + "policies": {}, 236 + "checkConstraints": {}, 237 + "isRLSEnabled": false 238 + }, 239 + "public.api_keys": { 240 + "name": "api_keys", 241 + "schema": "", 242 + "columns": { 243 + "xata_id": { 244 + "name": "xata_id", 245 + "type": "text", 246 + "primaryKey": true, 247 + "notNull": true 248 + }, 249 + "name": { 250 + "name": "name", 251 + "type": "text", 252 + "primaryKey": false, 253 + "notNull": true 254 + }, 255 + "api_key": { 256 + "name": "api_key", 257 + "type": "text", 258 + "primaryKey": false, 259 + "notNull": true 260 + }, 261 + "shared_secret": { 262 + "name": "shared_secret", 263 + "type": "text", 264 + "primaryKey": false, 265 + "notNull": true 266 + }, 267 + "description": { 268 + "name": "description", 269 + "type": "text", 270 + "primaryKey": false, 271 + "notNull": false 272 + }, 273 + "enabled": { 274 + "name": "enabled", 275 + "type": "boolean", 276 + "primaryKey": false, 277 + "notNull": true, 278 + "default": true 279 + }, 280 + "user_id": { 281 + "name": "user_id", 282 + "type": "text", 283 + "primaryKey": false, 284 + "notNull": true 285 + }, 286 + "xata_createdat": { 287 + "name": "xata_createdat", 288 + "type": "timestamp", 289 + "primaryKey": false, 290 + "notNull": true, 291 + "default": "now()" 292 + }, 293 + "xata_updatedat": { 294 + "name": "xata_updatedat", 295 + "type": "timestamp", 296 + "primaryKey": false, 297 + "notNull": true, 298 + "default": "now()" 299 + } 300 + }, 301 + "indexes": {}, 302 + "foreignKeys": { 303 + "api_keys_user_id_users_xata_id_fk": { 304 + "name": "api_keys_user_id_users_xata_id_fk", 305 + "tableFrom": "api_keys", 306 + "tableTo": "users", 307 + "columnsFrom": [ 308 + "user_id" 309 + ], 310 + "columnsTo": [ 311 + "xata_id" 312 + ], 313 + "onDelete": "no action", 314 + "onUpdate": "no action" 315 + } 316 + }, 317 + "compositePrimaryKeys": {}, 318 + "uniqueConstraints": {}, 319 + "policies": {}, 320 + "checkConstraints": {}, 321 + "isRLSEnabled": false 322 + }, 323 + "public.artist_albums": { 324 + "name": "artist_albums", 325 + "schema": "", 326 + "columns": { 327 + "xata_id": { 328 + "name": "xata_id", 329 + "type": "text", 330 + "primaryKey": true, 331 + "notNull": true 332 + }, 333 + "artist_id": { 334 + "name": "artist_id", 335 + "type": "text", 336 + "primaryKey": false, 337 + "notNull": true 338 + }, 339 + "album_id": { 340 + "name": "album_id", 341 + "type": "text", 342 + "primaryKey": false, 343 + "notNull": true 344 + }, 345 + "xata_createdat": { 346 + "name": "xata_createdat", 347 + "type": "timestamp", 348 + "primaryKey": false, 349 + "notNull": true, 350 + "default": "now()" 351 + }, 352 + "xata_updatedat": { 353 + "name": "xata_updatedat", 354 + "type": "timestamp", 355 + "primaryKey": false, 356 + "notNull": true, 357 + "default": "now()" 358 + }, 359 + "xata_version": { 360 + "name": "xata_version", 361 + "type": "integer", 362 + "primaryKey": false, 363 + "notNull": false 364 + } 365 + }, 366 + "indexes": {}, 367 + "foreignKeys": { 368 + "artist_albums_artist_id_artists_xata_id_fk": { 369 + "name": "artist_albums_artist_id_artists_xata_id_fk", 370 + "tableFrom": "artist_albums", 371 + "tableTo": "artists", 372 + "columnsFrom": [ 373 + "artist_id" 374 + ], 375 + "columnsTo": [ 376 + "xata_id" 377 + ], 378 + "onDelete": "no action", 379 + "onUpdate": "no action" 380 + }, 381 + "artist_albums_album_id_albums_xata_id_fk": { 382 + "name": "artist_albums_album_id_albums_xata_id_fk", 383 + "tableFrom": "artist_albums", 384 + "tableTo": "albums", 385 + "columnsFrom": [ 386 + "album_id" 387 + ], 388 + "columnsTo": [ 389 + "xata_id" 390 + ], 391 + "onDelete": "no action", 392 + "onUpdate": "no action" 393 + } 394 + }, 395 + "compositePrimaryKeys": {}, 396 + "uniqueConstraints": {}, 397 + "policies": {}, 398 + "checkConstraints": {}, 399 + "isRLSEnabled": false 400 + }, 401 + "public.artist_tracks": { 402 + "name": "artist_tracks", 403 + "schema": "", 404 + "columns": { 405 + "xata_id": { 406 + "name": "xata_id", 407 + "type": "text", 408 + "primaryKey": true, 409 + "notNull": true 410 + }, 411 + "artist_id": { 412 + "name": "artist_id", 413 + "type": "text", 414 + "primaryKey": false, 415 + "notNull": true 416 + }, 417 + "track_id": { 418 + "name": "track_id", 419 + "type": "text", 420 + "primaryKey": false, 421 + "notNull": true 422 + }, 423 + "xata_createdat": { 424 + "name": "xata_createdat", 425 + "type": "timestamp", 426 + "primaryKey": false, 427 + "notNull": true, 428 + "default": "now()" 429 + }, 430 + "xata_updatedat": { 431 + "name": "xata_updatedat", 432 + "type": "timestamp", 433 + "primaryKey": false, 434 + "notNull": true, 435 + "default": "now()" 436 + }, 437 + "xata_version": { 438 + "name": "xata_version", 439 + "type": "integer", 440 + "primaryKey": false, 441 + "notNull": false 442 + } 443 + }, 444 + "indexes": {}, 445 + "foreignKeys": { 446 + "artist_tracks_artist_id_artists_xata_id_fk": { 447 + "name": "artist_tracks_artist_id_artists_xata_id_fk", 448 + "tableFrom": "artist_tracks", 449 + "tableTo": "artists", 450 + "columnsFrom": [ 451 + "artist_id" 452 + ], 453 + "columnsTo": [ 454 + "xata_id" 455 + ], 456 + "onDelete": "no action", 457 + "onUpdate": "no action" 458 + }, 459 + "artist_tracks_track_id_tracks_xata_id_fk": { 460 + "name": "artist_tracks_track_id_tracks_xata_id_fk", 461 + "tableFrom": "artist_tracks", 462 + "tableTo": "tracks", 463 + "columnsFrom": [ 464 + "track_id" 465 + ], 466 + "columnsTo": [ 467 + "xata_id" 468 + ], 469 + "onDelete": "no action", 470 + "onUpdate": "no action" 471 + } 472 + }, 473 + "compositePrimaryKeys": {}, 474 + "uniqueConstraints": {}, 475 + "policies": {}, 476 + "checkConstraints": {}, 477 + "isRLSEnabled": false 478 + }, 479 + "public.artists": { 480 + "name": "artists", 481 + "schema": "", 482 + "columns": { 483 + "xata_id": { 484 + "name": "xata_id", 485 + "type": "text", 486 + "primaryKey": true, 487 + "notNull": true 488 + }, 489 + "name": { 490 + "name": "name", 491 + "type": "text", 492 + "primaryKey": false, 493 + "notNull": true 494 + }, 495 + "biography": { 496 + "name": "biography", 497 + "type": "text", 498 + "primaryKey": false, 499 + "notNull": false 500 + }, 501 + "born": { 502 + "name": "born", 503 + "type": "timestamp", 504 + "primaryKey": false, 505 + "notNull": false 506 + }, 507 + "born_in": { 508 + "name": "born_in", 509 + "type": "text", 510 + "primaryKey": false, 511 + "notNull": false 512 + }, 513 + "died": { 514 + "name": "died", 515 + "type": "timestamp", 516 + "primaryKey": false, 517 + "notNull": false 518 + }, 519 + "picture": { 520 + "name": "picture", 521 + "type": "text", 522 + "primaryKey": false, 523 + "notNull": false 524 + }, 525 + "sha256": { 526 + "name": "sha256", 527 + "type": "text", 528 + "primaryKey": false, 529 + "notNull": true 530 + }, 531 + "uri": { 532 + "name": "uri", 533 + "type": "text", 534 + "primaryKey": false, 535 + "notNull": false 536 + }, 537 + "apple_music_link": { 538 + "name": "apple_music_link", 539 + "type": "text", 540 + "primaryKey": false, 541 + "notNull": false 542 + }, 543 + "spotify_link": { 544 + "name": "spotify_link", 545 + "type": "text", 546 + "primaryKey": false, 547 + "notNull": false 548 + }, 549 + "tidal_link": { 550 + "name": "tidal_link", 551 + "type": "text", 552 + "primaryKey": false, 553 + "notNull": false 554 + }, 555 + "youtube_link": { 556 + "name": "youtube_link", 557 + "type": "text", 558 + "primaryKey": false, 559 + "notNull": false 560 + }, 561 + "genres": { 562 + "name": "genres", 563 + "type": "text[]", 564 + "primaryKey": false, 565 + "notNull": false 566 + }, 567 + "xata_createdat": { 568 + "name": "xata_createdat", 569 + "type": "timestamp", 570 + "primaryKey": false, 571 + "notNull": true, 572 + "default": "now()" 573 + }, 574 + "xata_updatedat": { 575 + "name": "xata_updatedat", 576 + "type": "timestamp", 577 + "primaryKey": false, 578 + "notNull": true, 579 + "default": "now()" 580 + }, 581 + "xata_version": { 582 + "name": "xata_version", 583 + "type": "integer", 584 + "primaryKey": false, 585 + "notNull": false 586 + } 587 + }, 588 + "indexes": {}, 589 + "foreignKeys": {}, 590 + "compositePrimaryKeys": {}, 591 + "uniqueConstraints": { 592 + "artists_sha256_unique": { 593 + "name": "artists_sha256_unique", 594 + "nullsNotDistinct": false, 595 + "columns": [ 596 + "sha256" 597 + ] 598 + }, 599 + "artists_uri_unique": { 600 + "name": "artists_uri_unique", 601 + "nullsNotDistinct": false, 602 + "columns": [ 603 + "uri" 604 + ] 605 + } 606 + }, 607 + "policies": {}, 608 + "checkConstraints": {}, 609 + "isRLSEnabled": false 610 + }, 611 + "public.dropbox_accounts": { 612 + "name": "dropbox_accounts", 613 + "schema": "", 614 + "columns": { 615 + "xata_id": { 616 + "name": "xata_id", 617 + "type": "text", 618 + "primaryKey": true, 619 + "notNull": true 620 + }, 621 + "email": { 622 + "name": "email", 623 + "type": "text", 624 + "primaryKey": false, 625 + "notNull": true 626 + }, 627 + "is_beta_user": { 628 + "name": "is_beta_user", 629 + "type": "boolean", 630 + "primaryKey": false, 631 + "notNull": true, 632 + "default": false 633 + }, 634 + "user_id": { 635 + "name": "user_id", 636 + "type": "text", 637 + "primaryKey": false, 638 + "notNull": true 639 + }, 640 + "xata_version": { 641 + "name": "xata_version", 642 + "type": "text", 643 + "primaryKey": false, 644 + "notNull": false 645 + }, 646 + "xata_createdat": { 647 + "name": "xata_createdat", 648 + "type": "timestamp", 649 + "primaryKey": false, 650 + "notNull": true, 651 + "default": "now()" 652 + }, 653 + "xata_updatedat": { 654 + "name": "xata_updatedat", 655 + "type": "timestamp", 656 + "primaryKey": false, 657 + "notNull": true, 658 + "default": "now()" 659 + } 660 + }, 661 + "indexes": {}, 662 + "foreignKeys": { 663 + "dropbox_accounts_user_id_users_xata_id_fk": { 664 + "name": "dropbox_accounts_user_id_users_xata_id_fk", 665 + "tableFrom": "dropbox_accounts", 666 + "tableTo": "users", 667 + "columnsFrom": [ 668 + "user_id" 669 + ], 670 + "columnsTo": [ 671 + "xata_id" 672 + ], 673 + "onDelete": "no action", 674 + "onUpdate": "no action" 675 + } 676 + }, 677 + "compositePrimaryKeys": {}, 678 + "uniqueConstraints": { 679 + "dropbox_accounts_email_unique": { 680 + "name": "dropbox_accounts_email_unique", 681 + "nullsNotDistinct": false, 682 + "columns": [ 683 + "email" 684 + ] 685 + } 686 + }, 687 + "policies": {}, 688 + "checkConstraints": {}, 689 + "isRLSEnabled": false 690 + }, 691 + "public.dropbox_directories": { 692 + "name": "dropbox_directories", 693 + "schema": "", 694 + "columns": { 695 + "xata_id": { 696 + "name": "xata_id", 697 + "type": "text", 698 + "primaryKey": true, 699 + "notNull": true 700 + }, 701 + "name": { 702 + "name": "name", 703 + "type": "text", 704 + "primaryKey": false, 705 + "notNull": true 706 + }, 707 + "path": { 708 + "name": "path", 709 + "type": "text", 710 + "primaryKey": false, 711 + "notNull": true 712 + }, 713 + "parent_id": { 714 + "name": "parent_id", 715 + "type": "text", 716 + "primaryKey": false, 717 + "notNull": false 718 + }, 719 + "dropbox_id": { 720 + "name": "dropbox_id", 721 + "type": "text", 722 + "primaryKey": false, 723 + "notNull": true 724 + }, 725 + "file_id": { 726 + "name": "file_id", 727 + "type": "text", 728 + "primaryKey": false, 729 + "notNull": true 730 + }, 731 + "xata_version": { 732 + "name": "xata_version", 733 + "type": "text", 734 + "primaryKey": false, 735 + "notNull": false 736 + }, 737 + "xata_createdat": { 738 + "name": "xata_createdat", 739 + "type": "timestamp", 740 + "primaryKey": false, 741 + "notNull": true, 742 + "default": "now()" 743 + }, 744 + "xata_updatedat": { 745 + "name": "xata_updatedat", 746 + "type": "timestamp", 747 + "primaryKey": false, 748 + "notNull": true, 749 + "default": "now()" 750 + } 751 + }, 752 + "indexes": {}, 753 + "foreignKeys": { 754 + "dropbox_directories_parent_id_dropbox_directories_xata_id_fk": { 755 + "name": "dropbox_directories_parent_id_dropbox_directories_xata_id_fk", 756 + "tableFrom": "dropbox_directories", 757 + "tableTo": "dropbox_directories", 758 + "columnsFrom": [ 759 + "parent_id" 760 + ], 761 + "columnsTo": [ 762 + "xata_id" 763 + ], 764 + "onDelete": "no action", 765 + "onUpdate": "no action" 766 + } 767 + }, 768 + "compositePrimaryKeys": {}, 769 + "uniqueConstraints": { 770 + "dropbox_directories_file_id_unique": { 771 + "name": "dropbox_directories_file_id_unique", 772 + "nullsNotDistinct": false, 773 + "columns": [ 774 + "file_id" 775 + ] 776 + } 777 + }, 778 + "policies": {}, 779 + "checkConstraints": {}, 780 + "isRLSEnabled": false 781 + }, 782 + "public.dropbox_paths": { 783 + "name": "dropbox_paths", 784 + "schema": "", 785 + "columns": { 786 + "xata_id": { 787 + "name": "xata_id", 788 + "type": "text", 789 + "primaryKey": true, 790 + "notNull": true 791 + }, 792 + "path": { 793 + "name": "path", 794 + "type": "text", 795 + "primaryKey": false, 796 + "notNull": true 797 + }, 798 + "name": { 799 + "name": "name", 800 + "type": "text", 801 + "primaryKey": false, 802 + "notNull": true 803 + }, 804 + "dropbox_id": { 805 + "name": "dropbox_id", 806 + "type": "text", 807 + "primaryKey": false, 808 + "notNull": true 809 + }, 810 + "track_id": { 811 + "name": "track_id", 812 + "type": "text", 813 + "primaryKey": false, 814 + "notNull": true 815 + }, 816 + "directory_id": { 817 + "name": "directory_id", 818 + "type": "text", 819 + "primaryKey": false, 820 + "notNull": false 821 + }, 822 + "file_id": { 823 + "name": "file_id", 824 + "type": "text", 825 + "primaryKey": false, 826 + "notNull": true 827 + }, 828 + "xata_version": { 829 + "name": "xata_version", 830 + "type": "text", 831 + "primaryKey": false, 832 + "notNull": false 833 + }, 834 + "xata_createdat": { 835 + "name": "xata_createdat", 836 + "type": "timestamp", 837 + "primaryKey": false, 838 + "notNull": true, 839 + "default": "now()" 840 + }, 841 + "xata_updatedat": { 842 + "name": "xata_updatedat", 843 + "type": "timestamp", 844 + "primaryKey": false, 845 + "notNull": true, 846 + "default": "now()" 847 + } 848 + }, 849 + "indexes": {}, 850 + "foreignKeys": { 851 + "dropbox_paths_directory_id_dropbox_directories_xata_id_fk": { 852 + "name": "dropbox_paths_directory_id_dropbox_directories_xata_id_fk", 853 + "tableFrom": "dropbox_paths", 854 + "tableTo": "dropbox_directories", 855 + "columnsFrom": [ 856 + "directory_id" 857 + ], 858 + "columnsTo": [ 859 + "xata_id" 860 + ], 861 + "onDelete": "no action", 862 + "onUpdate": "no action" 863 + } 864 + }, 865 + "compositePrimaryKeys": {}, 866 + "uniqueConstraints": { 867 + "dropbox_paths_file_id_unique": { 868 + "name": "dropbox_paths_file_id_unique", 869 + "nullsNotDistinct": false, 870 + "columns": [ 871 + "file_id" 872 + ] 873 + } 874 + }, 875 + "policies": {}, 876 + "checkConstraints": {}, 877 + "isRLSEnabled": false 878 + }, 879 + "public.dropbox_tokens": { 880 + "name": "dropbox_tokens", 881 + "schema": "", 882 + "columns": { 883 + "xata_id": { 884 + "name": "xata_id", 885 + "type": "text", 886 + "primaryKey": true, 887 + "notNull": true 888 + }, 889 + "refresh_token": { 890 + "name": "refresh_token", 891 + "type": "text", 892 + "primaryKey": false, 893 + "notNull": true 894 + }, 895 + "xata_createdat": { 896 + "name": "xata_createdat", 897 + "type": "timestamp", 898 + "primaryKey": false, 899 + "notNull": true, 900 + "default": "now()" 901 + }, 902 + "xata_updatedat": { 903 + "name": "xata_updatedat", 904 + "type": "timestamp", 905 + "primaryKey": false, 906 + "notNull": true, 907 + "default": "now()" 908 + } 909 + }, 910 + "indexes": {}, 911 + "foreignKeys": {}, 912 + "compositePrimaryKeys": {}, 913 + "uniqueConstraints": {}, 914 + "policies": {}, 915 + "checkConstraints": {}, 916 + "isRLSEnabled": false 917 + }, 918 + "public.dropbox": { 919 + "name": "dropbox", 920 + "schema": "", 921 + "columns": { 922 + "xata_id": { 923 + "name": "xata_id", 924 + "type": "text", 925 + "primaryKey": true, 926 + "notNull": true 927 + }, 928 + "user_id": { 929 + "name": "user_id", 930 + "type": "text", 931 + "primaryKey": false, 932 + "notNull": true 933 + }, 934 + "dropbox_token_id": { 935 + "name": "dropbox_token_id", 936 + "type": "text", 937 + "primaryKey": false, 938 + "notNull": true 939 + }, 940 + "xata_version": { 941 + "name": "xata_version", 942 + "type": "text", 943 + "primaryKey": false, 944 + "notNull": false 945 + }, 946 + "xata_createdat": { 947 + "name": "xata_createdat", 948 + "type": "timestamp", 949 + "primaryKey": false, 950 + "notNull": true, 951 + "default": "now()" 952 + }, 953 + "xata_updatedat": { 954 + "name": "xata_updatedat", 955 + "type": "timestamp", 956 + "primaryKey": false, 957 + "notNull": true, 958 + "default": "now()" 959 + } 960 + }, 961 + "indexes": {}, 962 + "foreignKeys": { 963 + "dropbox_user_id_users_xata_id_fk": { 964 + "name": "dropbox_user_id_users_xata_id_fk", 965 + "tableFrom": "dropbox", 966 + "tableTo": "users", 967 + "columnsFrom": [ 968 + "user_id" 969 + ], 970 + "columnsTo": [ 971 + "xata_id" 972 + ], 973 + "onDelete": "no action", 974 + "onUpdate": "no action" 975 + }, 976 + "dropbox_dropbox_token_id_dropbox_tokens_xata_id_fk": { 977 + "name": "dropbox_dropbox_token_id_dropbox_tokens_xata_id_fk", 978 + "tableFrom": "dropbox", 979 + "tableTo": "dropbox_tokens", 980 + "columnsFrom": [ 981 + "dropbox_token_id" 982 + ], 983 + "columnsTo": [ 984 + "xata_id" 985 + ], 986 + "onDelete": "no action", 987 + "onUpdate": "no action" 988 + } 989 + }, 990 + "compositePrimaryKeys": {}, 991 + "uniqueConstraints": {}, 992 + "policies": {}, 993 + "checkConstraints": {}, 994 + "isRLSEnabled": false 995 + }, 996 + "public.google_drive_accounts": { 997 + "name": "google_drive_accounts", 998 + "schema": "", 999 + "columns": { 1000 + "xata_id": { 1001 + "name": "xata_id", 1002 + "type": "text", 1003 + "primaryKey": true, 1004 + "notNull": true 1005 + }, 1006 + "email": { 1007 + "name": "email", 1008 + "type": "text", 1009 + "primaryKey": false, 1010 + "notNull": true 1011 + }, 1012 + "is_beta_user": { 1013 + "name": "is_beta_user", 1014 + "type": "boolean", 1015 + "primaryKey": false, 1016 + "notNull": true, 1017 + "default": false 1018 + }, 1019 + "user_id": { 1020 + "name": "user_id", 1021 + "type": "text", 1022 + "primaryKey": false, 1023 + "notNull": true 1024 + }, 1025 + "xata_version": { 1026 + "name": "xata_version", 1027 + "type": "text", 1028 + "primaryKey": false, 1029 + "notNull": false 1030 + }, 1031 + "xata_createdat": { 1032 + "name": "xata_createdat", 1033 + "type": "timestamp", 1034 + "primaryKey": false, 1035 + "notNull": true, 1036 + "default": "now()" 1037 + }, 1038 + "xata_updatedat": { 1039 + "name": "xata_updatedat", 1040 + "type": "timestamp", 1041 + "primaryKey": false, 1042 + "notNull": true, 1043 + "default": "now()" 1044 + } 1045 + }, 1046 + "indexes": {}, 1047 + "foreignKeys": { 1048 + "google_drive_accounts_user_id_users_xata_id_fk": { 1049 + "name": "google_drive_accounts_user_id_users_xata_id_fk", 1050 + "tableFrom": "google_drive_accounts", 1051 + "tableTo": "users", 1052 + "columnsFrom": [ 1053 + "user_id" 1054 + ], 1055 + "columnsTo": [ 1056 + "xata_id" 1057 + ], 1058 + "onDelete": "no action", 1059 + "onUpdate": "no action" 1060 + } 1061 + }, 1062 + "compositePrimaryKeys": {}, 1063 + "uniqueConstraints": { 1064 + "google_drive_accounts_email_unique": { 1065 + "name": "google_drive_accounts_email_unique", 1066 + "nullsNotDistinct": false, 1067 + "columns": [ 1068 + "email" 1069 + ] 1070 + } 1071 + }, 1072 + "policies": {}, 1073 + "checkConstraints": {}, 1074 + "isRLSEnabled": false 1075 + }, 1076 + "public.google_drive_directories": { 1077 + "name": "google_drive_directories", 1078 + "schema": "", 1079 + "columns": { 1080 + "xata_id": { 1081 + "name": "xata_id", 1082 + "type": "text", 1083 + "primaryKey": true, 1084 + "notNull": true 1085 + }, 1086 + "name": { 1087 + "name": "name", 1088 + "type": "text", 1089 + "primaryKey": false, 1090 + "notNull": true 1091 + }, 1092 + "path": { 1093 + "name": "path", 1094 + "type": "text", 1095 + "primaryKey": false, 1096 + "notNull": true 1097 + }, 1098 + "parent_id": { 1099 + "name": "parent_id", 1100 + "type": "text", 1101 + "primaryKey": false, 1102 + "notNull": false 1103 + }, 1104 + "google_drive_id": { 1105 + "name": "google_drive_id", 1106 + "type": "text", 1107 + "primaryKey": false, 1108 + "notNull": true 1109 + }, 1110 + "file_id": { 1111 + "name": "file_id", 1112 + "type": "text", 1113 + "primaryKey": false, 1114 + "notNull": true 1115 + }, 1116 + "xata_version": { 1117 + "name": "xata_version", 1118 + "type": "text", 1119 + "primaryKey": false, 1120 + "notNull": false 1121 + }, 1122 + "xata_createdat": { 1123 + "name": "xata_createdat", 1124 + "type": "timestamp", 1125 + "primaryKey": false, 1126 + "notNull": true, 1127 + "default": "now()" 1128 + }, 1129 + "xata_updatedat": { 1130 + "name": "xata_updatedat", 1131 + "type": "timestamp", 1132 + "primaryKey": false, 1133 + "notNull": true, 1134 + "default": "now()" 1135 + } 1136 + }, 1137 + "indexes": {}, 1138 + "foreignKeys": { 1139 + "google_drive_directories_parent_id_google_drive_directories_xata_id_fk": { 1140 + "name": "google_drive_directories_parent_id_google_drive_directories_xata_id_fk", 1141 + "tableFrom": "google_drive_directories", 1142 + "tableTo": "google_drive_directories", 1143 + "columnsFrom": [ 1144 + "parent_id" 1145 + ], 1146 + "columnsTo": [ 1147 + "xata_id" 1148 + ], 1149 + "onDelete": "no action", 1150 + "onUpdate": "no action" 1151 + } 1152 + }, 1153 + "compositePrimaryKeys": {}, 1154 + "uniqueConstraints": { 1155 + "google_drive_directories_file_id_unique": { 1156 + "name": "google_drive_directories_file_id_unique", 1157 + "nullsNotDistinct": false, 1158 + "columns": [ 1159 + "file_id" 1160 + ] 1161 + } 1162 + }, 1163 + "policies": {}, 1164 + "checkConstraints": {}, 1165 + "isRLSEnabled": false 1166 + }, 1167 + "public.google_drive_paths": { 1168 + "name": "google_drive_paths", 1169 + "schema": "", 1170 + "columns": { 1171 + "xata_id": { 1172 + "name": "xata_id", 1173 + "type": "text", 1174 + "primaryKey": true, 1175 + "notNull": true 1176 + }, 1177 + "google_drive_id": { 1178 + "name": "google_drive_id", 1179 + "type": "text", 1180 + "primaryKey": false, 1181 + "notNull": true 1182 + }, 1183 + "track_id": { 1184 + "name": "track_id", 1185 + "type": "text", 1186 + "primaryKey": false, 1187 + "notNull": true 1188 + }, 1189 + "name": { 1190 + "name": "name", 1191 + "type": "text", 1192 + "primaryKey": false, 1193 + "notNull": true 1194 + }, 1195 + "directory_id": { 1196 + "name": "directory_id", 1197 + "type": "text", 1198 + "primaryKey": false, 1199 + "notNull": false 1200 + }, 1201 + "file_id": { 1202 + "name": "file_id", 1203 + "type": "text", 1204 + "primaryKey": false, 1205 + "notNull": true 1206 + }, 1207 + "xata_version": { 1208 + "name": "xata_version", 1209 + "type": "text", 1210 + "primaryKey": false, 1211 + "notNull": false 1212 + }, 1213 + "xata_createdat": { 1214 + "name": "xata_createdat", 1215 + "type": "timestamp", 1216 + "primaryKey": false, 1217 + "notNull": true, 1218 + "default": "now()" 1219 + }, 1220 + "xata_updatedat": { 1221 + "name": "xata_updatedat", 1222 + "type": "timestamp", 1223 + "primaryKey": false, 1224 + "notNull": true, 1225 + "default": "now()" 1226 + } 1227 + }, 1228 + "indexes": {}, 1229 + "foreignKeys": { 1230 + "google_drive_paths_directory_id_google_drive_directories_xata_id_fk": { 1231 + "name": "google_drive_paths_directory_id_google_drive_directories_xata_id_fk", 1232 + "tableFrom": "google_drive_paths", 1233 + "tableTo": "google_drive_directories", 1234 + "columnsFrom": [ 1235 + "directory_id" 1236 + ], 1237 + "columnsTo": [ 1238 + "xata_id" 1239 + ], 1240 + "onDelete": "no action", 1241 + "onUpdate": "no action" 1242 + } 1243 + }, 1244 + "compositePrimaryKeys": {}, 1245 + "uniqueConstraints": { 1246 + "google_drive_paths_file_id_unique": { 1247 + "name": "google_drive_paths_file_id_unique", 1248 + "nullsNotDistinct": false, 1249 + "columns": [ 1250 + "file_id" 1251 + ] 1252 + } 1253 + }, 1254 + "policies": {}, 1255 + "checkConstraints": {}, 1256 + "isRLSEnabled": false 1257 + }, 1258 + "public.google_drive_tokens": { 1259 + "name": "google_drive_tokens", 1260 + "schema": "", 1261 + "columns": { 1262 + "xata_id": { 1263 + "name": "xata_id", 1264 + "type": "text", 1265 + "primaryKey": true, 1266 + "notNull": true 1267 + }, 1268 + "refresh_token": { 1269 + "name": "refresh_token", 1270 + "type": "text", 1271 + "primaryKey": false, 1272 + "notNull": true 1273 + }, 1274 + "xata_createdat": { 1275 + "name": "xata_createdat", 1276 + "type": "timestamp", 1277 + "primaryKey": false, 1278 + "notNull": true, 1279 + "default": "now()" 1280 + }, 1281 + "xata_updatedat": { 1282 + "name": "xata_updatedat", 1283 + "type": "timestamp", 1284 + "primaryKey": false, 1285 + "notNull": true, 1286 + "default": "now()" 1287 + } 1288 + }, 1289 + "indexes": {}, 1290 + "foreignKeys": {}, 1291 + "compositePrimaryKeys": {}, 1292 + "uniqueConstraints": {}, 1293 + "policies": {}, 1294 + "checkConstraints": {}, 1295 + "isRLSEnabled": false 1296 + }, 1297 + "public.google_drive": { 1298 + "name": "google_drive", 1299 + "schema": "", 1300 + "columns": { 1301 + "xata_id": { 1302 + "name": "xata_id", 1303 + "type": "text", 1304 + "primaryKey": true, 1305 + "notNull": true 1306 + }, 1307 + "google_drive_token_id": { 1308 + "name": "google_drive_token_id", 1309 + "type": "text", 1310 + "primaryKey": false, 1311 + "notNull": true 1312 + }, 1313 + "user_id": { 1314 + "name": "user_id", 1315 + "type": "text", 1316 + "primaryKey": false, 1317 + "notNull": true 1318 + }, 1319 + "xata_version": { 1320 + "name": "xata_version", 1321 + "type": "text", 1322 + "primaryKey": false, 1323 + "notNull": false 1324 + }, 1325 + "xata_createdat": { 1326 + "name": "xata_createdat", 1327 + "type": "timestamp", 1328 + "primaryKey": false, 1329 + "notNull": true, 1330 + "default": "now()" 1331 + }, 1332 + "xata_updatedat": { 1333 + "name": "xata_updatedat", 1334 + "type": "timestamp", 1335 + "primaryKey": false, 1336 + "notNull": true, 1337 + "default": "now()" 1338 + } 1339 + }, 1340 + "indexes": {}, 1341 + "foreignKeys": { 1342 + "google_drive_google_drive_token_id_google_drive_tokens_xata_id_fk": { 1343 + "name": "google_drive_google_drive_token_id_google_drive_tokens_xata_id_fk", 1344 + "tableFrom": "google_drive", 1345 + "tableTo": "google_drive_tokens", 1346 + "columnsFrom": [ 1347 + "google_drive_token_id" 1348 + ], 1349 + "columnsTo": [ 1350 + "xata_id" 1351 + ], 1352 + "onDelete": "no action", 1353 + "onUpdate": "no action" 1354 + }, 1355 + "google_drive_user_id_users_xata_id_fk": { 1356 + "name": "google_drive_user_id_users_xata_id_fk", 1357 + "tableFrom": "google_drive", 1358 + "tableTo": "users", 1359 + "columnsFrom": [ 1360 + "user_id" 1361 + ], 1362 + "columnsTo": [ 1363 + "xata_id" 1364 + ], 1365 + "onDelete": "no action", 1366 + "onUpdate": "no action" 1367 + } 1368 + }, 1369 + "compositePrimaryKeys": {}, 1370 + "uniqueConstraints": {}, 1371 + "policies": {}, 1372 + "checkConstraints": {}, 1373 + "isRLSEnabled": false 1374 + }, 1375 + "public.loved_tracks": { 1376 + "name": "loved_tracks", 1377 + "schema": "", 1378 + "columns": { 1379 + "xata_id": { 1380 + "name": "xata_id", 1381 + "type": "text", 1382 + "primaryKey": true, 1383 + "notNull": true 1384 + }, 1385 + "user_id": { 1386 + "name": "user_id", 1387 + "type": "text", 1388 + "primaryKey": false, 1389 + "notNull": true 1390 + }, 1391 + "track_id": { 1392 + "name": "track_id", 1393 + "type": "text", 1394 + "primaryKey": false, 1395 + "notNull": true 1396 + }, 1397 + "uri": { 1398 + "name": "uri", 1399 + "type": "text", 1400 + "primaryKey": false, 1401 + "notNull": false 1402 + }, 1403 + "xata_createdat": { 1404 + "name": "xata_createdat", 1405 + "type": "timestamp", 1406 + "primaryKey": false, 1407 + "notNull": true, 1408 + "default": "now()" 1409 + } 1410 + }, 1411 + "indexes": {}, 1412 + "foreignKeys": { 1413 + "loved_tracks_user_id_users_xata_id_fk": { 1414 + "name": "loved_tracks_user_id_users_xata_id_fk", 1415 + "tableFrom": "loved_tracks", 1416 + "tableTo": "users", 1417 + "columnsFrom": [ 1418 + "user_id" 1419 + ], 1420 + "columnsTo": [ 1421 + "xata_id" 1422 + ], 1423 + "onDelete": "no action", 1424 + "onUpdate": "no action" 1425 + }, 1426 + "loved_tracks_track_id_tracks_xata_id_fk": { 1427 + "name": "loved_tracks_track_id_tracks_xata_id_fk", 1428 + "tableFrom": "loved_tracks", 1429 + "tableTo": "tracks", 1430 + "columnsFrom": [ 1431 + "track_id" 1432 + ], 1433 + "columnsTo": [ 1434 + "xata_id" 1435 + ], 1436 + "onDelete": "no action", 1437 + "onUpdate": "no action" 1438 + } 1439 + }, 1440 + "compositePrimaryKeys": {}, 1441 + "uniqueConstraints": { 1442 + "loved_tracks_uri_unique": { 1443 + "name": "loved_tracks_uri_unique", 1444 + "nullsNotDistinct": false, 1445 + "columns": [ 1446 + "uri" 1447 + ] 1448 + } 1449 + }, 1450 + "policies": {}, 1451 + "checkConstraints": {}, 1452 + "isRLSEnabled": false 1453 + }, 1454 + "public.playlist_tracks": { 1455 + "name": "playlist_tracks", 1456 + "schema": "", 1457 + "columns": { 1458 + "xata_id": { 1459 + "name": "xata_id", 1460 + "type": "text", 1461 + "primaryKey": true, 1462 + "notNull": true 1463 + }, 1464 + "playlist_id": { 1465 + "name": "playlist_id", 1466 + "type": "text", 1467 + "primaryKey": false, 1468 + "notNull": true 1469 + }, 1470 + "track_id": { 1471 + "name": "track_id", 1472 + "type": "text", 1473 + "primaryKey": false, 1474 + "notNull": true 1475 + }, 1476 + "xata_createdat": { 1477 + "name": "xata_createdat", 1478 + "type": "timestamp", 1479 + "primaryKey": false, 1480 + "notNull": true, 1481 + "default": "now()" 1482 + } 1483 + }, 1484 + "indexes": {}, 1485 + "foreignKeys": { 1486 + "playlist_tracks_playlist_id_playlists_xata_id_fk": { 1487 + "name": "playlist_tracks_playlist_id_playlists_xata_id_fk", 1488 + "tableFrom": "playlist_tracks", 1489 + "tableTo": "playlists", 1490 + "columnsFrom": [ 1491 + "playlist_id" 1492 + ], 1493 + "columnsTo": [ 1494 + "xata_id" 1495 + ], 1496 + "onDelete": "no action", 1497 + "onUpdate": "no action" 1498 + }, 1499 + "playlist_tracks_track_id_tracks_xata_id_fk": { 1500 + "name": "playlist_tracks_track_id_tracks_xata_id_fk", 1501 + "tableFrom": "playlist_tracks", 1502 + "tableTo": "tracks", 1503 + "columnsFrom": [ 1504 + "track_id" 1505 + ], 1506 + "columnsTo": [ 1507 + "xata_id" 1508 + ], 1509 + "onDelete": "no action", 1510 + "onUpdate": "no action" 1511 + } 1512 + }, 1513 + "compositePrimaryKeys": {}, 1514 + "uniqueConstraints": {}, 1515 + "policies": {}, 1516 + "checkConstraints": {}, 1517 + "isRLSEnabled": false 1518 + }, 1519 + "public.playlists": { 1520 + "name": "playlists", 1521 + "schema": "", 1522 + "columns": { 1523 + "xata_id": { 1524 + "name": "xata_id", 1525 + "type": "text", 1526 + "primaryKey": true, 1527 + "notNull": true 1528 + }, 1529 + "name": { 1530 + "name": "name", 1531 + "type": "text", 1532 + "primaryKey": false, 1533 + "notNull": true 1534 + }, 1535 + "picture": { 1536 + "name": "picture", 1537 + "type": "text", 1538 + "primaryKey": false, 1539 + "notNull": false 1540 + }, 1541 + "description": { 1542 + "name": "description", 1543 + "type": "text", 1544 + "primaryKey": false, 1545 + "notNull": false 1546 + }, 1547 + "uri": { 1548 + "name": "uri", 1549 + "type": "text", 1550 + "primaryKey": false, 1551 + "notNull": false 1552 + }, 1553 + "spotify_link": { 1554 + "name": "spotify_link", 1555 + "type": "text", 1556 + "primaryKey": false, 1557 + "notNull": false 1558 + }, 1559 + "tidal_link": { 1560 + "name": "tidal_link", 1561 + "type": "text", 1562 + "primaryKey": false, 1563 + "notNull": false 1564 + }, 1565 + "apple_music_link": { 1566 + "name": "apple_music_link", 1567 + "type": "text", 1568 + "primaryKey": false, 1569 + "notNull": false 1570 + }, 1571 + "created_by": { 1572 + "name": "created_by", 1573 + "type": "text", 1574 + "primaryKey": false, 1575 + "notNull": true 1576 + }, 1577 + "xata_createdat": { 1578 + "name": "xata_createdat", 1579 + "type": "timestamp", 1580 + "primaryKey": false, 1581 + "notNull": true, 1582 + "default": "now()" 1583 + }, 1584 + "xata_updatedat": { 1585 + "name": "xata_updatedat", 1586 + "type": "timestamp", 1587 + "primaryKey": false, 1588 + "notNull": true, 1589 + "default": "now()" 1590 + } 1591 + }, 1592 + "indexes": {}, 1593 + "foreignKeys": { 1594 + "playlists_created_by_users_xata_id_fk": { 1595 + "name": "playlists_created_by_users_xata_id_fk", 1596 + "tableFrom": "playlists", 1597 + "tableTo": "users", 1598 + "columnsFrom": [ 1599 + "created_by" 1600 + ], 1601 + "columnsTo": [ 1602 + "xata_id" 1603 + ], 1604 + "onDelete": "no action", 1605 + "onUpdate": "no action" 1606 + } 1607 + }, 1608 + "compositePrimaryKeys": {}, 1609 + "uniqueConstraints": { 1610 + "playlists_uri_unique": { 1611 + "name": "playlists_uri_unique", 1612 + "nullsNotDistinct": false, 1613 + "columns": [ 1614 + "uri" 1615 + ] 1616 + } 1617 + }, 1618 + "policies": {}, 1619 + "checkConstraints": {}, 1620 + "isRLSEnabled": false 1621 + }, 1622 + "public.profile_shouts": { 1623 + "name": "profile_shouts", 1624 + "schema": "", 1625 + "columns": { 1626 + "xata_id": { 1627 + "name": "xata_id", 1628 + "type": "text", 1629 + "primaryKey": true, 1630 + "notNull": true 1631 + }, 1632 + "user_id": { 1633 + "name": "user_id", 1634 + "type": "text", 1635 + "primaryKey": false, 1636 + "notNull": true 1637 + }, 1638 + "shout_id": { 1639 + "name": "shout_id", 1640 + "type": "text", 1641 + "primaryKey": false, 1642 + "notNull": true 1643 + }, 1644 + "xata_createdat": { 1645 + "name": "xata_createdat", 1646 + "type": "timestamp", 1647 + "primaryKey": false, 1648 + "notNull": true, 1649 + "default": "now()" 1650 + } 1651 + }, 1652 + "indexes": {}, 1653 + "foreignKeys": { 1654 + "profile_shouts_user_id_users_xata_id_fk": { 1655 + "name": "profile_shouts_user_id_users_xata_id_fk", 1656 + "tableFrom": "profile_shouts", 1657 + "tableTo": "users", 1658 + "columnsFrom": [ 1659 + "user_id" 1660 + ], 1661 + "columnsTo": [ 1662 + "xata_id" 1663 + ], 1664 + "onDelete": "no action", 1665 + "onUpdate": "no action" 1666 + }, 1667 + "profile_shouts_shout_id_shouts_xata_id_fk": { 1668 + "name": "profile_shouts_shout_id_shouts_xata_id_fk", 1669 + "tableFrom": "profile_shouts", 1670 + "tableTo": "shouts", 1671 + "columnsFrom": [ 1672 + "shout_id" 1673 + ], 1674 + "columnsTo": [ 1675 + "xata_id" 1676 + ], 1677 + "onDelete": "no action", 1678 + "onUpdate": "no action" 1679 + } 1680 + }, 1681 + "compositePrimaryKeys": {}, 1682 + "uniqueConstraints": {}, 1683 + "policies": {}, 1684 + "checkConstraints": {}, 1685 + "isRLSEnabled": false 1686 + }, 1687 + "public.queue_tracks": { 1688 + "name": "queue_tracks", 1689 + "schema": "", 1690 + "columns": { 1691 + "xata_id": { 1692 + "name": "xata_id", 1693 + "type": "text", 1694 + "primaryKey": true, 1695 + "notNull": true 1696 + }, 1697 + "user_id": { 1698 + "name": "user_id", 1699 + "type": "text", 1700 + "primaryKey": false, 1701 + "notNull": true 1702 + }, 1703 + "track_id": { 1704 + "name": "track_id", 1705 + "type": "text", 1706 + "primaryKey": false, 1707 + "notNull": true 1708 + }, 1709 + "position": { 1710 + "name": "position", 1711 + "type": "integer", 1712 + "primaryKey": false, 1713 + "notNull": true 1714 + }, 1715 + "file_uri": { 1716 + "name": "file_uri", 1717 + "type": "text", 1718 + "primaryKey": false, 1719 + "notNull": true 1720 + }, 1721 + "xata_version": { 1722 + "name": "xata_version", 1723 + "type": "integer", 1724 + "primaryKey": false, 1725 + "notNull": true, 1726 + "default": 0 1727 + }, 1728 + "xata_createdat": { 1729 + "name": "xata_createdat", 1730 + "type": "timestamp", 1731 + "primaryKey": false, 1732 + "notNull": true, 1733 + "default": "now()" 1734 + }, 1735 + "xata_updatedat": { 1736 + "name": "xata_updatedat", 1737 + "type": "timestamp", 1738 + "primaryKey": false, 1739 + "notNull": true, 1740 + "default": "now()" 1741 + } 1742 + }, 1743 + "indexes": {}, 1744 + "foreignKeys": { 1745 + "queue_tracks_user_id_users_xata_id_fk": { 1746 + "name": "queue_tracks_user_id_users_xata_id_fk", 1747 + "tableFrom": "queue_tracks", 1748 + "tableTo": "users", 1749 + "columnsFrom": [ 1750 + "user_id" 1751 + ], 1752 + "columnsTo": [ 1753 + "xata_id" 1754 + ], 1755 + "onDelete": "no action", 1756 + "onUpdate": "no action" 1757 + }, 1758 + "queue_tracks_track_id_tracks_xata_id_fk": { 1759 + "name": "queue_tracks_track_id_tracks_xata_id_fk", 1760 + "tableFrom": "queue_tracks", 1761 + "tableTo": "tracks", 1762 + "columnsFrom": [ 1763 + "track_id" 1764 + ], 1765 + "columnsTo": [ 1766 + "xata_id" 1767 + ], 1768 + "onDelete": "no action", 1769 + "onUpdate": "no action" 1770 + } 1771 + }, 1772 + "compositePrimaryKeys": {}, 1773 + "uniqueConstraints": {}, 1774 + "policies": {}, 1775 + "checkConstraints": {}, 1776 + "isRLSEnabled": false 1777 + }, 1778 + "public.scrobbles": { 1779 + "name": "scrobbles", 1780 + "schema": "", 1781 + "columns": { 1782 + "xata_id": { 1783 + "name": "xata_id", 1784 + "type": "text", 1785 + "primaryKey": true, 1786 + "notNull": true 1787 + }, 1788 + "user_id": { 1789 + "name": "user_id", 1790 + "type": "text", 1791 + "primaryKey": false, 1792 + "notNull": false 1793 + }, 1794 + "track_id": { 1795 + "name": "track_id", 1796 + "type": "text", 1797 + "primaryKey": false, 1798 + "notNull": false 1799 + }, 1800 + "album_id": { 1801 + "name": "album_id", 1802 + "type": "text", 1803 + "primaryKey": false, 1804 + "notNull": false 1805 + }, 1806 + "artist_id": { 1807 + "name": "artist_id", 1808 + "type": "text", 1809 + "primaryKey": false, 1810 + "notNull": false 1811 + }, 1812 + "uri": { 1813 + "name": "uri", 1814 + "type": "text", 1815 + "primaryKey": false, 1816 + "notNull": false 1817 + }, 1818 + "xata_createdat": { 1819 + "name": "xata_createdat", 1820 + "type": "timestamp", 1821 + "primaryKey": false, 1822 + "notNull": true, 1823 + "default": "now()" 1824 + }, 1825 + "xata_updatedat": { 1826 + "name": "xata_updatedat", 1827 + "type": "timestamp", 1828 + "primaryKey": false, 1829 + "notNull": true, 1830 + "default": "now()" 1831 + }, 1832 + "xata_version": { 1833 + "name": "xata_version", 1834 + "type": "integer", 1835 + "primaryKey": false, 1836 + "notNull": false 1837 + }, 1838 + "timestamp": { 1839 + "name": "timestamp", 1840 + "type": "timestamp", 1841 + "primaryKey": false, 1842 + "notNull": true, 1843 + "default": "now()" 1844 + } 1845 + }, 1846 + "indexes": {}, 1847 + "foreignKeys": { 1848 + "scrobbles_user_id_users_xata_id_fk": { 1849 + "name": "scrobbles_user_id_users_xata_id_fk", 1850 + "tableFrom": "scrobbles", 1851 + "tableTo": "users", 1852 + "columnsFrom": [ 1853 + "user_id" 1854 + ], 1855 + "columnsTo": [ 1856 + "xata_id" 1857 + ], 1858 + "onDelete": "no action", 1859 + "onUpdate": "no action" 1860 + }, 1861 + "scrobbles_track_id_tracks_xata_id_fk": { 1862 + "name": "scrobbles_track_id_tracks_xata_id_fk", 1863 + "tableFrom": "scrobbles", 1864 + "tableTo": "tracks", 1865 + "columnsFrom": [ 1866 + "track_id" 1867 + ], 1868 + "columnsTo": [ 1869 + "xata_id" 1870 + ], 1871 + "onDelete": "no action", 1872 + "onUpdate": "no action" 1873 + }, 1874 + "scrobbles_album_id_albums_xata_id_fk": { 1875 + "name": "scrobbles_album_id_albums_xata_id_fk", 1876 + "tableFrom": "scrobbles", 1877 + "tableTo": "albums", 1878 + "columnsFrom": [ 1879 + "album_id" 1880 + ], 1881 + "columnsTo": [ 1882 + "xata_id" 1883 + ], 1884 + "onDelete": "no action", 1885 + "onUpdate": "no action" 1886 + }, 1887 + "scrobbles_artist_id_artists_xata_id_fk": { 1888 + "name": "scrobbles_artist_id_artists_xata_id_fk", 1889 + "tableFrom": "scrobbles", 1890 + "tableTo": "artists", 1891 + "columnsFrom": [ 1892 + "artist_id" 1893 + ], 1894 + "columnsTo": [ 1895 + "xata_id" 1896 + ], 1897 + "onDelete": "no action", 1898 + "onUpdate": "no action" 1899 + } 1900 + }, 1901 + "compositePrimaryKeys": {}, 1902 + "uniqueConstraints": { 1903 + "scrobbles_uri_unique": { 1904 + "name": "scrobbles_uri_unique", 1905 + "nullsNotDistinct": false, 1906 + "columns": [ 1907 + "uri" 1908 + ] 1909 + } 1910 + }, 1911 + "policies": {}, 1912 + "checkConstraints": {}, 1913 + "isRLSEnabled": false 1914 + }, 1915 + "public.shout_likes": { 1916 + "name": "shout_likes", 1917 + "schema": "", 1918 + "columns": { 1919 + "xata_id": { 1920 + "name": "xata_id", 1921 + "type": "text", 1922 + "primaryKey": true, 1923 + "notNull": true 1924 + }, 1925 + "user_id": { 1926 + "name": "user_id", 1927 + "type": "text", 1928 + "primaryKey": false, 1929 + "notNull": true 1930 + }, 1931 + "shout_id": { 1932 + "name": "shout_id", 1933 + "type": "text", 1934 + "primaryKey": false, 1935 + "notNull": true 1936 + }, 1937 + "xata_createdat": { 1938 + "name": "xata_createdat", 1939 + "type": "timestamp", 1940 + "primaryKey": false, 1941 + "notNull": true, 1942 + "default": "now()" 1943 + }, 1944 + "uri": { 1945 + "name": "uri", 1946 + "type": "text", 1947 + "primaryKey": false, 1948 + "notNull": true 1949 + } 1950 + }, 1951 + "indexes": {}, 1952 + "foreignKeys": { 1953 + "shout_likes_user_id_users_xata_id_fk": { 1954 + "name": "shout_likes_user_id_users_xata_id_fk", 1955 + "tableFrom": "shout_likes", 1956 + "tableTo": "users", 1957 + "columnsFrom": [ 1958 + "user_id" 1959 + ], 1960 + "columnsTo": [ 1961 + "xata_id" 1962 + ], 1963 + "onDelete": "no action", 1964 + "onUpdate": "no action" 1965 + }, 1966 + "shout_likes_shout_id_shouts_xata_id_fk": { 1967 + "name": "shout_likes_shout_id_shouts_xata_id_fk", 1968 + "tableFrom": "shout_likes", 1969 + "tableTo": "shouts", 1970 + "columnsFrom": [ 1971 + "shout_id" 1972 + ], 1973 + "columnsTo": [ 1974 + "xata_id" 1975 + ], 1976 + "onDelete": "no action", 1977 + "onUpdate": "no action" 1978 + } 1979 + }, 1980 + "compositePrimaryKeys": {}, 1981 + "uniqueConstraints": { 1982 + "shout_likes_uri_unique": { 1983 + "name": "shout_likes_uri_unique", 1984 + "nullsNotDistinct": false, 1985 + "columns": [ 1986 + "uri" 1987 + ] 1988 + } 1989 + }, 1990 + "policies": {}, 1991 + "checkConstraints": {}, 1992 + "isRLSEnabled": false 1993 + }, 1994 + "public.shout_reports": { 1995 + "name": "shout_reports", 1996 + "schema": "", 1997 + "columns": { 1998 + "xata_id": { 1999 + "name": "xata_id", 2000 + "type": "text", 2001 + "primaryKey": true, 2002 + "notNull": true 2003 + }, 2004 + "user_id": { 2005 + "name": "user_id", 2006 + "type": "text", 2007 + "primaryKey": false, 2008 + "notNull": true 2009 + }, 2010 + "shout_id": { 2011 + "name": "shout_id", 2012 + "type": "text", 2013 + "primaryKey": false, 2014 + "notNull": true 2015 + }, 2016 + "xata_createdat": { 2017 + "name": "xata_createdat", 2018 + "type": "timestamp", 2019 + "primaryKey": false, 2020 + "notNull": true, 2021 + "default": "now()" 2022 + } 2023 + }, 2024 + "indexes": {}, 2025 + "foreignKeys": { 2026 + "shout_reports_user_id_users_xata_id_fk": { 2027 + "name": "shout_reports_user_id_users_xata_id_fk", 2028 + "tableFrom": "shout_reports", 2029 + "tableTo": "users", 2030 + "columnsFrom": [ 2031 + "user_id" 2032 + ], 2033 + "columnsTo": [ 2034 + "xata_id" 2035 + ], 2036 + "onDelete": "no action", 2037 + "onUpdate": "no action" 2038 + }, 2039 + "shout_reports_shout_id_shouts_xata_id_fk": { 2040 + "name": "shout_reports_shout_id_shouts_xata_id_fk", 2041 + "tableFrom": "shout_reports", 2042 + "tableTo": "shouts", 2043 + "columnsFrom": [ 2044 + "shout_id" 2045 + ], 2046 + "columnsTo": [ 2047 + "xata_id" 2048 + ], 2049 + "onDelete": "no action", 2050 + "onUpdate": "no action" 2051 + } 2052 + }, 2053 + "compositePrimaryKeys": {}, 2054 + "uniqueConstraints": {}, 2055 + "policies": {}, 2056 + "checkConstraints": {}, 2057 + "isRLSEnabled": false 2058 + }, 2059 + "public.shouts": { 2060 + "name": "shouts", 2061 + "schema": "", 2062 + "columns": { 2063 + "xata_id": { 2064 + "name": "xata_id", 2065 + "type": "text", 2066 + "primaryKey": true, 2067 + "notNull": true 2068 + }, 2069 + "content": { 2070 + "name": "content", 2071 + "type": "text", 2072 + "primaryKey": false, 2073 + "notNull": true 2074 + }, 2075 + "track_id": { 2076 + "name": "track_id", 2077 + "type": "text", 2078 + "primaryKey": false, 2079 + "notNull": false 2080 + }, 2081 + "artist_id": { 2082 + "name": "artist_id", 2083 + "type": "text", 2084 + "primaryKey": false, 2085 + "notNull": false 2086 + }, 2087 + "album_id": { 2088 + "name": "album_id", 2089 + "type": "text", 2090 + "primaryKey": false, 2091 + "notNull": false 2092 + }, 2093 + "scrobble_id": { 2094 + "name": "scrobble_id", 2095 + "type": "text", 2096 + "primaryKey": false, 2097 + "notNull": false 2098 + }, 2099 + "uri": { 2100 + "name": "uri", 2101 + "type": "text", 2102 + "primaryKey": false, 2103 + "notNull": true 2104 + }, 2105 + "author_id": { 2106 + "name": "author_id", 2107 + "type": "text", 2108 + "primaryKey": false, 2109 + "notNull": true 2110 + }, 2111 + "parent_id": { 2112 + "name": "parent_id", 2113 + "type": "text", 2114 + "primaryKey": false, 2115 + "notNull": false 2116 + }, 2117 + "xata_createdat": { 2118 + "name": "xata_createdat", 2119 + "type": "timestamp", 2120 + "primaryKey": false, 2121 + "notNull": true, 2122 + "default": "now()" 2123 + }, 2124 + "xata_updatedat": { 2125 + "name": "xata_updatedat", 2126 + "type": "timestamp", 2127 + "primaryKey": false, 2128 + "notNull": true, 2129 + "default": "now()" 2130 + } 2131 + }, 2132 + "indexes": {}, 2133 + "foreignKeys": { 2134 + "shouts_track_id_tracks_xata_id_fk": { 2135 + "name": "shouts_track_id_tracks_xata_id_fk", 2136 + "tableFrom": "shouts", 2137 + "tableTo": "tracks", 2138 + "columnsFrom": [ 2139 + "track_id" 2140 + ], 2141 + "columnsTo": [ 2142 + "xata_id" 2143 + ], 2144 + "onDelete": "no action", 2145 + "onUpdate": "no action" 2146 + }, 2147 + "shouts_artist_id_users_xata_id_fk": { 2148 + "name": "shouts_artist_id_users_xata_id_fk", 2149 + "tableFrom": "shouts", 2150 + "tableTo": "users", 2151 + "columnsFrom": [ 2152 + "artist_id" 2153 + ], 2154 + "columnsTo": [ 2155 + "xata_id" 2156 + ], 2157 + "onDelete": "no action", 2158 + "onUpdate": "no action" 2159 + }, 2160 + "shouts_album_id_albums_xata_id_fk": { 2161 + "name": "shouts_album_id_albums_xata_id_fk", 2162 + "tableFrom": "shouts", 2163 + "tableTo": "albums", 2164 + "columnsFrom": [ 2165 + "album_id" 2166 + ], 2167 + "columnsTo": [ 2168 + "xata_id" 2169 + ], 2170 + "onDelete": "no action", 2171 + "onUpdate": "no action" 2172 + }, 2173 + "shouts_scrobble_id_scrobbles_xata_id_fk": { 2174 + "name": "shouts_scrobble_id_scrobbles_xata_id_fk", 2175 + "tableFrom": "shouts", 2176 + "tableTo": "scrobbles", 2177 + "columnsFrom": [ 2178 + "scrobble_id" 2179 + ], 2180 + "columnsTo": [ 2181 + "xata_id" 2182 + ], 2183 + "onDelete": "no action", 2184 + "onUpdate": "no action" 2185 + }, 2186 + "shouts_author_id_users_xata_id_fk": { 2187 + "name": "shouts_author_id_users_xata_id_fk", 2188 + "tableFrom": "shouts", 2189 + "tableTo": "users", 2190 + "columnsFrom": [ 2191 + "author_id" 2192 + ], 2193 + "columnsTo": [ 2194 + "xata_id" 2195 + ], 2196 + "onDelete": "no action", 2197 + "onUpdate": "no action" 2198 + }, 2199 + "shouts_parent_id_shouts_xata_id_fk": { 2200 + "name": "shouts_parent_id_shouts_xata_id_fk", 2201 + "tableFrom": "shouts", 2202 + "tableTo": "shouts", 2203 + "columnsFrom": [ 2204 + "parent_id" 2205 + ], 2206 + "columnsTo": [ 2207 + "xata_id" 2208 + ], 2209 + "onDelete": "no action", 2210 + "onUpdate": "no action" 2211 + } 2212 + }, 2213 + "compositePrimaryKeys": {}, 2214 + "uniqueConstraints": { 2215 + "shouts_uri_unique": { 2216 + "name": "shouts_uri_unique", 2217 + "nullsNotDistinct": false, 2218 + "columns": [ 2219 + "uri" 2220 + ] 2221 + } 2222 + }, 2223 + "policies": {}, 2224 + "checkConstraints": {}, 2225 + "isRLSEnabled": false 2226 + }, 2227 + "public.spotify_accounts": { 2228 + "name": "spotify_accounts", 2229 + "schema": "", 2230 + "columns": { 2231 + "xata_id": { 2232 + "name": "xata_id", 2233 + "type": "text", 2234 + "primaryKey": true, 2235 + "notNull": true 2236 + }, 2237 + "xata_version": { 2238 + "name": "xata_version", 2239 + "type": "integer", 2240 + "primaryKey": false, 2241 + "notNull": false 2242 + }, 2243 + "email": { 2244 + "name": "email", 2245 + "type": "text", 2246 + "primaryKey": false, 2247 + "notNull": true 2248 + }, 2249 + "user_id": { 2250 + "name": "user_id", 2251 + "type": "text", 2252 + "primaryKey": false, 2253 + "notNull": true 2254 + }, 2255 + "is_beta_user": { 2256 + "name": "is_beta_user", 2257 + "type": "boolean", 2258 + "primaryKey": false, 2259 + "notNull": true, 2260 + "default": false 2261 + }, 2262 + "xata_createdat": { 2263 + "name": "xata_createdat", 2264 + "type": "timestamp", 2265 + "primaryKey": false, 2266 + "notNull": true, 2267 + "default": "now()" 2268 + }, 2269 + "xata_updatedat": { 2270 + "name": "xata_updatedat", 2271 + "type": "timestamp", 2272 + "primaryKey": false, 2273 + "notNull": true, 2274 + "default": "now()" 2275 + } 2276 + }, 2277 + "indexes": {}, 2278 + "foreignKeys": { 2279 + "spotify_accounts_user_id_users_xata_id_fk": { 2280 + "name": "spotify_accounts_user_id_users_xata_id_fk", 2281 + "tableFrom": "spotify_accounts", 2282 + "tableTo": "users", 2283 + "columnsFrom": [ 2284 + "user_id" 2285 + ], 2286 + "columnsTo": [ 2287 + "xata_id" 2288 + ], 2289 + "onDelete": "no action", 2290 + "onUpdate": "no action" 2291 + } 2292 + }, 2293 + "compositePrimaryKeys": {}, 2294 + "uniqueConstraints": {}, 2295 + "policies": {}, 2296 + "checkConstraints": {}, 2297 + "isRLSEnabled": false 2298 + }, 2299 + "public.spotify_tokens": { 2300 + "name": "spotify_tokens", 2301 + "schema": "", 2302 + "columns": { 2303 + "xata_id": { 2304 + "name": "xata_id", 2305 + "type": "text", 2306 + "primaryKey": true, 2307 + "notNull": true 2308 + }, 2309 + "xata_version": { 2310 + "name": "xata_version", 2311 + "type": "integer", 2312 + "primaryKey": false, 2313 + "notNull": false 2314 + }, 2315 + "access_token": { 2316 + "name": "access_token", 2317 + "type": "text", 2318 + "primaryKey": false, 2319 + "notNull": true 2320 + }, 2321 + "refresh_token": { 2322 + "name": "refresh_token", 2323 + "type": "text", 2324 + "primaryKey": false, 2325 + "notNull": true 2326 + }, 2327 + "user_id": { 2328 + "name": "user_id", 2329 + "type": "text", 2330 + "primaryKey": false, 2331 + "notNull": true 2332 + }, 2333 + "xata_createdat": { 2334 + "name": "xata_createdat", 2335 + "type": "timestamp", 2336 + "primaryKey": false, 2337 + "notNull": true, 2338 + "default": "now()" 2339 + }, 2340 + "xata_updatedat": { 2341 + "name": "xata_updatedat", 2342 + "type": "timestamp", 2343 + "primaryKey": false, 2344 + "notNull": true, 2345 + "default": "now()" 2346 + } 2347 + }, 2348 + "indexes": {}, 2349 + "foreignKeys": { 2350 + "spotify_tokens_user_id_users_xata_id_fk": { 2351 + "name": "spotify_tokens_user_id_users_xata_id_fk", 2352 + "tableFrom": "spotify_tokens", 2353 + "tableTo": "users", 2354 + "columnsFrom": [ 2355 + "user_id" 2356 + ], 2357 + "columnsTo": [ 2358 + "xata_id" 2359 + ], 2360 + "onDelete": "no action", 2361 + "onUpdate": "no action" 2362 + } 2363 + }, 2364 + "compositePrimaryKeys": {}, 2365 + "uniqueConstraints": {}, 2366 + "policies": {}, 2367 + "checkConstraints": {}, 2368 + "isRLSEnabled": false 2369 + }, 2370 + "public.tracks": { 2371 + "name": "tracks", 2372 + "schema": "", 2373 + "columns": { 2374 + "xata_id": { 2375 + "name": "xata_id", 2376 + "type": "text", 2377 + "primaryKey": true, 2378 + "notNull": true 2379 + }, 2380 + "title": { 2381 + "name": "title", 2382 + "type": "text", 2383 + "primaryKey": false, 2384 + "notNull": true 2385 + }, 2386 + "artist": { 2387 + "name": "artist", 2388 + "type": "text", 2389 + "primaryKey": false, 2390 + "notNull": true 2391 + }, 2392 + "album_artist": { 2393 + "name": "album_artist", 2394 + "type": "text", 2395 + "primaryKey": false, 2396 + "notNull": true 2397 + }, 2398 + "album_art": { 2399 + "name": "album_art", 2400 + "type": "text", 2401 + "primaryKey": false, 2402 + "notNull": false 2403 + }, 2404 + "album": { 2405 + "name": "album", 2406 + "type": "text", 2407 + "primaryKey": false, 2408 + "notNull": true 2409 + }, 2410 + "track_number": { 2411 + "name": "track_number", 2412 + "type": "integer", 2413 + "primaryKey": false, 2414 + "notNull": false 2415 + }, 2416 + "duration": { 2417 + "name": "duration", 2418 + "type": "integer", 2419 + "primaryKey": false, 2420 + "notNull": true 2421 + }, 2422 + "mb_id": { 2423 + "name": "mb_id", 2424 + "type": "text", 2425 + "primaryKey": false, 2426 + "notNull": false 2427 + }, 2428 + "youtube_link": { 2429 + "name": "youtube_link", 2430 + "type": "text", 2431 + "primaryKey": false, 2432 + "notNull": false 2433 + }, 2434 + "spotify_link": { 2435 + "name": "spotify_link", 2436 + "type": "text", 2437 + "primaryKey": false, 2438 + "notNull": false 2439 + }, 2440 + "apple_music_link": { 2441 + "name": "apple_music_link", 2442 + "type": "text", 2443 + "primaryKey": false, 2444 + "notNull": false 2445 + }, 2446 + "tidal_link": { 2447 + "name": "tidal_link", 2448 + "type": "text", 2449 + "primaryKey": false, 2450 + "notNull": false 2451 + }, 2452 + "sha256": { 2453 + "name": "sha256", 2454 + "type": "text", 2455 + "primaryKey": false, 2456 + "notNull": true 2457 + }, 2458 + "disc_number": { 2459 + "name": "disc_number", 2460 + "type": "integer", 2461 + "primaryKey": false, 2462 + "notNull": false 2463 + }, 2464 + "lyrics": { 2465 + "name": "lyrics", 2466 + "type": "text", 2467 + "primaryKey": false, 2468 + "notNull": false 2469 + }, 2470 + "composer": { 2471 + "name": "composer", 2472 + "type": "text", 2473 + "primaryKey": false, 2474 + "notNull": false 2475 + }, 2476 + "genre": { 2477 + "name": "genre", 2478 + "type": "text", 2479 + "primaryKey": false, 2480 + "notNull": false 2481 + }, 2482 + "label": { 2483 + "name": "label", 2484 + "type": "text", 2485 + "primaryKey": false, 2486 + "notNull": false 2487 + }, 2488 + "copyright_message": { 2489 + "name": "copyright_message", 2490 + "type": "text", 2491 + "primaryKey": false, 2492 + "notNull": false 2493 + }, 2494 + "uri": { 2495 + "name": "uri", 2496 + "type": "text", 2497 + "primaryKey": false, 2498 + "notNull": false 2499 + }, 2500 + "album_uri": { 2501 + "name": "album_uri", 2502 + "type": "text", 2503 + "primaryKey": false, 2504 + "notNull": false 2505 + }, 2506 + "artist_uri": { 2507 + "name": "artist_uri", 2508 + "type": "text", 2509 + "primaryKey": false, 2510 + "notNull": false 2511 + }, 2512 + "xata_createdat": { 2513 + "name": "xata_createdat", 2514 + "type": "timestamp", 2515 + "primaryKey": false, 2516 + "notNull": true, 2517 + "default": "now()" 2518 + }, 2519 + "xata_updatedat": { 2520 + "name": "xata_updatedat", 2521 + "type": "timestamp", 2522 + "primaryKey": false, 2523 + "notNull": true, 2524 + "default": "now()" 2525 + }, 2526 + "xata_version": { 2527 + "name": "xata_version", 2528 + "type": "integer", 2529 + "primaryKey": false, 2530 + "notNull": false 2531 + } 2532 + }, 2533 + "indexes": {}, 2534 + "foreignKeys": {}, 2535 + "compositePrimaryKeys": {}, 2536 + "uniqueConstraints": { 2537 + "tracks_mb_id_unique": { 2538 + "name": "tracks_mb_id_unique", 2539 + "nullsNotDistinct": false, 2540 + "columns": [ 2541 + "mb_id" 2542 + ] 2543 + }, 2544 + "tracks_youtube_link_unique": { 2545 + "name": "tracks_youtube_link_unique", 2546 + "nullsNotDistinct": false, 2547 + "columns": [ 2548 + "youtube_link" 2549 + ] 2550 + }, 2551 + "tracks_spotify_link_unique": { 2552 + "name": "tracks_spotify_link_unique", 2553 + "nullsNotDistinct": false, 2554 + "columns": [ 2555 + "spotify_link" 2556 + ] 2557 + }, 2558 + "tracks_apple_music_link_unique": { 2559 + "name": "tracks_apple_music_link_unique", 2560 + "nullsNotDistinct": false, 2561 + "columns": [ 2562 + "apple_music_link" 2563 + ] 2564 + }, 2565 + "tracks_tidal_link_unique": { 2566 + "name": "tracks_tidal_link_unique", 2567 + "nullsNotDistinct": false, 2568 + "columns": [ 2569 + "tidal_link" 2570 + ] 2571 + }, 2572 + "tracks_sha256_unique": { 2573 + "name": "tracks_sha256_unique", 2574 + "nullsNotDistinct": false, 2575 + "columns": [ 2576 + "sha256" 2577 + ] 2578 + }, 2579 + "tracks_uri_unique": { 2580 + "name": "tracks_uri_unique", 2581 + "nullsNotDistinct": false, 2582 + "columns": [ 2583 + "uri" 2584 + ] 2585 + } 2586 + }, 2587 + "policies": {}, 2588 + "checkConstraints": {}, 2589 + "isRLSEnabled": false 2590 + }, 2591 + "public.user_albums": { 2592 + "name": "user_albums", 2593 + "schema": "", 2594 + "columns": { 2595 + "xata_id": { 2596 + "name": "xata_id", 2597 + "type": "text", 2598 + "primaryKey": true, 2599 + "notNull": true 2600 + }, 2601 + "user_id": { 2602 + "name": "user_id", 2603 + "type": "text", 2604 + "primaryKey": false, 2605 + "notNull": true 2606 + }, 2607 + "album_id": { 2608 + "name": "album_id", 2609 + "type": "text", 2610 + "primaryKey": false, 2611 + "notNull": true 2612 + }, 2613 + "xata_createdat": { 2614 + "name": "xata_createdat", 2615 + "type": "timestamp", 2616 + "primaryKey": false, 2617 + "notNull": true, 2618 + "default": "now()" 2619 + }, 2620 + "xata_updatedat": { 2621 + "name": "xata_updatedat", 2622 + "type": "timestamp", 2623 + "primaryKey": false, 2624 + "notNull": true, 2625 + "default": "now()" 2626 + }, 2627 + "xata_version": { 2628 + "name": "xata_version", 2629 + "type": "integer", 2630 + "primaryKey": false, 2631 + "notNull": false 2632 + }, 2633 + "scrobbles": { 2634 + "name": "scrobbles", 2635 + "type": "integer", 2636 + "primaryKey": false, 2637 + "notNull": false 2638 + }, 2639 + "uri": { 2640 + "name": "uri", 2641 + "type": "text", 2642 + "primaryKey": false, 2643 + "notNull": true 2644 + } 2645 + }, 2646 + "indexes": {}, 2647 + "foreignKeys": { 2648 + "user_albums_user_id_users_xata_id_fk": { 2649 + "name": "user_albums_user_id_users_xata_id_fk", 2650 + "tableFrom": "user_albums", 2651 + "tableTo": "users", 2652 + "columnsFrom": [ 2653 + "user_id" 2654 + ], 2655 + "columnsTo": [ 2656 + "xata_id" 2657 + ], 2658 + "onDelete": "no action", 2659 + "onUpdate": "no action" 2660 + }, 2661 + "user_albums_album_id_albums_xata_id_fk": { 2662 + "name": "user_albums_album_id_albums_xata_id_fk", 2663 + "tableFrom": "user_albums", 2664 + "tableTo": "albums", 2665 + "columnsFrom": [ 2666 + "album_id" 2667 + ], 2668 + "columnsTo": [ 2669 + "xata_id" 2670 + ], 2671 + "onDelete": "no action", 2672 + "onUpdate": "no action" 2673 + } 2674 + }, 2675 + "compositePrimaryKeys": {}, 2676 + "uniqueConstraints": { 2677 + "user_albums_uri_unique": { 2678 + "name": "user_albums_uri_unique", 2679 + "nullsNotDistinct": false, 2680 + "columns": [ 2681 + "uri" 2682 + ] 2683 + } 2684 + }, 2685 + "policies": {}, 2686 + "checkConstraints": {}, 2687 + "isRLSEnabled": false 2688 + }, 2689 + "public.user_artists": { 2690 + "name": "user_artists", 2691 + "schema": "", 2692 + "columns": { 2693 + "xata_id": { 2694 + "name": "xata_id", 2695 + "type": "text", 2696 + "primaryKey": true, 2697 + "notNull": true 2698 + }, 2699 + "user_id": { 2700 + "name": "user_id", 2701 + "type": "text", 2702 + "primaryKey": false, 2703 + "notNull": true 2704 + }, 2705 + "artist_id": { 2706 + "name": "artist_id", 2707 + "type": "text", 2708 + "primaryKey": false, 2709 + "notNull": true 2710 + }, 2711 + "xata_createdat": { 2712 + "name": "xata_createdat", 2713 + "type": "timestamp", 2714 + "primaryKey": false, 2715 + "notNull": true, 2716 + "default": "now()" 2717 + }, 2718 + "xata_updatedat": { 2719 + "name": "xata_updatedat", 2720 + "type": "timestamp", 2721 + "primaryKey": false, 2722 + "notNull": true, 2723 + "default": "now()" 2724 + }, 2725 + "xata_version": { 2726 + "name": "xata_version", 2727 + "type": "integer", 2728 + "primaryKey": false, 2729 + "notNull": false 2730 + }, 2731 + "scrobbles": { 2732 + "name": "scrobbles", 2733 + "type": "integer", 2734 + "primaryKey": false, 2735 + "notNull": false 2736 + }, 2737 + "uri": { 2738 + "name": "uri", 2739 + "type": "text", 2740 + "primaryKey": false, 2741 + "notNull": true 2742 + } 2743 + }, 2744 + "indexes": {}, 2745 + "foreignKeys": { 2746 + "user_artists_user_id_users_xata_id_fk": { 2747 + "name": "user_artists_user_id_users_xata_id_fk", 2748 + "tableFrom": "user_artists", 2749 + "tableTo": "users", 2750 + "columnsFrom": [ 2751 + "user_id" 2752 + ], 2753 + "columnsTo": [ 2754 + "xata_id" 2755 + ], 2756 + "onDelete": "no action", 2757 + "onUpdate": "no action" 2758 + }, 2759 + "user_artists_artist_id_artists_xata_id_fk": { 2760 + "name": "user_artists_artist_id_artists_xata_id_fk", 2761 + "tableFrom": "user_artists", 2762 + "tableTo": "artists", 2763 + "columnsFrom": [ 2764 + "artist_id" 2765 + ], 2766 + "columnsTo": [ 2767 + "xata_id" 2768 + ], 2769 + "onDelete": "no action", 2770 + "onUpdate": "no action" 2771 + } 2772 + }, 2773 + "compositePrimaryKeys": {}, 2774 + "uniqueConstraints": { 2775 + "user_artists_uri_unique": { 2776 + "name": "user_artists_uri_unique", 2777 + "nullsNotDistinct": false, 2778 + "columns": [ 2779 + "uri" 2780 + ] 2781 + } 2782 + }, 2783 + "policies": {}, 2784 + "checkConstraints": {}, 2785 + "isRLSEnabled": false 2786 + }, 2787 + "public.user_playlists": { 2788 + "name": "user_playlists", 2789 + "schema": "", 2790 + "columns": { 2791 + "xata_id": { 2792 + "name": "xata_id", 2793 + "type": "text", 2794 + "primaryKey": true, 2795 + "notNull": true 2796 + }, 2797 + "user_id": { 2798 + "name": "user_id", 2799 + "type": "text", 2800 + "primaryKey": false, 2801 + "notNull": true 2802 + }, 2803 + "playlist_id": { 2804 + "name": "playlist_id", 2805 + "type": "text", 2806 + "primaryKey": false, 2807 + "notNull": true 2808 + }, 2809 + "xata_createdat": { 2810 + "name": "xata_createdat", 2811 + "type": "timestamp", 2812 + "primaryKey": false, 2813 + "notNull": true, 2814 + "default": "now()" 2815 + }, 2816 + "uri": { 2817 + "name": "uri", 2818 + "type": "text", 2819 + "primaryKey": false, 2820 + "notNull": false 2821 + } 2822 + }, 2823 + "indexes": {}, 2824 + "foreignKeys": { 2825 + "user_playlists_user_id_users_xata_id_fk": { 2826 + "name": "user_playlists_user_id_users_xata_id_fk", 2827 + "tableFrom": "user_playlists", 2828 + "tableTo": "users", 2829 + "columnsFrom": [ 2830 + "user_id" 2831 + ], 2832 + "columnsTo": [ 2833 + "xata_id" 2834 + ], 2835 + "onDelete": "no action", 2836 + "onUpdate": "no action" 2837 + }, 2838 + "user_playlists_playlist_id_tracks_xata_id_fk": { 2839 + "name": "user_playlists_playlist_id_tracks_xata_id_fk", 2840 + "tableFrom": "user_playlists", 2841 + "tableTo": "tracks", 2842 + "columnsFrom": [ 2843 + "playlist_id" 2844 + ], 2845 + "columnsTo": [ 2846 + "xata_id" 2847 + ], 2848 + "onDelete": "no action", 2849 + "onUpdate": "no action" 2850 + } 2851 + }, 2852 + "compositePrimaryKeys": {}, 2853 + "uniqueConstraints": { 2854 + "user_playlists_uri_unique": { 2855 + "name": "user_playlists_uri_unique", 2856 + "nullsNotDistinct": false, 2857 + "columns": [ 2858 + "uri" 2859 + ] 2860 + } 2861 + }, 2862 + "policies": {}, 2863 + "checkConstraints": {}, 2864 + "isRLSEnabled": false 2865 + }, 2866 + "public.user_tracks": { 2867 + "name": "user_tracks", 2868 + "schema": "", 2869 + "columns": { 2870 + "xata_id": { 2871 + "name": "xata_id", 2872 + "type": "text", 2873 + "primaryKey": true, 2874 + "notNull": true 2875 + }, 2876 + "user_id": { 2877 + "name": "user_id", 2878 + "type": "text", 2879 + "primaryKey": false, 2880 + "notNull": true 2881 + }, 2882 + "track_id": { 2883 + "name": "track_id", 2884 + "type": "text", 2885 + "primaryKey": false, 2886 + "notNull": true 2887 + }, 2888 + "xata_createdat": { 2889 + "name": "xata_createdat", 2890 + "type": "timestamp", 2891 + "primaryKey": false, 2892 + "notNull": true, 2893 + "default": "now()" 2894 + }, 2895 + "xata_updatedat": { 2896 + "name": "xata_updatedat", 2897 + "type": "timestamp", 2898 + "primaryKey": false, 2899 + "notNull": true, 2900 + "default": "now()" 2901 + }, 2902 + "xata_version": { 2903 + "name": "xata_version", 2904 + "type": "integer", 2905 + "primaryKey": false, 2906 + "notNull": false 2907 + }, 2908 + "uri": { 2909 + "name": "uri", 2910 + "type": "text", 2911 + "primaryKey": false, 2912 + "notNull": true 2913 + }, 2914 + "scrobbles": { 2915 + "name": "scrobbles", 2916 + "type": "integer", 2917 + "primaryKey": false, 2918 + "notNull": false 2919 + } 2920 + }, 2921 + "indexes": {}, 2922 + "foreignKeys": { 2923 + "user_tracks_user_id_users_xata_id_fk": { 2924 + "name": "user_tracks_user_id_users_xata_id_fk", 2925 + "tableFrom": "user_tracks", 2926 + "tableTo": "users", 2927 + "columnsFrom": [ 2928 + "user_id" 2929 + ], 2930 + "columnsTo": [ 2931 + "xata_id" 2932 + ], 2933 + "onDelete": "no action", 2934 + "onUpdate": "no action" 2935 + }, 2936 + "user_tracks_track_id_tracks_xata_id_fk": { 2937 + "name": "user_tracks_track_id_tracks_xata_id_fk", 2938 + "tableFrom": "user_tracks", 2939 + "tableTo": "tracks", 2940 + "columnsFrom": [ 2941 + "track_id" 2942 + ], 2943 + "columnsTo": [ 2944 + "xata_id" 2945 + ], 2946 + "onDelete": "no action", 2947 + "onUpdate": "no action" 2948 + } 2949 + }, 2950 + "compositePrimaryKeys": {}, 2951 + "uniqueConstraints": { 2952 + "user_tracks_uri_unique": { 2953 + "name": "user_tracks_uri_unique", 2954 + "nullsNotDistinct": false, 2955 + "columns": [ 2956 + "uri" 2957 + ] 2958 + } 2959 + }, 2960 + "policies": {}, 2961 + "checkConstraints": {}, 2962 + "isRLSEnabled": false 2963 + }, 2964 + "public.users": { 2965 + "name": "users", 2966 + "schema": "", 2967 + "columns": { 2968 + "xata_id": { 2969 + "name": "xata_id", 2970 + "type": "text", 2971 + "primaryKey": true, 2972 + "notNull": true 2973 + }, 2974 + "did": { 2975 + "name": "did", 2976 + "type": "text", 2977 + "primaryKey": false, 2978 + "notNull": true 2979 + }, 2980 + "display_name": { 2981 + "name": "display_name", 2982 + "type": "text", 2983 + "primaryKey": false, 2984 + "notNull": false 2985 + }, 2986 + "handle": { 2987 + "name": "handle", 2988 + "type": "text", 2989 + "primaryKey": false, 2990 + "notNull": true 2991 + }, 2992 + "avatar": { 2993 + "name": "avatar", 2994 + "type": "text", 2995 + "primaryKey": false, 2996 + "notNull": true 2997 + }, 2998 + "xata_createdat": { 2999 + "name": "xata_createdat", 3000 + "type": "timestamp", 3001 + "primaryKey": false, 3002 + "notNull": true, 3003 + "default": "now()" 3004 + }, 3005 + "xata_updatedat": { 3006 + "name": "xata_updatedat", 3007 + "type": "timestamp", 3008 + "primaryKey": false, 3009 + "notNull": true, 3010 + "default": "now()" 3011 + }, 3012 + "xata_version": { 3013 + "name": "xata_version", 3014 + "type": "integer", 3015 + "primaryKey": false, 3016 + "notNull": false 3017 + } 3018 + }, 3019 + "indexes": {}, 3020 + "foreignKeys": {}, 3021 + "compositePrimaryKeys": {}, 3022 + "uniqueConstraints": { 3023 + "users_did_unique": { 3024 + "name": "users_did_unique", 3025 + "nullsNotDistinct": false, 3026 + "columns": [ 3027 + "did" 3028 + ] 3029 + }, 3030 + "users_handle_unique": { 3031 + "name": "users_handle_unique", 3032 + "nullsNotDistinct": false, 3033 + "columns": [ 3034 + "handle" 3035 + ] 3036 + } 3037 + }, 3038 + "policies": {}, 3039 + "checkConstraints": {}, 3040 + "isRLSEnabled": false 3041 + }, 3042 + "public.webscrobblers": { 3043 + "name": "webscrobblers", 3044 + "schema": "", 3045 + "columns": { 3046 + "xata_id": { 3047 + "name": "xata_id", 3048 + "type": "text", 3049 + "primaryKey": true, 3050 + "notNull": true 3051 + }, 3052 + "name": { 3053 + "name": "name", 3054 + "type": "text", 3055 + "primaryKey": false, 3056 + "notNull": true 3057 + }, 3058 + "uuid": { 3059 + "name": "uuid", 3060 + "type": "text", 3061 + "primaryKey": false, 3062 + "notNull": true 3063 + }, 3064 + "description": { 3065 + "name": "description", 3066 + "type": "text", 3067 + "primaryKey": false, 3068 + "notNull": false 3069 + }, 3070 + "enabled": { 3071 + "name": "enabled", 3072 + "type": "boolean", 3073 + "primaryKey": false, 3074 + "notNull": true, 3075 + "default": true 3076 + }, 3077 + "user_id": { 3078 + "name": "user_id", 3079 + "type": "text", 3080 + "primaryKey": false, 3081 + "notNull": true 3082 + }, 3083 + "xata_createdat": { 3084 + "name": "xata_createdat", 3085 + "type": "timestamp", 3086 + "primaryKey": false, 3087 + "notNull": true, 3088 + "default": "now()" 3089 + }, 3090 + "xata_updatedat": { 3091 + "name": "xata_updatedat", 3092 + "type": "timestamp", 3093 + "primaryKey": false, 3094 + "notNull": true, 3095 + "default": "now()" 3096 + } 3097 + }, 3098 + "indexes": {}, 3099 + "foreignKeys": { 3100 + "webscrobblers_user_id_users_xata_id_fk": { 3101 + "name": "webscrobblers_user_id_users_xata_id_fk", 3102 + "tableFrom": "webscrobblers", 3103 + "tableTo": "users", 3104 + "columnsFrom": [ 3105 + "user_id" 3106 + ], 3107 + "columnsTo": [ 3108 + "xata_id" 3109 + ], 3110 + "onDelete": "no action", 3111 + "onUpdate": "no action" 3112 + } 3113 + }, 3114 + "compositePrimaryKeys": {}, 3115 + "uniqueConstraints": {}, 3116 + "policies": {}, 3117 + "checkConstraints": {}, 3118 + "isRLSEnabled": false 3119 + } 3120 + }, 3121 + "enums": {}, 3122 + "schemas": {}, 3123 + "sequences": {}, 3124 + "roles": {}, 3125 + "policies": {}, 3126 + "views": {}, 3127 + "_meta": { 3128 + "columns": {}, 3129 + "schemas": {}, 3130 + "tables": {} 3131 + } 3132 + }
+3132
apps/api/drizzle/meta/0002_snapshot.json
··· 1 + { 2 + "id": "3ef49661-fdf0-4245-8943-ff69567a09b9", 3 + "prevId": "6ebb9775-b900-418c-bbd8-b704571a20d5", 4 + "version": "7", 5 + "dialect": "postgresql", 6 + "tables": { 7 + "public.album_tracks": { 8 + "name": "album_tracks", 9 + "schema": "", 10 + "columns": { 11 + "xata_id": { 12 + "name": "xata_id", 13 + "type": "text", 14 + "primaryKey": true, 15 + "notNull": true 16 + }, 17 + "album_id": { 18 + "name": "album_id", 19 + "type": "text", 20 + "primaryKey": false, 21 + "notNull": true 22 + }, 23 + "track_id": { 24 + "name": "track_id", 25 + "type": "text", 26 + "primaryKey": false, 27 + "notNull": true 28 + }, 29 + "xata_createdat": { 30 + "name": "xata_createdat", 31 + "type": "timestamp", 32 + "primaryKey": false, 33 + "notNull": true, 34 + "default": "now()" 35 + }, 36 + "xata_updatedat": { 37 + "name": "xata_updatedat", 38 + "type": "timestamp", 39 + "primaryKey": false, 40 + "notNull": true, 41 + "default": "now()" 42 + }, 43 + "xata_version": { 44 + "name": "xata_version", 45 + "type": "integer", 46 + "primaryKey": false, 47 + "notNull": false 48 + } 49 + }, 50 + "indexes": {}, 51 + "foreignKeys": { 52 + "album_tracks_album_id_albums_xata_id_fk": { 53 + "name": "album_tracks_album_id_albums_xata_id_fk", 54 + "tableFrom": "album_tracks", 55 + "tableTo": "albums", 56 + "columnsFrom": [ 57 + "album_id" 58 + ], 59 + "columnsTo": [ 60 + "xata_id" 61 + ], 62 + "onDelete": "no action", 63 + "onUpdate": "no action" 64 + }, 65 + "album_tracks_track_id_tracks_xata_id_fk": { 66 + "name": "album_tracks_track_id_tracks_xata_id_fk", 67 + "tableFrom": "album_tracks", 68 + "tableTo": "tracks", 69 + "columnsFrom": [ 70 + "track_id" 71 + ], 72 + "columnsTo": [ 73 + "xata_id" 74 + ], 75 + "onDelete": "no action", 76 + "onUpdate": "no action" 77 + } 78 + }, 79 + "compositePrimaryKeys": {}, 80 + "uniqueConstraints": {}, 81 + "policies": {}, 82 + "checkConstraints": {}, 83 + "isRLSEnabled": false 84 + }, 85 + "public.albums": { 86 + "name": "albums", 87 + "schema": "", 88 + "columns": { 89 + "xata_id": { 90 + "name": "xata_id", 91 + "type": "text", 92 + "primaryKey": true, 93 + "notNull": true 94 + }, 95 + "title": { 96 + "name": "title", 97 + "type": "text", 98 + "primaryKey": false, 99 + "notNull": true 100 + }, 101 + "artist": { 102 + "name": "artist", 103 + "type": "text", 104 + "primaryKey": false, 105 + "notNull": true 106 + }, 107 + "release_date": { 108 + "name": "release_date", 109 + "type": "text", 110 + "primaryKey": false, 111 + "notNull": false 112 + }, 113 + "year": { 114 + "name": "year", 115 + "type": "integer", 116 + "primaryKey": false, 117 + "notNull": false 118 + }, 119 + "album_art": { 120 + "name": "album_art", 121 + "type": "text", 122 + "primaryKey": false, 123 + "notNull": false 124 + }, 125 + "uri": { 126 + "name": "uri", 127 + "type": "text", 128 + "primaryKey": false, 129 + "notNull": false 130 + }, 131 + "artist_uri": { 132 + "name": "artist_uri", 133 + "type": "text", 134 + "primaryKey": false, 135 + "notNull": false 136 + }, 137 + "apple_music_link": { 138 + "name": "apple_music_link", 139 + "type": "text", 140 + "primaryKey": false, 141 + "notNull": false 142 + }, 143 + "spotify_link": { 144 + "name": "spotify_link", 145 + "type": "text", 146 + "primaryKey": false, 147 + "notNull": false 148 + }, 149 + "tidal_link": { 150 + "name": "tidal_link", 151 + "type": "text", 152 + "primaryKey": false, 153 + "notNull": false 154 + }, 155 + "youtube_link": { 156 + "name": "youtube_link", 157 + "type": "text", 158 + "primaryKey": false, 159 + "notNull": false 160 + }, 161 + "sha256": { 162 + "name": "sha256", 163 + "type": "text", 164 + "primaryKey": false, 165 + "notNull": true 166 + }, 167 + "xata_createdat": { 168 + "name": "xata_createdat", 169 + "type": "timestamp", 170 + "primaryKey": false, 171 + "notNull": true, 172 + "default": "now()" 173 + }, 174 + "xata_updatedat": { 175 + "name": "xata_updatedat", 176 + "type": "timestamp", 177 + "primaryKey": false, 178 + "notNull": true, 179 + "default": "now()" 180 + }, 181 + "xata_version": { 182 + "name": "xata_version", 183 + "type": "integer", 184 + "primaryKey": false, 185 + "notNull": false 186 + } 187 + }, 188 + "indexes": {}, 189 + "foreignKeys": {}, 190 + "compositePrimaryKeys": {}, 191 + "uniqueConstraints": { 192 + "albums_uri_unique": { 193 + "name": "albums_uri_unique", 194 + "nullsNotDistinct": false, 195 + "columns": [ 196 + "uri" 197 + ] 198 + }, 199 + "albums_apple_music_link_unique": { 200 + "name": "albums_apple_music_link_unique", 201 + "nullsNotDistinct": false, 202 + "columns": [ 203 + "apple_music_link" 204 + ] 205 + }, 206 + "albums_spotify_link_unique": { 207 + "name": "albums_spotify_link_unique", 208 + "nullsNotDistinct": false, 209 + "columns": [ 210 + "spotify_link" 211 + ] 212 + }, 213 + "albums_tidal_link_unique": { 214 + "name": "albums_tidal_link_unique", 215 + "nullsNotDistinct": false, 216 + "columns": [ 217 + "tidal_link" 218 + ] 219 + }, 220 + "albums_youtube_link_unique": { 221 + "name": "albums_youtube_link_unique", 222 + "nullsNotDistinct": false, 223 + "columns": [ 224 + "youtube_link" 225 + ] 226 + }, 227 + "albums_sha256_unique": { 228 + "name": "albums_sha256_unique", 229 + "nullsNotDistinct": false, 230 + "columns": [ 231 + "sha256" 232 + ] 233 + } 234 + }, 235 + "policies": {}, 236 + "checkConstraints": {}, 237 + "isRLSEnabled": false 238 + }, 239 + "public.api_keys": { 240 + "name": "api_keys", 241 + "schema": "", 242 + "columns": { 243 + "xata_id": { 244 + "name": "xata_id", 245 + "type": "text", 246 + "primaryKey": true, 247 + "notNull": true 248 + }, 249 + "name": { 250 + "name": "name", 251 + "type": "text", 252 + "primaryKey": false, 253 + "notNull": true 254 + }, 255 + "api_key": { 256 + "name": "api_key", 257 + "type": "text", 258 + "primaryKey": false, 259 + "notNull": true 260 + }, 261 + "shared_secret": { 262 + "name": "shared_secret", 263 + "type": "text", 264 + "primaryKey": false, 265 + "notNull": true 266 + }, 267 + "description": { 268 + "name": "description", 269 + "type": "text", 270 + "primaryKey": false, 271 + "notNull": false 272 + }, 273 + "enabled": { 274 + "name": "enabled", 275 + "type": "boolean", 276 + "primaryKey": false, 277 + "notNull": true, 278 + "default": true 279 + }, 280 + "user_id": { 281 + "name": "user_id", 282 + "type": "text", 283 + "primaryKey": false, 284 + "notNull": true 285 + }, 286 + "xata_createdat": { 287 + "name": "xata_createdat", 288 + "type": "timestamp", 289 + "primaryKey": false, 290 + "notNull": true, 291 + "default": "now()" 292 + }, 293 + "xata_updatedat": { 294 + "name": "xata_updatedat", 295 + "type": "timestamp", 296 + "primaryKey": false, 297 + "notNull": true, 298 + "default": "now()" 299 + } 300 + }, 301 + "indexes": {}, 302 + "foreignKeys": { 303 + "api_keys_user_id_users_xata_id_fk": { 304 + "name": "api_keys_user_id_users_xata_id_fk", 305 + "tableFrom": "api_keys", 306 + "tableTo": "users", 307 + "columnsFrom": [ 308 + "user_id" 309 + ], 310 + "columnsTo": [ 311 + "xata_id" 312 + ], 313 + "onDelete": "no action", 314 + "onUpdate": "no action" 315 + } 316 + }, 317 + "compositePrimaryKeys": {}, 318 + "uniqueConstraints": {}, 319 + "policies": {}, 320 + "checkConstraints": {}, 321 + "isRLSEnabled": false 322 + }, 323 + "public.artist_albums": { 324 + "name": "artist_albums", 325 + "schema": "", 326 + "columns": { 327 + "xata_id": { 328 + "name": "xata_id", 329 + "type": "text", 330 + "primaryKey": true, 331 + "notNull": true 332 + }, 333 + "artist_id": { 334 + "name": "artist_id", 335 + "type": "text", 336 + "primaryKey": false, 337 + "notNull": true 338 + }, 339 + "album_id": { 340 + "name": "album_id", 341 + "type": "text", 342 + "primaryKey": false, 343 + "notNull": true 344 + }, 345 + "xata_createdat": { 346 + "name": "xata_createdat", 347 + "type": "timestamp", 348 + "primaryKey": false, 349 + "notNull": true, 350 + "default": "now()" 351 + }, 352 + "xata_updatedat": { 353 + "name": "xata_updatedat", 354 + "type": "timestamp", 355 + "primaryKey": false, 356 + "notNull": true, 357 + "default": "now()" 358 + }, 359 + "xata_version": { 360 + "name": "xata_version", 361 + "type": "integer", 362 + "primaryKey": false, 363 + "notNull": false 364 + } 365 + }, 366 + "indexes": {}, 367 + "foreignKeys": { 368 + "artist_albums_artist_id_artists_xata_id_fk": { 369 + "name": "artist_albums_artist_id_artists_xata_id_fk", 370 + "tableFrom": "artist_albums", 371 + "tableTo": "artists", 372 + "columnsFrom": [ 373 + "artist_id" 374 + ], 375 + "columnsTo": [ 376 + "xata_id" 377 + ], 378 + "onDelete": "no action", 379 + "onUpdate": "no action" 380 + }, 381 + "artist_albums_album_id_albums_xata_id_fk": { 382 + "name": "artist_albums_album_id_albums_xata_id_fk", 383 + "tableFrom": "artist_albums", 384 + "tableTo": "albums", 385 + "columnsFrom": [ 386 + "album_id" 387 + ], 388 + "columnsTo": [ 389 + "xata_id" 390 + ], 391 + "onDelete": "no action", 392 + "onUpdate": "no action" 393 + } 394 + }, 395 + "compositePrimaryKeys": {}, 396 + "uniqueConstraints": {}, 397 + "policies": {}, 398 + "checkConstraints": {}, 399 + "isRLSEnabled": false 400 + }, 401 + "public.artist_tracks": { 402 + "name": "artist_tracks", 403 + "schema": "", 404 + "columns": { 405 + "xata_id": { 406 + "name": "xata_id", 407 + "type": "text", 408 + "primaryKey": true, 409 + "notNull": true 410 + }, 411 + "artist_id": { 412 + "name": "artist_id", 413 + "type": "text", 414 + "primaryKey": false, 415 + "notNull": true 416 + }, 417 + "track_id": { 418 + "name": "track_id", 419 + "type": "text", 420 + "primaryKey": false, 421 + "notNull": true 422 + }, 423 + "xata_createdat": { 424 + "name": "xata_createdat", 425 + "type": "timestamp", 426 + "primaryKey": false, 427 + "notNull": true, 428 + "default": "now()" 429 + }, 430 + "xata_updatedat": { 431 + "name": "xata_updatedat", 432 + "type": "timestamp", 433 + "primaryKey": false, 434 + "notNull": true, 435 + "default": "now()" 436 + }, 437 + "xata_version": { 438 + "name": "xata_version", 439 + "type": "integer", 440 + "primaryKey": false, 441 + "notNull": false 442 + } 443 + }, 444 + "indexes": {}, 445 + "foreignKeys": { 446 + "artist_tracks_artist_id_artists_xata_id_fk": { 447 + "name": "artist_tracks_artist_id_artists_xata_id_fk", 448 + "tableFrom": "artist_tracks", 449 + "tableTo": "artists", 450 + "columnsFrom": [ 451 + "artist_id" 452 + ], 453 + "columnsTo": [ 454 + "xata_id" 455 + ], 456 + "onDelete": "no action", 457 + "onUpdate": "no action" 458 + }, 459 + "artist_tracks_track_id_tracks_xata_id_fk": { 460 + "name": "artist_tracks_track_id_tracks_xata_id_fk", 461 + "tableFrom": "artist_tracks", 462 + "tableTo": "tracks", 463 + "columnsFrom": [ 464 + "track_id" 465 + ], 466 + "columnsTo": [ 467 + "xata_id" 468 + ], 469 + "onDelete": "no action", 470 + "onUpdate": "no action" 471 + } 472 + }, 473 + "compositePrimaryKeys": {}, 474 + "uniqueConstraints": {}, 475 + "policies": {}, 476 + "checkConstraints": {}, 477 + "isRLSEnabled": false 478 + }, 479 + "public.artists": { 480 + "name": "artists", 481 + "schema": "", 482 + "columns": { 483 + "xata_id": { 484 + "name": "xata_id", 485 + "type": "text", 486 + "primaryKey": true, 487 + "notNull": true 488 + }, 489 + "name": { 490 + "name": "name", 491 + "type": "text", 492 + "primaryKey": false, 493 + "notNull": true 494 + }, 495 + "biography": { 496 + "name": "biography", 497 + "type": "text", 498 + "primaryKey": false, 499 + "notNull": false 500 + }, 501 + "born": { 502 + "name": "born", 503 + "type": "timestamp", 504 + "primaryKey": false, 505 + "notNull": false 506 + }, 507 + "born_in": { 508 + "name": "born_in", 509 + "type": "text", 510 + "primaryKey": false, 511 + "notNull": false 512 + }, 513 + "died": { 514 + "name": "died", 515 + "type": "timestamp", 516 + "primaryKey": false, 517 + "notNull": false 518 + }, 519 + "picture": { 520 + "name": "picture", 521 + "type": "text", 522 + "primaryKey": false, 523 + "notNull": false 524 + }, 525 + "sha256": { 526 + "name": "sha256", 527 + "type": "text", 528 + "primaryKey": false, 529 + "notNull": true 530 + }, 531 + "uri": { 532 + "name": "uri", 533 + "type": "text", 534 + "primaryKey": false, 535 + "notNull": false 536 + }, 537 + "apple_music_link": { 538 + "name": "apple_music_link", 539 + "type": "text", 540 + "primaryKey": false, 541 + "notNull": false 542 + }, 543 + "spotify_link": { 544 + "name": "spotify_link", 545 + "type": "text", 546 + "primaryKey": false, 547 + "notNull": false 548 + }, 549 + "tidal_link": { 550 + "name": "tidal_link", 551 + "type": "text", 552 + "primaryKey": false, 553 + "notNull": false 554 + }, 555 + "youtube_link": { 556 + "name": "youtube_link", 557 + "type": "text", 558 + "primaryKey": false, 559 + "notNull": false 560 + }, 561 + "genres": { 562 + "name": "genres", 563 + "type": "text[]", 564 + "primaryKey": false, 565 + "notNull": false 566 + }, 567 + "xata_createdat": { 568 + "name": "xata_createdat", 569 + "type": "timestamp", 570 + "primaryKey": false, 571 + "notNull": true, 572 + "default": "now()" 573 + }, 574 + "xata_updatedat": { 575 + "name": "xata_updatedat", 576 + "type": "timestamp", 577 + "primaryKey": false, 578 + "notNull": true, 579 + "default": "now()" 580 + }, 581 + "xata_version": { 582 + "name": "xata_version", 583 + "type": "integer", 584 + "primaryKey": false, 585 + "notNull": false 586 + } 587 + }, 588 + "indexes": {}, 589 + "foreignKeys": {}, 590 + "compositePrimaryKeys": {}, 591 + "uniqueConstraints": { 592 + "artists_sha256_unique": { 593 + "name": "artists_sha256_unique", 594 + "nullsNotDistinct": false, 595 + "columns": [ 596 + "sha256" 597 + ] 598 + }, 599 + "artists_uri_unique": { 600 + "name": "artists_uri_unique", 601 + "nullsNotDistinct": false, 602 + "columns": [ 603 + "uri" 604 + ] 605 + } 606 + }, 607 + "policies": {}, 608 + "checkConstraints": {}, 609 + "isRLSEnabled": false 610 + }, 611 + "public.dropbox_accounts": { 612 + "name": "dropbox_accounts", 613 + "schema": "", 614 + "columns": { 615 + "xata_id": { 616 + "name": "xata_id", 617 + "type": "text", 618 + "primaryKey": true, 619 + "notNull": true 620 + }, 621 + "email": { 622 + "name": "email", 623 + "type": "text", 624 + "primaryKey": false, 625 + "notNull": true 626 + }, 627 + "is_beta_user": { 628 + "name": "is_beta_user", 629 + "type": "boolean", 630 + "primaryKey": false, 631 + "notNull": true, 632 + "default": false 633 + }, 634 + "user_id": { 635 + "name": "user_id", 636 + "type": "text", 637 + "primaryKey": false, 638 + "notNull": true 639 + }, 640 + "xata_version": { 641 + "name": "xata_version", 642 + "type": "text", 643 + "primaryKey": false, 644 + "notNull": false 645 + }, 646 + "xata_createdat": { 647 + "name": "xata_createdat", 648 + "type": "timestamp", 649 + "primaryKey": false, 650 + "notNull": true, 651 + "default": "now()" 652 + }, 653 + "xata_updatedat": { 654 + "name": "xata_updatedat", 655 + "type": "timestamp", 656 + "primaryKey": false, 657 + "notNull": true, 658 + "default": "now()" 659 + } 660 + }, 661 + "indexes": {}, 662 + "foreignKeys": { 663 + "dropbox_accounts_user_id_users_xata_id_fk": { 664 + "name": "dropbox_accounts_user_id_users_xata_id_fk", 665 + "tableFrom": "dropbox_accounts", 666 + "tableTo": "users", 667 + "columnsFrom": [ 668 + "user_id" 669 + ], 670 + "columnsTo": [ 671 + "xata_id" 672 + ], 673 + "onDelete": "no action", 674 + "onUpdate": "no action" 675 + } 676 + }, 677 + "compositePrimaryKeys": {}, 678 + "uniqueConstraints": { 679 + "dropbox_accounts_email_unique": { 680 + "name": "dropbox_accounts_email_unique", 681 + "nullsNotDistinct": false, 682 + "columns": [ 683 + "email" 684 + ] 685 + } 686 + }, 687 + "policies": {}, 688 + "checkConstraints": {}, 689 + "isRLSEnabled": false 690 + }, 691 + "public.dropbox_directories": { 692 + "name": "dropbox_directories", 693 + "schema": "", 694 + "columns": { 695 + "xata_id": { 696 + "name": "xata_id", 697 + "type": "text", 698 + "primaryKey": true, 699 + "notNull": true 700 + }, 701 + "name": { 702 + "name": "name", 703 + "type": "text", 704 + "primaryKey": false, 705 + "notNull": true 706 + }, 707 + "path": { 708 + "name": "path", 709 + "type": "text", 710 + "primaryKey": false, 711 + "notNull": true 712 + }, 713 + "parent_id": { 714 + "name": "parent_id", 715 + "type": "text", 716 + "primaryKey": false, 717 + "notNull": false 718 + }, 719 + "dropbox_id": { 720 + "name": "dropbox_id", 721 + "type": "text", 722 + "primaryKey": false, 723 + "notNull": true 724 + }, 725 + "file_id": { 726 + "name": "file_id", 727 + "type": "text", 728 + "primaryKey": false, 729 + "notNull": true 730 + }, 731 + "xata_version": { 732 + "name": "xata_version", 733 + "type": "text", 734 + "primaryKey": false, 735 + "notNull": false 736 + }, 737 + "xata_createdat": { 738 + "name": "xata_createdat", 739 + "type": "timestamp", 740 + "primaryKey": false, 741 + "notNull": true, 742 + "default": "now()" 743 + }, 744 + "xata_updatedat": { 745 + "name": "xata_updatedat", 746 + "type": "timestamp", 747 + "primaryKey": false, 748 + "notNull": true, 749 + "default": "now()" 750 + } 751 + }, 752 + "indexes": {}, 753 + "foreignKeys": { 754 + "dropbox_directories_parent_id_dropbox_directories_xata_id_fk": { 755 + "name": "dropbox_directories_parent_id_dropbox_directories_xata_id_fk", 756 + "tableFrom": "dropbox_directories", 757 + "tableTo": "dropbox_directories", 758 + "columnsFrom": [ 759 + "parent_id" 760 + ], 761 + "columnsTo": [ 762 + "xata_id" 763 + ], 764 + "onDelete": "no action", 765 + "onUpdate": "no action" 766 + } 767 + }, 768 + "compositePrimaryKeys": {}, 769 + "uniqueConstraints": { 770 + "dropbox_directories_file_id_unique": { 771 + "name": "dropbox_directories_file_id_unique", 772 + "nullsNotDistinct": false, 773 + "columns": [ 774 + "file_id" 775 + ] 776 + } 777 + }, 778 + "policies": {}, 779 + "checkConstraints": {}, 780 + "isRLSEnabled": false 781 + }, 782 + "public.dropbox_paths": { 783 + "name": "dropbox_paths", 784 + "schema": "", 785 + "columns": { 786 + "xata_id": { 787 + "name": "xata_id", 788 + "type": "text", 789 + "primaryKey": true, 790 + "notNull": true 791 + }, 792 + "path": { 793 + "name": "path", 794 + "type": "text", 795 + "primaryKey": false, 796 + "notNull": true 797 + }, 798 + "name": { 799 + "name": "name", 800 + "type": "text", 801 + "primaryKey": false, 802 + "notNull": true 803 + }, 804 + "dropbox_id": { 805 + "name": "dropbox_id", 806 + "type": "text", 807 + "primaryKey": false, 808 + "notNull": true 809 + }, 810 + "track_id": { 811 + "name": "track_id", 812 + "type": "text", 813 + "primaryKey": false, 814 + "notNull": true 815 + }, 816 + "directory_id": { 817 + "name": "directory_id", 818 + "type": "text", 819 + "primaryKey": false, 820 + "notNull": false 821 + }, 822 + "file_id": { 823 + "name": "file_id", 824 + "type": "text", 825 + "primaryKey": false, 826 + "notNull": true 827 + }, 828 + "xata_version": { 829 + "name": "xata_version", 830 + "type": "text", 831 + "primaryKey": false, 832 + "notNull": false 833 + }, 834 + "xata_createdat": { 835 + "name": "xata_createdat", 836 + "type": "timestamp", 837 + "primaryKey": false, 838 + "notNull": true, 839 + "default": "now()" 840 + }, 841 + "xata_updatedat": { 842 + "name": "xata_updatedat", 843 + "type": "timestamp", 844 + "primaryKey": false, 845 + "notNull": true, 846 + "default": "now()" 847 + } 848 + }, 849 + "indexes": {}, 850 + "foreignKeys": { 851 + "dropbox_paths_directory_id_dropbox_directories_xata_id_fk": { 852 + "name": "dropbox_paths_directory_id_dropbox_directories_xata_id_fk", 853 + "tableFrom": "dropbox_paths", 854 + "tableTo": "dropbox_directories", 855 + "columnsFrom": [ 856 + "directory_id" 857 + ], 858 + "columnsTo": [ 859 + "xata_id" 860 + ], 861 + "onDelete": "no action", 862 + "onUpdate": "no action" 863 + } 864 + }, 865 + "compositePrimaryKeys": {}, 866 + "uniqueConstraints": { 867 + "dropbox_paths_file_id_unique": { 868 + "name": "dropbox_paths_file_id_unique", 869 + "nullsNotDistinct": false, 870 + "columns": [ 871 + "file_id" 872 + ] 873 + } 874 + }, 875 + "policies": {}, 876 + "checkConstraints": {}, 877 + "isRLSEnabled": false 878 + }, 879 + "public.dropbox_tokens": { 880 + "name": "dropbox_tokens", 881 + "schema": "", 882 + "columns": { 883 + "xata_id": { 884 + "name": "xata_id", 885 + "type": "text", 886 + "primaryKey": true, 887 + "notNull": true 888 + }, 889 + "refresh_token": { 890 + "name": "refresh_token", 891 + "type": "text", 892 + "primaryKey": false, 893 + "notNull": true 894 + }, 895 + "xata_createdat": { 896 + "name": "xata_createdat", 897 + "type": "timestamp", 898 + "primaryKey": false, 899 + "notNull": true, 900 + "default": "now()" 901 + }, 902 + "xata_updatedat": { 903 + "name": "xata_updatedat", 904 + "type": "timestamp", 905 + "primaryKey": false, 906 + "notNull": true, 907 + "default": "now()" 908 + } 909 + }, 910 + "indexes": {}, 911 + "foreignKeys": {}, 912 + "compositePrimaryKeys": {}, 913 + "uniqueConstraints": {}, 914 + "policies": {}, 915 + "checkConstraints": {}, 916 + "isRLSEnabled": false 917 + }, 918 + "public.dropbox": { 919 + "name": "dropbox", 920 + "schema": "", 921 + "columns": { 922 + "xata_id": { 923 + "name": "xata_id", 924 + "type": "text", 925 + "primaryKey": true, 926 + "notNull": true 927 + }, 928 + "user_id": { 929 + "name": "user_id", 930 + "type": "text", 931 + "primaryKey": false, 932 + "notNull": true 933 + }, 934 + "dropbox_token_id": { 935 + "name": "dropbox_token_id", 936 + "type": "text", 937 + "primaryKey": false, 938 + "notNull": true 939 + }, 940 + "xata_version": { 941 + "name": "xata_version", 942 + "type": "text", 943 + "primaryKey": false, 944 + "notNull": false 945 + }, 946 + "xata_createdat": { 947 + "name": "xata_createdat", 948 + "type": "timestamp", 949 + "primaryKey": false, 950 + "notNull": true, 951 + "default": "now()" 952 + }, 953 + "xata_updatedat": { 954 + "name": "xata_updatedat", 955 + "type": "timestamp", 956 + "primaryKey": false, 957 + "notNull": true, 958 + "default": "now()" 959 + } 960 + }, 961 + "indexes": {}, 962 + "foreignKeys": { 963 + "dropbox_user_id_users_xata_id_fk": { 964 + "name": "dropbox_user_id_users_xata_id_fk", 965 + "tableFrom": "dropbox", 966 + "tableTo": "users", 967 + "columnsFrom": [ 968 + "user_id" 969 + ], 970 + "columnsTo": [ 971 + "xata_id" 972 + ], 973 + "onDelete": "no action", 974 + "onUpdate": "no action" 975 + }, 976 + "dropbox_dropbox_token_id_dropbox_tokens_xata_id_fk": { 977 + "name": "dropbox_dropbox_token_id_dropbox_tokens_xata_id_fk", 978 + "tableFrom": "dropbox", 979 + "tableTo": "dropbox_tokens", 980 + "columnsFrom": [ 981 + "dropbox_token_id" 982 + ], 983 + "columnsTo": [ 984 + "xata_id" 985 + ], 986 + "onDelete": "no action", 987 + "onUpdate": "no action" 988 + } 989 + }, 990 + "compositePrimaryKeys": {}, 991 + "uniqueConstraints": {}, 992 + "policies": {}, 993 + "checkConstraints": {}, 994 + "isRLSEnabled": false 995 + }, 996 + "public.google_drive_accounts": { 997 + "name": "google_drive_accounts", 998 + "schema": "", 999 + "columns": { 1000 + "xata_id": { 1001 + "name": "xata_id", 1002 + "type": "text", 1003 + "primaryKey": true, 1004 + "notNull": true 1005 + }, 1006 + "email": { 1007 + "name": "email", 1008 + "type": "text", 1009 + "primaryKey": false, 1010 + "notNull": true 1011 + }, 1012 + "is_beta_user": { 1013 + "name": "is_beta_user", 1014 + "type": "boolean", 1015 + "primaryKey": false, 1016 + "notNull": true, 1017 + "default": false 1018 + }, 1019 + "user_id": { 1020 + "name": "user_id", 1021 + "type": "text", 1022 + "primaryKey": false, 1023 + "notNull": true 1024 + }, 1025 + "xata_version": { 1026 + "name": "xata_version", 1027 + "type": "text", 1028 + "primaryKey": false, 1029 + "notNull": false 1030 + }, 1031 + "xata_createdat": { 1032 + "name": "xata_createdat", 1033 + "type": "timestamp", 1034 + "primaryKey": false, 1035 + "notNull": true, 1036 + "default": "now()" 1037 + }, 1038 + "xata_updatedat": { 1039 + "name": "xata_updatedat", 1040 + "type": "timestamp", 1041 + "primaryKey": false, 1042 + "notNull": true, 1043 + "default": "now()" 1044 + } 1045 + }, 1046 + "indexes": {}, 1047 + "foreignKeys": { 1048 + "google_drive_accounts_user_id_users_xata_id_fk": { 1049 + "name": "google_drive_accounts_user_id_users_xata_id_fk", 1050 + "tableFrom": "google_drive_accounts", 1051 + "tableTo": "users", 1052 + "columnsFrom": [ 1053 + "user_id" 1054 + ], 1055 + "columnsTo": [ 1056 + "xata_id" 1057 + ], 1058 + "onDelete": "no action", 1059 + "onUpdate": "no action" 1060 + } 1061 + }, 1062 + "compositePrimaryKeys": {}, 1063 + "uniqueConstraints": { 1064 + "google_drive_accounts_email_unique": { 1065 + "name": "google_drive_accounts_email_unique", 1066 + "nullsNotDistinct": false, 1067 + "columns": [ 1068 + "email" 1069 + ] 1070 + } 1071 + }, 1072 + "policies": {}, 1073 + "checkConstraints": {}, 1074 + "isRLSEnabled": false 1075 + }, 1076 + "public.google_drive_directories": { 1077 + "name": "google_drive_directories", 1078 + "schema": "", 1079 + "columns": { 1080 + "xata_id": { 1081 + "name": "xata_id", 1082 + "type": "text", 1083 + "primaryKey": true, 1084 + "notNull": true 1085 + }, 1086 + "name": { 1087 + "name": "name", 1088 + "type": "text", 1089 + "primaryKey": false, 1090 + "notNull": true 1091 + }, 1092 + "path": { 1093 + "name": "path", 1094 + "type": "text", 1095 + "primaryKey": false, 1096 + "notNull": true 1097 + }, 1098 + "parent_id": { 1099 + "name": "parent_id", 1100 + "type": "text", 1101 + "primaryKey": false, 1102 + "notNull": false 1103 + }, 1104 + "google_drive_id": { 1105 + "name": "google_drive_id", 1106 + "type": "text", 1107 + "primaryKey": false, 1108 + "notNull": true 1109 + }, 1110 + "file_id": { 1111 + "name": "file_id", 1112 + "type": "text", 1113 + "primaryKey": false, 1114 + "notNull": true 1115 + }, 1116 + "xata_version": { 1117 + "name": "xata_version", 1118 + "type": "text", 1119 + "primaryKey": false, 1120 + "notNull": false 1121 + }, 1122 + "xata_createdat": { 1123 + "name": "xata_createdat", 1124 + "type": "timestamp", 1125 + "primaryKey": false, 1126 + "notNull": true, 1127 + "default": "now()" 1128 + }, 1129 + "xata_updatedat": { 1130 + "name": "xata_updatedat", 1131 + "type": "timestamp", 1132 + "primaryKey": false, 1133 + "notNull": true, 1134 + "default": "now()" 1135 + } 1136 + }, 1137 + "indexes": {}, 1138 + "foreignKeys": { 1139 + "google_drive_directories_parent_id_google_drive_directories_xata_id_fk": { 1140 + "name": "google_drive_directories_parent_id_google_drive_directories_xata_id_fk", 1141 + "tableFrom": "google_drive_directories", 1142 + "tableTo": "google_drive_directories", 1143 + "columnsFrom": [ 1144 + "parent_id" 1145 + ], 1146 + "columnsTo": [ 1147 + "xata_id" 1148 + ], 1149 + "onDelete": "no action", 1150 + "onUpdate": "no action" 1151 + } 1152 + }, 1153 + "compositePrimaryKeys": {}, 1154 + "uniqueConstraints": { 1155 + "google_drive_directories_file_id_unique": { 1156 + "name": "google_drive_directories_file_id_unique", 1157 + "nullsNotDistinct": false, 1158 + "columns": [ 1159 + "file_id" 1160 + ] 1161 + } 1162 + }, 1163 + "policies": {}, 1164 + "checkConstraints": {}, 1165 + "isRLSEnabled": false 1166 + }, 1167 + "public.google_drive_paths": { 1168 + "name": "google_drive_paths", 1169 + "schema": "", 1170 + "columns": { 1171 + "xata_id": { 1172 + "name": "xata_id", 1173 + "type": "text", 1174 + "primaryKey": true, 1175 + "notNull": true 1176 + }, 1177 + "google_drive_id": { 1178 + "name": "google_drive_id", 1179 + "type": "text", 1180 + "primaryKey": false, 1181 + "notNull": true 1182 + }, 1183 + "track_id": { 1184 + "name": "track_id", 1185 + "type": "text", 1186 + "primaryKey": false, 1187 + "notNull": true 1188 + }, 1189 + "name": { 1190 + "name": "name", 1191 + "type": "text", 1192 + "primaryKey": false, 1193 + "notNull": true 1194 + }, 1195 + "directory_id": { 1196 + "name": "directory_id", 1197 + "type": "text", 1198 + "primaryKey": false, 1199 + "notNull": false 1200 + }, 1201 + "file_id": { 1202 + "name": "file_id", 1203 + "type": "text", 1204 + "primaryKey": false, 1205 + "notNull": true 1206 + }, 1207 + "xata_version": { 1208 + "name": "xata_version", 1209 + "type": "text", 1210 + "primaryKey": false, 1211 + "notNull": false 1212 + }, 1213 + "xata_createdat": { 1214 + "name": "xata_createdat", 1215 + "type": "timestamp", 1216 + "primaryKey": false, 1217 + "notNull": true, 1218 + "default": "now()" 1219 + }, 1220 + "xata_updatedat": { 1221 + "name": "xata_updatedat", 1222 + "type": "timestamp", 1223 + "primaryKey": false, 1224 + "notNull": true, 1225 + "default": "now()" 1226 + } 1227 + }, 1228 + "indexes": {}, 1229 + "foreignKeys": { 1230 + "google_drive_paths_directory_id_google_drive_directories_xata_id_fk": { 1231 + "name": "google_drive_paths_directory_id_google_drive_directories_xata_id_fk", 1232 + "tableFrom": "google_drive_paths", 1233 + "tableTo": "google_drive_directories", 1234 + "columnsFrom": [ 1235 + "directory_id" 1236 + ], 1237 + "columnsTo": [ 1238 + "xata_id" 1239 + ], 1240 + "onDelete": "no action", 1241 + "onUpdate": "no action" 1242 + } 1243 + }, 1244 + "compositePrimaryKeys": {}, 1245 + "uniqueConstraints": { 1246 + "google_drive_paths_file_id_unique": { 1247 + "name": "google_drive_paths_file_id_unique", 1248 + "nullsNotDistinct": false, 1249 + "columns": [ 1250 + "file_id" 1251 + ] 1252 + } 1253 + }, 1254 + "policies": {}, 1255 + "checkConstraints": {}, 1256 + "isRLSEnabled": false 1257 + }, 1258 + "public.google_drive_tokens": { 1259 + "name": "google_drive_tokens", 1260 + "schema": "", 1261 + "columns": { 1262 + "xata_id": { 1263 + "name": "xata_id", 1264 + "type": "text", 1265 + "primaryKey": true, 1266 + "notNull": true 1267 + }, 1268 + "refresh_token": { 1269 + "name": "refresh_token", 1270 + "type": "text", 1271 + "primaryKey": false, 1272 + "notNull": true 1273 + }, 1274 + "xata_createdat": { 1275 + "name": "xata_createdat", 1276 + "type": "timestamp", 1277 + "primaryKey": false, 1278 + "notNull": true, 1279 + "default": "now()" 1280 + }, 1281 + "xata_updatedat": { 1282 + "name": "xata_updatedat", 1283 + "type": "timestamp", 1284 + "primaryKey": false, 1285 + "notNull": true, 1286 + "default": "now()" 1287 + } 1288 + }, 1289 + "indexes": {}, 1290 + "foreignKeys": {}, 1291 + "compositePrimaryKeys": {}, 1292 + "uniqueConstraints": {}, 1293 + "policies": {}, 1294 + "checkConstraints": {}, 1295 + "isRLSEnabled": false 1296 + }, 1297 + "public.google_drive": { 1298 + "name": "google_drive", 1299 + "schema": "", 1300 + "columns": { 1301 + "xata_id": { 1302 + "name": "xata_id", 1303 + "type": "text", 1304 + "primaryKey": true, 1305 + "notNull": true 1306 + }, 1307 + "google_drive_token_id": { 1308 + "name": "google_drive_token_id", 1309 + "type": "text", 1310 + "primaryKey": false, 1311 + "notNull": true 1312 + }, 1313 + "user_id": { 1314 + "name": "user_id", 1315 + "type": "text", 1316 + "primaryKey": false, 1317 + "notNull": true 1318 + }, 1319 + "xata_version": { 1320 + "name": "xata_version", 1321 + "type": "text", 1322 + "primaryKey": false, 1323 + "notNull": false 1324 + }, 1325 + "xata_createdat": { 1326 + "name": "xata_createdat", 1327 + "type": "timestamp", 1328 + "primaryKey": false, 1329 + "notNull": true, 1330 + "default": "now()" 1331 + }, 1332 + "xata_updatedat": { 1333 + "name": "xata_updatedat", 1334 + "type": "timestamp", 1335 + "primaryKey": false, 1336 + "notNull": true, 1337 + "default": "now()" 1338 + } 1339 + }, 1340 + "indexes": {}, 1341 + "foreignKeys": { 1342 + "google_drive_google_drive_token_id_google_drive_tokens_xata_id_fk": { 1343 + "name": "google_drive_google_drive_token_id_google_drive_tokens_xata_id_fk", 1344 + "tableFrom": "google_drive", 1345 + "tableTo": "google_drive_tokens", 1346 + "columnsFrom": [ 1347 + "google_drive_token_id" 1348 + ], 1349 + "columnsTo": [ 1350 + "xata_id" 1351 + ], 1352 + "onDelete": "no action", 1353 + "onUpdate": "no action" 1354 + }, 1355 + "google_drive_user_id_users_xata_id_fk": { 1356 + "name": "google_drive_user_id_users_xata_id_fk", 1357 + "tableFrom": "google_drive", 1358 + "tableTo": "users", 1359 + "columnsFrom": [ 1360 + "user_id" 1361 + ], 1362 + "columnsTo": [ 1363 + "xata_id" 1364 + ], 1365 + "onDelete": "no action", 1366 + "onUpdate": "no action" 1367 + } 1368 + }, 1369 + "compositePrimaryKeys": {}, 1370 + "uniqueConstraints": {}, 1371 + "policies": {}, 1372 + "checkConstraints": {}, 1373 + "isRLSEnabled": false 1374 + }, 1375 + "public.loved_tracks": { 1376 + "name": "loved_tracks", 1377 + "schema": "", 1378 + "columns": { 1379 + "xata_id": { 1380 + "name": "xata_id", 1381 + "type": "text", 1382 + "primaryKey": true, 1383 + "notNull": true 1384 + }, 1385 + "user_id": { 1386 + "name": "user_id", 1387 + "type": "text", 1388 + "primaryKey": false, 1389 + "notNull": true 1390 + }, 1391 + "track_id": { 1392 + "name": "track_id", 1393 + "type": "text", 1394 + "primaryKey": false, 1395 + "notNull": true 1396 + }, 1397 + "uri": { 1398 + "name": "uri", 1399 + "type": "text", 1400 + "primaryKey": false, 1401 + "notNull": false 1402 + }, 1403 + "xata_createdat": { 1404 + "name": "xata_createdat", 1405 + "type": "timestamp", 1406 + "primaryKey": false, 1407 + "notNull": true, 1408 + "default": "now()" 1409 + } 1410 + }, 1411 + "indexes": {}, 1412 + "foreignKeys": { 1413 + "loved_tracks_user_id_users_xata_id_fk": { 1414 + "name": "loved_tracks_user_id_users_xata_id_fk", 1415 + "tableFrom": "loved_tracks", 1416 + "tableTo": "users", 1417 + "columnsFrom": [ 1418 + "user_id" 1419 + ], 1420 + "columnsTo": [ 1421 + "xata_id" 1422 + ], 1423 + "onDelete": "no action", 1424 + "onUpdate": "no action" 1425 + }, 1426 + "loved_tracks_track_id_tracks_xata_id_fk": { 1427 + "name": "loved_tracks_track_id_tracks_xata_id_fk", 1428 + "tableFrom": "loved_tracks", 1429 + "tableTo": "tracks", 1430 + "columnsFrom": [ 1431 + "track_id" 1432 + ], 1433 + "columnsTo": [ 1434 + "xata_id" 1435 + ], 1436 + "onDelete": "no action", 1437 + "onUpdate": "no action" 1438 + } 1439 + }, 1440 + "compositePrimaryKeys": {}, 1441 + "uniqueConstraints": { 1442 + "loved_tracks_uri_unique": { 1443 + "name": "loved_tracks_uri_unique", 1444 + "nullsNotDistinct": false, 1445 + "columns": [ 1446 + "uri" 1447 + ] 1448 + } 1449 + }, 1450 + "policies": {}, 1451 + "checkConstraints": {}, 1452 + "isRLSEnabled": false 1453 + }, 1454 + "public.playlist_tracks": { 1455 + "name": "playlist_tracks", 1456 + "schema": "", 1457 + "columns": { 1458 + "xata_id": { 1459 + "name": "xata_id", 1460 + "type": "text", 1461 + "primaryKey": true, 1462 + "notNull": true 1463 + }, 1464 + "playlist_id": { 1465 + "name": "playlist_id", 1466 + "type": "text", 1467 + "primaryKey": false, 1468 + "notNull": true 1469 + }, 1470 + "track_id": { 1471 + "name": "track_id", 1472 + "type": "text", 1473 + "primaryKey": false, 1474 + "notNull": true 1475 + }, 1476 + "xata_createdat": { 1477 + "name": "xata_createdat", 1478 + "type": "timestamp", 1479 + "primaryKey": false, 1480 + "notNull": true, 1481 + "default": "now()" 1482 + } 1483 + }, 1484 + "indexes": {}, 1485 + "foreignKeys": { 1486 + "playlist_tracks_playlist_id_playlists_xata_id_fk": { 1487 + "name": "playlist_tracks_playlist_id_playlists_xata_id_fk", 1488 + "tableFrom": "playlist_tracks", 1489 + "tableTo": "playlists", 1490 + "columnsFrom": [ 1491 + "playlist_id" 1492 + ], 1493 + "columnsTo": [ 1494 + "xata_id" 1495 + ], 1496 + "onDelete": "no action", 1497 + "onUpdate": "no action" 1498 + }, 1499 + "playlist_tracks_track_id_tracks_xata_id_fk": { 1500 + "name": "playlist_tracks_track_id_tracks_xata_id_fk", 1501 + "tableFrom": "playlist_tracks", 1502 + "tableTo": "tracks", 1503 + "columnsFrom": [ 1504 + "track_id" 1505 + ], 1506 + "columnsTo": [ 1507 + "xata_id" 1508 + ], 1509 + "onDelete": "no action", 1510 + "onUpdate": "no action" 1511 + } 1512 + }, 1513 + "compositePrimaryKeys": {}, 1514 + "uniqueConstraints": {}, 1515 + "policies": {}, 1516 + "checkConstraints": {}, 1517 + "isRLSEnabled": false 1518 + }, 1519 + "public.playlists": { 1520 + "name": "playlists", 1521 + "schema": "", 1522 + "columns": { 1523 + "xata_id": { 1524 + "name": "xata_id", 1525 + "type": "text", 1526 + "primaryKey": true, 1527 + "notNull": true 1528 + }, 1529 + "name": { 1530 + "name": "name", 1531 + "type": "text", 1532 + "primaryKey": false, 1533 + "notNull": true 1534 + }, 1535 + "picture": { 1536 + "name": "picture", 1537 + "type": "text", 1538 + "primaryKey": false, 1539 + "notNull": false 1540 + }, 1541 + "description": { 1542 + "name": "description", 1543 + "type": "text", 1544 + "primaryKey": false, 1545 + "notNull": false 1546 + }, 1547 + "uri": { 1548 + "name": "uri", 1549 + "type": "text", 1550 + "primaryKey": false, 1551 + "notNull": false 1552 + }, 1553 + "spotify_link": { 1554 + "name": "spotify_link", 1555 + "type": "text", 1556 + "primaryKey": false, 1557 + "notNull": false 1558 + }, 1559 + "tidal_link": { 1560 + "name": "tidal_link", 1561 + "type": "text", 1562 + "primaryKey": false, 1563 + "notNull": false 1564 + }, 1565 + "apple_music_link": { 1566 + "name": "apple_music_link", 1567 + "type": "text", 1568 + "primaryKey": false, 1569 + "notNull": false 1570 + }, 1571 + "created_by": { 1572 + "name": "created_by", 1573 + "type": "text", 1574 + "primaryKey": false, 1575 + "notNull": true 1576 + }, 1577 + "xata_createdat": { 1578 + "name": "xata_createdat", 1579 + "type": "timestamp", 1580 + "primaryKey": false, 1581 + "notNull": true, 1582 + "default": "now()" 1583 + }, 1584 + "xata_updatedat": { 1585 + "name": "xata_updatedat", 1586 + "type": "timestamp", 1587 + "primaryKey": false, 1588 + "notNull": true, 1589 + "default": "now()" 1590 + } 1591 + }, 1592 + "indexes": {}, 1593 + "foreignKeys": { 1594 + "playlists_created_by_users_xata_id_fk": { 1595 + "name": "playlists_created_by_users_xata_id_fk", 1596 + "tableFrom": "playlists", 1597 + "tableTo": "users", 1598 + "columnsFrom": [ 1599 + "created_by" 1600 + ], 1601 + "columnsTo": [ 1602 + "xata_id" 1603 + ], 1604 + "onDelete": "no action", 1605 + "onUpdate": "no action" 1606 + } 1607 + }, 1608 + "compositePrimaryKeys": {}, 1609 + "uniqueConstraints": { 1610 + "playlists_uri_unique": { 1611 + "name": "playlists_uri_unique", 1612 + "nullsNotDistinct": false, 1613 + "columns": [ 1614 + "uri" 1615 + ] 1616 + } 1617 + }, 1618 + "policies": {}, 1619 + "checkConstraints": {}, 1620 + "isRLSEnabled": false 1621 + }, 1622 + "public.profile_shouts": { 1623 + "name": "profile_shouts", 1624 + "schema": "", 1625 + "columns": { 1626 + "xata_id": { 1627 + "name": "xata_id", 1628 + "type": "text", 1629 + "primaryKey": true, 1630 + "notNull": true 1631 + }, 1632 + "user_id": { 1633 + "name": "user_id", 1634 + "type": "text", 1635 + "primaryKey": false, 1636 + "notNull": true 1637 + }, 1638 + "shout_id": { 1639 + "name": "shout_id", 1640 + "type": "text", 1641 + "primaryKey": false, 1642 + "notNull": true 1643 + }, 1644 + "xata_createdat": { 1645 + "name": "xata_createdat", 1646 + "type": "timestamp", 1647 + "primaryKey": false, 1648 + "notNull": true, 1649 + "default": "now()" 1650 + } 1651 + }, 1652 + "indexes": {}, 1653 + "foreignKeys": { 1654 + "profile_shouts_user_id_users_xata_id_fk": { 1655 + "name": "profile_shouts_user_id_users_xata_id_fk", 1656 + "tableFrom": "profile_shouts", 1657 + "tableTo": "users", 1658 + "columnsFrom": [ 1659 + "user_id" 1660 + ], 1661 + "columnsTo": [ 1662 + "xata_id" 1663 + ], 1664 + "onDelete": "no action", 1665 + "onUpdate": "no action" 1666 + }, 1667 + "profile_shouts_shout_id_shouts_xata_id_fk": { 1668 + "name": "profile_shouts_shout_id_shouts_xata_id_fk", 1669 + "tableFrom": "profile_shouts", 1670 + "tableTo": "shouts", 1671 + "columnsFrom": [ 1672 + "shout_id" 1673 + ], 1674 + "columnsTo": [ 1675 + "xata_id" 1676 + ], 1677 + "onDelete": "no action", 1678 + "onUpdate": "no action" 1679 + } 1680 + }, 1681 + "compositePrimaryKeys": {}, 1682 + "uniqueConstraints": {}, 1683 + "policies": {}, 1684 + "checkConstraints": {}, 1685 + "isRLSEnabled": false 1686 + }, 1687 + "public.queue_tracks": { 1688 + "name": "queue_tracks", 1689 + "schema": "", 1690 + "columns": { 1691 + "xata_id": { 1692 + "name": "xata_id", 1693 + "type": "text", 1694 + "primaryKey": true, 1695 + "notNull": true 1696 + }, 1697 + "user_id": { 1698 + "name": "user_id", 1699 + "type": "text", 1700 + "primaryKey": false, 1701 + "notNull": true 1702 + }, 1703 + "track_id": { 1704 + "name": "track_id", 1705 + "type": "text", 1706 + "primaryKey": false, 1707 + "notNull": true 1708 + }, 1709 + "position": { 1710 + "name": "position", 1711 + "type": "integer", 1712 + "primaryKey": false, 1713 + "notNull": true 1714 + }, 1715 + "file_uri": { 1716 + "name": "file_uri", 1717 + "type": "text", 1718 + "primaryKey": false, 1719 + "notNull": true 1720 + }, 1721 + "xata_version": { 1722 + "name": "xata_version", 1723 + "type": "integer", 1724 + "primaryKey": false, 1725 + "notNull": true, 1726 + "default": 0 1727 + }, 1728 + "xata_createdat": { 1729 + "name": "xata_createdat", 1730 + "type": "timestamp", 1731 + "primaryKey": false, 1732 + "notNull": true, 1733 + "default": "now()" 1734 + }, 1735 + "xata_updatedat": { 1736 + "name": "xata_updatedat", 1737 + "type": "timestamp", 1738 + "primaryKey": false, 1739 + "notNull": true, 1740 + "default": "now()" 1741 + } 1742 + }, 1743 + "indexes": {}, 1744 + "foreignKeys": { 1745 + "queue_tracks_user_id_users_xata_id_fk": { 1746 + "name": "queue_tracks_user_id_users_xata_id_fk", 1747 + "tableFrom": "queue_tracks", 1748 + "tableTo": "users", 1749 + "columnsFrom": [ 1750 + "user_id" 1751 + ], 1752 + "columnsTo": [ 1753 + "xata_id" 1754 + ], 1755 + "onDelete": "no action", 1756 + "onUpdate": "no action" 1757 + }, 1758 + "queue_tracks_track_id_tracks_xata_id_fk": { 1759 + "name": "queue_tracks_track_id_tracks_xata_id_fk", 1760 + "tableFrom": "queue_tracks", 1761 + "tableTo": "tracks", 1762 + "columnsFrom": [ 1763 + "track_id" 1764 + ], 1765 + "columnsTo": [ 1766 + "xata_id" 1767 + ], 1768 + "onDelete": "no action", 1769 + "onUpdate": "no action" 1770 + } 1771 + }, 1772 + "compositePrimaryKeys": {}, 1773 + "uniqueConstraints": {}, 1774 + "policies": {}, 1775 + "checkConstraints": {}, 1776 + "isRLSEnabled": false 1777 + }, 1778 + "public.scrobbles": { 1779 + "name": "scrobbles", 1780 + "schema": "", 1781 + "columns": { 1782 + "xata_id": { 1783 + "name": "xata_id", 1784 + "type": "text", 1785 + "primaryKey": true, 1786 + "notNull": true 1787 + }, 1788 + "user_id": { 1789 + "name": "user_id", 1790 + "type": "text", 1791 + "primaryKey": false, 1792 + "notNull": false 1793 + }, 1794 + "track_id": { 1795 + "name": "track_id", 1796 + "type": "text", 1797 + "primaryKey": false, 1798 + "notNull": false 1799 + }, 1800 + "album_id": { 1801 + "name": "album_id", 1802 + "type": "text", 1803 + "primaryKey": false, 1804 + "notNull": false 1805 + }, 1806 + "artist_id": { 1807 + "name": "artist_id", 1808 + "type": "text", 1809 + "primaryKey": false, 1810 + "notNull": false 1811 + }, 1812 + "uri": { 1813 + "name": "uri", 1814 + "type": "text", 1815 + "primaryKey": false, 1816 + "notNull": false 1817 + }, 1818 + "xata_createdat": { 1819 + "name": "xata_createdat", 1820 + "type": "timestamp", 1821 + "primaryKey": false, 1822 + "notNull": true, 1823 + "default": "now()" 1824 + }, 1825 + "xata_updatedat": { 1826 + "name": "xata_updatedat", 1827 + "type": "timestamp", 1828 + "primaryKey": false, 1829 + "notNull": true, 1830 + "default": "now()" 1831 + }, 1832 + "xata_version": { 1833 + "name": "xata_version", 1834 + "type": "integer", 1835 + "primaryKey": false, 1836 + "notNull": false 1837 + }, 1838 + "timestamp": { 1839 + "name": "timestamp", 1840 + "type": "timestamp", 1841 + "primaryKey": false, 1842 + "notNull": true, 1843 + "default": "now()" 1844 + } 1845 + }, 1846 + "indexes": {}, 1847 + "foreignKeys": { 1848 + "scrobbles_user_id_users_xata_id_fk": { 1849 + "name": "scrobbles_user_id_users_xata_id_fk", 1850 + "tableFrom": "scrobbles", 1851 + "tableTo": "users", 1852 + "columnsFrom": [ 1853 + "user_id" 1854 + ], 1855 + "columnsTo": [ 1856 + "xata_id" 1857 + ], 1858 + "onDelete": "no action", 1859 + "onUpdate": "no action" 1860 + }, 1861 + "scrobbles_track_id_tracks_xata_id_fk": { 1862 + "name": "scrobbles_track_id_tracks_xata_id_fk", 1863 + "tableFrom": "scrobbles", 1864 + "tableTo": "tracks", 1865 + "columnsFrom": [ 1866 + "track_id" 1867 + ], 1868 + "columnsTo": [ 1869 + "xata_id" 1870 + ], 1871 + "onDelete": "no action", 1872 + "onUpdate": "no action" 1873 + }, 1874 + "scrobbles_album_id_albums_xata_id_fk": { 1875 + "name": "scrobbles_album_id_albums_xata_id_fk", 1876 + "tableFrom": "scrobbles", 1877 + "tableTo": "albums", 1878 + "columnsFrom": [ 1879 + "album_id" 1880 + ], 1881 + "columnsTo": [ 1882 + "xata_id" 1883 + ], 1884 + "onDelete": "no action", 1885 + "onUpdate": "no action" 1886 + }, 1887 + "scrobbles_artist_id_artists_xata_id_fk": { 1888 + "name": "scrobbles_artist_id_artists_xata_id_fk", 1889 + "tableFrom": "scrobbles", 1890 + "tableTo": "artists", 1891 + "columnsFrom": [ 1892 + "artist_id" 1893 + ], 1894 + "columnsTo": [ 1895 + "xata_id" 1896 + ], 1897 + "onDelete": "no action", 1898 + "onUpdate": "no action" 1899 + } 1900 + }, 1901 + "compositePrimaryKeys": {}, 1902 + "uniqueConstraints": { 1903 + "scrobbles_uri_unique": { 1904 + "name": "scrobbles_uri_unique", 1905 + "nullsNotDistinct": false, 1906 + "columns": [ 1907 + "uri" 1908 + ] 1909 + } 1910 + }, 1911 + "policies": {}, 1912 + "checkConstraints": {}, 1913 + "isRLSEnabled": false 1914 + }, 1915 + "public.shout_likes": { 1916 + "name": "shout_likes", 1917 + "schema": "", 1918 + "columns": { 1919 + "xata_id": { 1920 + "name": "xata_id", 1921 + "type": "text", 1922 + "primaryKey": true, 1923 + "notNull": true 1924 + }, 1925 + "user_id": { 1926 + "name": "user_id", 1927 + "type": "text", 1928 + "primaryKey": false, 1929 + "notNull": true 1930 + }, 1931 + "shout_id": { 1932 + "name": "shout_id", 1933 + "type": "text", 1934 + "primaryKey": false, 1935 + "notNull": true 1936 + }, 1937 + "xata_createdat": { 1938 + "name": "xata_createdat", 1939 + "type": "timestamp", 1940 + "primaryKey": false, 1941 + "notNull": true, 1942 + "default": "now()" 1943 + }, 1944 + "uri": { 1945 + "name": "uri", 1946 + "type": "text", 1947 + "primaryKey": false, 1948 + "notNull": true 1949 + } 1950 + }, 1951 + "indexes": {}, 1952 + "foreignKeys": { 1953 + "shout_likes_user_id_users_xata_id_fk": { 1954 + "name": "shout_likes_user_id_users_xata_id_fk", 1955 + "tableFrom": "shout_likes", 1956 + "tableTo": "users", 1957 + "columnsFrom": [ 1958 + "user_id" 1959 + ], 1960 + "columnsTo": [ 1961 + "xata_id" 1962 + ], 1963 + "onDelete": "no action", 1964 + "onUpdate": "no action" 1965 + }, 1966 + "shout_likes_shout_id_shouts_xata_id_fk": { 1967 + "name": "shout_likes_shout_id_shouts_xata_id_fk", 1968 + "tableFrom": "shout_likes", 1969 + "tableTo": "shouts", 1970 + "columnsFrom": [ 1971 + "shout_id" 1972 + ], 1973 + "columnsTo": [ 1974 + "xata_id" 1975 + ], 1976 + "onDelete": "no action", 1977 + "onUpdate": "no action" 1978 + } 1979 + }, 1980 + "compositePrimaryKeys": {}, 1981 + "uniqueConstraints": { 1982 + "shout_likes_uri_unique": { 1983 + "name": "shout_likes_uri_unique", 1984 + "nullsNotDistinct": false, 1985 + "columns": [ 1986 + "uri" 1987 + ] 1988 + } 1989 + }, 1990 + "policies": {}, 1991 + "checkConstraints": {}, 1992 + "isRLSEnabled": false 1993 + }, 1994 + "public.shout_reports": { 1995 + "name": "shout_reports", 1996 + "schema": "", 1997 + "columns": { 1998 + "xata_id": { 1999 + "name": "xata_id", 2000 + "type": "text", 2001 + "primaryKey": true, 2002 + "notNull": true 2003 + }, 2004 + "user_id": { 2005 + "name": "user_id", 2006 + "type": "text", 2007 + "primaryKey": false, 2008 + "notNull": true 2009 + }, 2010 + "shout_id": { 2011 + "name": "shout_id", 2012 + "type": "text", 2013 + "primaryKey": false, 2014 + "notNull": true 2015 + }, 2016 + "xata_createdat": { 2017 + "name": "xata_createdat", 2018 + "type": "timestamp", 2019 + "primaryKey": false, 2020 + "notNull": true, 2021 + "default": "now()" 2022 + } 2023 + }, 2024 + "indexes": {}, 2025 + "foreignKeys": { 2026 + "shout_reports_user_id_users_xata_id_fk": { 2027 + "name": "shout_reports_user_id_users_xata_id_fk", 2028 + "tableFrom": "shout_reports", 2029 + "tableTo": "users", 2030 + "columnsFrom": [ 2031 + "user_id" 2032 + ], 2033 + "columnsTo": [ 2034 + "xata_id" 2035 + ], 2036 + "onDelete": "no action", 2037 + "onUpdate": "no action" 2038 + }, 2039 + "shout_reports_shout_id_shouts_xata_id_fk": { 2040 + "name": "shout_reports_shout_id_shouts_xata_id_fk", 2041 + "tableFrom": "shout_reports", 2042 + "tableTo": "shouts", 2043 + "columnsFrom": [ 2044 + "shout_id" 2045 + ], 2046 + "columnsTo": [ 2047 + "xata_id" 2048 + ], 2049 + "onDelete": "no action", 2050 + "onUpdate": "no action" 2051 + } 2052 + }, 2053 + "compositePrimaryKeys": {}, 2054 + "uniqueConstraints": {}, 2055 + "policies": {}, 2056 + "checkConstraints": {}, 2057 + "isRLSEnabled": false 2058 + }, 2059 + "public.shouts": { 2060 + "name": "shouts", 2061 + "schema": "", 2062 + "columns": { 2063 + "xata_id": { 2064 + "name": "xata_id", 2065 + "type": "text", 2066 + "primaryKey": true, 2067 + "notNull": true 2068 + }, 2069 + "content": { 2070 + "name": "content", 2071 + "type": "text", 2072 + "primaryKey": false, 2073 + "notNull": true 2074 + }, 2075 + "track_id": { 2076 + "name": "track_id", 2077 + "type": "text", 2078 + "primaryKey": false, 2079 + "notNull": false 2080 + }, 2081 + "artist_id": { 2082 + "name": "artist_id", 2083 + "type": "text", 2084 + "primaryKey": false, 2085 + "notNull": false 2086 + }, 2087 + "album_id": { 2088 + "name": "album_id", 2089 + "type": "text", 2090 + "primaryKey": false, 2091 + "notNull": false 2092 + }, 2093 + "scrobble_id": { 2094 + "name": "scrobble_id", 2095 + "type": "text", 2096 + "primaryKey": false, 2097 + "notNull": false 2098 + }, 2099 + "uri": { 2100 + "name": "uri", 2101 + "type": "text", 2102 + "primaryKey": false, 2103 + "notNull": true 2104 + }, 2105 + "author_id": { 2106 + "name": "author_id", 2107 + "type": "text", 2108 + "primaryKey": false, 2109 + "notNull": true 2110 + }, 2111 + "parent_id": { 2112 + "name": "parent_id", 2113 + "type": "text", 2114 + "primaryKey": false, 2115 + "notNull": false 2116 + }, 2117 + "xata_createdat": { 2118 + "name": "xata_createdat", 2119 + "type": "timestamp", 2120 + "primaryKey": false, 2121 + "notNull": true, 2122 + "default": "now()" 2123 + }, 2124 + "xata_updatedat": { 2125 + "name": "xata_updatedat", 2126 + "type": "timestamp", 2127 + "primaryKey": false, 2128 + "notNull": true, 2129 + "default": "now()" 2130 + } 2131 + }, 2132 + "indexes": {}, 2133 + "foreignKeys": { 2134 + "shouts_track_id_tracks_xata_id_fk": { 2135 + "name": "shouts_track_id_tracks_xata_id_fk", 2136 + "tableFrom": "shouts", 2137 + "tableTo": "tracks", 2138 + "columnsFrom": [ 2139 + "track_id" 2140 + ], 2141 + "columnsTo": [ 2142 + "xata_id" 2143 + ], 2144 + "onDelete": "no action", 2145 + "onUpdate": "no action" 2146 + }, 2147 + "shouts_artist_id_users_xata_id_fk": { 2148 + "name": "shouts_artist_id_users_xata_id_fk", 2149 + "tableFrom": "shouts", 2150 + "tableTo": "users", 2151 + "columnsFrom": [ 2152 + "artist_id" 2153 + ], 2154 + "columnsTo": [ 2155 + "xata_id" 2156 + ], 2157 + "onDelete": "no action", 2158 + "onUpdate": "no action" 2159 + }, 2160 + "shouts_album_id_albums_xata_id_fk": { 2161 + "name": "shouts_album_id_albums_xata_id_fk", 2162 + "tableFrom": "shouts", 2163 + "tableTo": "albums", 2164 + "columnsFrom": [ 2165 + "album_id" 2166 + ], 2167 + "columnsTo": [ 2168 + "xata_id" 2169 + ], 2170 + "onDelete": "no action", 2171 + "onUpdate": "no action" 2172 + }, 2173 + "shouts_scrobble_id_scrobbles_xata_id_fk": { 2174 + "name": "shouts_scrobble_id_scrobbles_xata_id_fk", 2175 + "tableFrom": "shouts", 2176 + "tableTo": "scrobbles", 2177 + "columnsFrom": [ 2178 + "scrobble_id" 2179 + ], 2180 + "columnsTo": [ 2181 + "xata_id" 2182 + ], 2183 + "onDelete": "no action", 2184 + "onUpdate": "no action" 2185 + }, 2186 + "shouts_author_id_users_xata_id_fk": { 2187 + "name": "shouts_author_id_users_xata_id_fk", 2188 + "tableFrom": "shouts", 2189 + "tableTo": "users", 2190 + "columnsFrom": [ 2191 + "author_id" 2192 + ], 2193 + "columnsTo": [ 2194 + "xata_id" 2195 + ], 2196 + "onDelete": "no action", 2197 + "onUpdate": "no action" 2198 + }, 2199 + "shouts_parent_id_shouts_xata_id_fk": { 2200 + "name": "shouts_parent_id_shouts_xata_id_fk", 2201 + "tableFrom": "shouts", 2202 + "tableTo": "shouts", 2203 + "columnsFrom": [ 2204 + "parent_id" 2205 + ], 2206 + "columnsTo": [ 2207 + "xata_id" 2208 + ], 2209 + "onDelete": "no action", 2210 + "onUpdate": "no action" 2211 + } 2212 + }, 2213 + "compositePrimaryKeys": {}, 2214 + "uniqueConstraints": { 2215 + "shouts_uri_unique": { 2216 + "name": "shouts_uri_unique", 2217 + "nullsNotDistinct": false, 2218 + "columns": [ 2219 + "uri" 2220 + ] 2221 + } 2222 + }, 2223 + "policies": {}, 2224 + "checkConstraints": {}, 2225 + "isRLSEnabled": false 2226 + }, 2227 + "public.spotify_accounts": { 2228 + "name": "spotify_accounts", 2229 + "schema": "", 2230 + "columns": { 2231 + "xata_id": { 2232 + "name": "xata_id", 2233 + "type": "text", 2234 + "primaryKey": true, 2235 + "notNull": true 2236 + }, 2237 + "xata_version": { 2238 + "name": "xata_version", 2239 + "type": "integer", 2240 + "primaryKey": false, 2241 + "notNull": false 2242 + }, 2243 + "email": { 2244 + "name": "email", 2245 + "type": "text", 2246 + "primaryKey": false, 2247 + "notNull": true 2248 + }, 2249 + "user_id": { 2250 + "name": "user_id", 2251 + "type": "text", 2252 + "primaryKey": false, 2253 + "notNull": true 2254 + }, 2255 + "is_beta_user": { 2256 + "name": "is_beta_user", 2257 + "type": "boolean", 2258 + "primaryKey": false, 2259 + "notNull": true, 2260 + "default": false 2261 + }, 2262 + "xata_createdat": { 2263 + "name": "xata_createdat", 2264 + "type": "timestamp", 2265 + "primaryKey": false, 2266 + "notNull": true, 2267 + "default": "now()" 2268 + }, 2269 + "xata_updatedat": { 2270 + "name": "xata_updatedat", 2271 + "type": "timestamp", 2272 + "primaryKey": false, 2273 + "notNull": true, 2274 + "default": "now()" 2275 + } 2276 + }, 2277 + "indexes": {}, 2278 + "foreignKeys": { 2279 + "spotify_accounts_user_id_users_xata_id_fk": { 2280 + "name": "spotify_accounts_user_id_users_xata_id_fk", 2281 + "tableFrom": "spotify_accounts", 2282 + "tableTo": "users", 2283 + "columnsFrom": [ 2284 + "user_id" 2285 + ], 2286 + "columnsTo": [ 2287 + "xata_id" 2288 + ], 2289 + "onDelete": "no action", 2290 + "onUpdate": "no action" 2291 + } 2292 + }, 2293 + "compositePrimaryKeys": {}, 2294 + "uniqueConstraints": {}, 2295 + "policies": {}, 2296 + "checkConstraints": {}, 2297 + "isRLSEnabled": false 2298 + }, 2299 + "public.spotify_tokens": { 2300 + "name": "spotify_tokens", 2301 + "schema": "", 2302 + "columns": { 2303 + "xata_id": { 2304 + "name": "xata_id", 2305 + "type": "text", 2306 + "primaryKey": true, 2307 + "notNull": true 2308 + }, 2309 + "xata_version": { 2310 + "name": "xata_version", 2311 + "type": "integer", 2312 + "primaryKey": false, 2313 + "notNull": false 2314 + }, 2315 + "access_token": { 2316 + "name": "access_token", 2317 + "type": "text", 2318 + "primaryKey": false, 2319 + "notNull": true 2320 + }, 2321 + "refresh_token": { 2322 + "name": "refresh_token", 2323 + "type": "text", 2324 + "primaryKey": false, 2325 + "notNull": true 2326 + }, 2327 + "user_id": { 2328 + "name": "user_id", 2329 + "type": "text", 2330 + "primaryKey": false, 2331 + "notNull": true 2332 + }, 2333 + "xata_createdat": { 2334 + "name": "xata_createdat", 2335 + "type": "timestamp", 2336 + "primaryKey": false, 2337 + "notNull": true, 2338 + "default": "now()" 2339 + }, 2340 + "xata_updatedat": { 2341 + "name": "xata_updatedat", 2342 + "type": "timestamp", 2343 + "primaryKey": false, 2344 + "notNull": true, 2345 + "default": "now()" 2346 + } 2347 + }, 2348 + "indexes": {}, 2349 + "foreignKeys": { 2350 + "spotify_tokens_user_id_users_xata_id_fk": { 2351 + "name": "spotify_tokens_user_id_users_xata_id_fk", 2352 + "tableFrom": "spotify_tokens", 2353 + "tableTo": "users", 2354 + "columnsFrom": [ 2355 + "user_id" 2356 + ], 2357 + "columnsTo": [ 2358 + "xata_id" 2359 + ], 2360 + "onDelete": "no action", 2361 + "onUpdate": "no action" 2362 + } 2363 + }, 2364 + "compositePrimaryKeys": {}, 2365 + "uniqueConstraints": {}, 2366 + "policies": {}, 2367 + "checkConstraints": {}, 2368 + "isRLSEnabled": false 2369 + }, 2370 + "public.tracks": { 2371 + "name": "tracks", 2372 + "schema": "", 2373 + "columns": { 2374 + "xata_id": { 2375 + "name": "xata_id", 2376 + "type": "text", 2377 + "primaryKey": true, 2378 + "notNull": true 2379 + }, 2380 + "title": { 2381 + "name": "title", 2382 + "type": "text", 2383 + "primaryKey": false, 2384 + "notNull": true 2385 + }, 2386 + "artist": { 2387 + "name": "artist", 2388 + "type": "text", 2389 + "primaryKey": false, 2390 + "notNull": true 2391 + }, 2392 + "album_artist": { 2393 + "name": "album_artist", 2394 + "type": "text", 2395 + "primaryKey": false, 2396 + "notNull": true 2397 + }, 2398 + "album_art": { 2399 + "name": "album_art", 2400 + "type": "text", 2401 + "primaryKey": false, 2402 + "notNull": false 2403 + }, 2404 + "album": { 2405 + "name": "album", 2406 + "type": "text", 2407 + "primaryKey": false, 2408 + "notNull": true 2409 + }, 2410 + "track_number": { 2411 + "name": "track_number", 2412 + "type": "integer", 2413 + "primaryKey": false, 2414 + "notNull": false 2415 + }, 2416 + "duration": { 2417 + "name": "duration", 2418 + "type": "integer", 2419 + "primaryKey": false, 2420 + "notNull": true 2421 + }, 2422 + "mb_id": { 2423 + "name": "mb_id", 2424 + "type": "text", 2425 + "primaryKey": false, 2426 + "notNull": false 2427 + }, 2428 + "youtube_link": { 2429 + "name": "youtube_link", 2430 + "type": "text", 2431 + "primaryKey": false, 2432 + "notNull": false 2433 + }, 2434 + "spotify_link": { 2435 + "name": "spotify_link", 2436 + "type": "text", 2437 + "primaryKey": false, 2438 + "notNull": false 2439 + }, 2440 + "apple_music_link": { 2441 + "name": "apple_music_link", 2442 + "type": "text", 2443 + "primaryKey": false, 2444 + "notNull": false 2445 + }, 2446 + "tidal_link": { 2447 + "name": "tidal_link", 2448 + "type": "text", 2449 + "primaryKey": false, 2450 + "notNull": false 2451 + }, 2452 + "sha256": { 2453 + "name": "sha256", 2454 + "type": "text", 2455 + "primaryKey": false, 2456 + "notNull": true 2457 + }, 2458 + "disc_number": { 2459 + "name": "disc_number", 2460 + "type": "integer", 2461 + "primaryKey": false, 2462 + "notNull": false 2463 + }, 2464 + "lyrics": { 2465 + "name": "lyrics", 2466 + "type": "text", 2467 + "primaryKey": false, 2468 + "notNull": false 2469 + }, 2470 + "composer": { 2471 + "name": "composer", 2472 + "type": "text", 2473 + "primaryKey": false, 2474 + "notNull": false 2475 + }, 2476 + "genre": { 2477 + "name": "genre", 2478 + "type": "text", 2479 + "primaryKey": false, 2480 + "notNull": false 2481 + }, 2482 + "label": { 2483 + "name": "label", 2484 + "type": "text", 2485 + "primaryKey": false, 2486 + "notNull": false 2487 + }, 2488 + "copyright_message": { 2489 + "name": "copyright_message", 2490 + "type": "text", 2491 + "primaryKey": false, 2492 + "notNull": false 2493 + }, 2494 + "uri": { 2495 + "name": "uri", 2496 + "type": "text", 2497 + "primaryKey": false, 2498 + "notNull": false 2499 + }, 2500 + "album_uri": { 2501 + "name": "album_uri", 2502 + "type": "text", 2503 + "primaryKey": false, 2504 + "notNull": false 2505 + }, 2506 + "artist_uri": { 2507 + "name": "artist_uri", 2508 + "type": "text", 2509 + "primaryKey": false, 2510 + "notNull": false 2511 + }, 2512 + "xata_createdat": { 2513 + "name": "xata_createdat", 2514 + "type": "timestamp", 2515 + "primaryKey": false, 2516 + "notNull": true, 2517 + "default": "now()" 2518 + }, 2519 + "xata_updatedat": { 2520 + "name": "xata_updatedat", 2521 + "type": "timestamp", 2522 + "primaryKey": false, 2523 + "notNull": true, 2524 + "default": "now()" 2525 + }, 2526 + "xata_version": { 2527 + "name": "xata_version", 2528 + "type": "integer", 2529 + "primaryKey": false, 2530 + "notNull": false 2531 + } 2532 + }, 2533 + "indexes": {}, 2534 + "foreignKeys": {}, 2535 + "compositePrimaryKeys": {}, 2536 + "uniqueConstraints": { 2537 + "tracks_mb_id_unique": { 2538 + "name": "tracks_mb_id_unique", 2539 + "nullsNotDistinct": false, 2540 + "columns": [ 2541 + "mb_id" 2542 + ] 2543 + }, 2544 + "tracks_youtube_link_unique": { 2545 + "name": "tracks_youtube_link_unique", 2546 + "nullsNotDistinct": false, 2547 + "columns": [ 2548 + "youtube_link" 2549 + ] 2550 + }, 2551 + "tracks_spotify_link_unique": { 2552 + "name": "tracks_spotify_link_unique", 2553 + "nullsNotDistinct": false, 2554 + "columns": [ 2555 + "spotify_link" 2556 + ] 2557 + }, 2558 + "tracks_apple_music_link_unique": { 2559 + "name": "tracks_apple_music_link_unique", 2560 + "nullsNotDistinct": false, 2561 + "columns": [ 2562 + "apple_music_link" 2563 + ] 2564 + }, 2565 + "tracks_tidal_link_unique": { 2566 + "name": "tracks_tidal_link_unique", 2567 + "nullsNotDistinct": false, 2568 + "columns": [ 2569 + "tidal_link" 2570 + ] 2571 + }, 2572 + "tracks_sha256_unique": { 2573 + "name": "tracks_sha256_unique", 2574 + "nullsNotDistinct": false, 2575 + "columns": [ 2576 + "sha256" 2577 + ] 2578 + }, 2579 + "tracks_uri_unique": { 2580 + "name": "tracks_uri_unique", 2581 + "nullsNotDistinct": false, 2582 + "columns": [ 2583 + "uri" 2584 + ] 2585 + } 2586 + }, 2587 + "policies": {}, 2588 + "checkConstraints": {}, 2589 + "isRLSEnabled": false 2590 + }, 2591 + "public.user_albums": { 2592 + "name": "user_albums", 2593 + "schema": "", 2594 + "columns": { 2595 + "xata_id": { 2596 + "name": "xata_id", 2597 + "type": "text", 2598 + "primaryKey": true, 2599 + "notNull": true 2600 + }, 2601 + "user_id": { 2602 + "name": "user_id", 2603 + "type": "text", 2604 + "primaryKey": false, 2605 + "notNull": true 2606 + }, 2607 + "album_id": { 2608 + "name": "album_id", 2609 + "type": "text", 2610 + "primaryKey": false, 2611 + "notNull": true 2612 + }, 2613 + "xata_createdat": { 2614 + "name": "xata_createdat", 2615 + "type": "timestamp", 2616 + "primaryKey": false, 2617 + "notNull": true, 2618 + "default": "now()" 2619 + }, 2620 + "xata_updatedat": { 2621 + "name": "xata_updatedat", 2622 + "type": "timestamp", 2623 + "primaryKey": false, 2624 + "notNull": true, 2625 + "default": "now()" 2626 + }, 2627 + "xata_version": { 2628 + "name": "xata_version", 2629 + "type": "integer", 2630 + "primaryKey": false, 2631 + "notNull": false 2632 + }, 2633 + "scrobbles": { 2634 + "name": "scrobbles", 2635 + "type": "integer", 2636 + "primaryKey": false, 2637 + "notNull": false 2638 + }, 2639 + "uri": { 2640 + "name": "uri", 2641 + "type": "text", 2642 + "primaryKey": false, 2643 + "notNull": true 2644 + } 2645 + }, 2646 + "indexes": {}, 2647 + "foreignKeys": { 2648 + "user_albums_user_id_users_xata_id_fk": { 2649 + "name": "user_albums_user_id_users_xata_id_fk", 2650 + "tableFrom": "user_albums", 2651 + "tableTo": "users", 2652 + "columnsFrom": [ 2653 + "user_id" 2654 + ], 2655 + "columnsTo": [ 2656 + "xata_id" 2657 + ], 2658 + "onDelete": "no action", 2659 + "onUpdate": "no action" 2660 + }, 2661 + "user_albums_album_id_albums_xata_id_fk": { 2662 + "name": "user_albums_album_id_albums_xata_id_fk", 2663 + "tableFrom": "user_albums", 2664 + "tableTo": "albums", 2665 + "columnsFrom": [ 2666 + "album_id" 2667 + ], 2668 + "columnsTo": [ 2669 + "xata_id" 2670 + ], 2671 + "onDelete": "no action", 2672 + "onUpdate": "no action" 2673 + } 2674 + }, 2675 + "compositePrimaryKeys": {}, 2676 + "uniqueConstraints": { 2677 + "user_albums_uri_unique": { 2678 + "name": "user_albums_uri_unique", 2679 + "nullsNotDistinct": false, 2680 + "columns": [ 2681 + "uri" 2682 + ] 2683 + } 2684 + }, 2685 + "policies": {}, 2686 + "checkConstraints": {}, 2687 + "isRLSEnabled": false 2688 + }, 2689 + "public.user_artists": { 2690 + "name": "user_artists", 2691 + "schema": "", 2692 + "columns": { 2693 + "xata_id": { 2694 + "name": "xata_id", 2695 + "type": "text", 2696 + "primaryKey": true, 2697 + "notNull": true 2698 + }, 2699 + "user_id": { 2700 + "name": "user_id", 2701 + "type": "text", 2702 + "primaryKey": false, 2703 + "notNull": true 2704 + }, 2705 + "artist_id": { 2706 + "name": "artist_id", 2707 + "type": "text", 2708 + "primaryKey": false, 2709 + "notNull": true 2710 + }, 2711 + "xata_createdat": { 2712 + "name": "xata_createdat", 2713 + "type": "timestamp", 2714 + "primaryKey": false, 2715 + "notNull": true, 2716 + "default": "now()" 2717 + }, 2718 + "xata_updatedat": { 2719 + "name": "xata_updatedat", 2720 + "type": "timestamp", 2721 + "primaryKey": false, 2722 + "notNull": true, 2723 + "default": "now()" 2724 + }, 2725 + "xata_version": { 2726 + "name": "xata_version", 2727 + "type": "integer", 2728 + "primaryKey": false, 2729 + "notNull": false 2730 + }, 2731 + "scrobbles": { 2732 + "name": "scrobbles", 2733 + "type": "integer", 2734 + "primaryKey": false, 2735 + "notNull": false 2736 + }, 2737 + "uri": { 2738 + "name": "uri", 2739 + "type": "text", 2740 + "primaryKey": false, 2741 + "notNull": true 2742 + } 2743 + }, 2744 + "indexes": {}, 2745 + "foreignKeys": { 2746 + "user_artists_user_id_users_xata_id_fk": { 2747 + "name": "user_artists_user_id_users_xata_id_fk", 2748 + "tableFrom": "user_artists", 2749 + "tableTo": "users", 2750 + "columnsFrom": [ 2751 + "user_id" 2752 + ], 2753 + "columnsTo": [ 2754 + "xata_id" 2755 + ], 2756 + "onDelete": "no action", 2757 + "onUpdate": "no action" 2758 + }, 2759 + "user_artists_artist_id_artists_xata_id_fk": { 2760 + "name": "user_artists_artist_id_artists_xata_id_fk", 2761 + "tableFrom": "user_artists", 2762 + "tableTo": "artists", 2763 + "columnsFrom": [ 2764 + "artist_id" 2765 + ], 2766 + "columnsTo": [ 2767 + "xata_id" 2768 + ], 2769 + "onDelete": "no action", 2770 + "onUpdate": "no action" 2771 + } 2772 + }, 2773 + "compositePrimaryKeys": {}, 2774 + "uniqueConstraints": { 2775 + "user_artists_uri_unique": { 2776 + "name": "user_artists_uri_unique", 2777 + "nullsNotDistinct": false, 2778 + "columns": [ 2779 + "uri" 2780 + ] 2781 + } 2782 + }, 2783 + "policies": {}, 2784 + "checkConstraints": {}, 2785 + "isRLSEnabled": false 2786 + }, 2787 + "public.user_playlists": { 2788 + "name": "user_playlists", 2789 + "schema": "", 2790 + "columns": { 2791 + "xata_id": { 2792 + "name": "xata_id", 2793 + "type": "text", 2794 + "primaryKey": true, 2795 + "notNull": true 2796 + }, 2797 + "user_id": { 2798 + "name": "user_id", 2799 + "type": "text", 2800 + "primaryKey": false, 2801 + "notNull": true 2802 + }, 2803 + "playlist_id": { 2804 + "name": "playlist_id", 2805 + "type": "text", 2806 + "primaryKey": false, 2807 + "notNull": true 2808 + }, 2809 + "xata_createdat": { 2810 + "name": "xata_createdat", 2811 + "type": "timestamp", 2812 + "primaryKey": false, 2813 + "notNull": true, 2814 + "default": "now()" 2815 + }, 2816 + "uri": { 2817 + "name": "uri", 2818 + "type": "text", 2819 + "primaryKey": false, 2820 + "notNull": false 2821 + } 2822 + }, 2823 + "indexes": {}, 2824 + "foreignKeys": { 2825 + "user_playlists_user_id_users_xata_id_fk": { 2826 + "name": "user_playlists_user_id_users_xata_id_fk", 2827 + "tableFrom": "user_playlists", 2828 + "tableTo": "users", 2829 + "columnsFrom": [ 2830 + "user_id" 2831 + ], 2832 + "columnsTo": [ 2833 + "xata_id" 2834 + ], 2835 + "onDelete": "no action", 2836 + "onUpdate": "no action" 2837 + }, 2838 + "user_playlists_playlist_id_playlists_xata_id_fk": { 2839 + "name": "user_playlists_playlist_id_playlists_xata_id_fk", 2840 + "tableFrom": "user_playlists", 2841 + "tableTo": "playlists", 2842 + "columnsFrom": [ 2843 + "playlist_id" 2844 + ], 2845 + "columnsTo": [ 2846 + "xata_id" 2847 + ], 2848 + "onDelete": "no action", 2849 + "onUpdate": "no action" 2850 + } 2851 + }, 2852 + "compositePrimaryKeys": {}, 2853 + "uniqueConstraints": { 2854 + "user_playlists_uri_unique": { 2855 + "name": "user_playlists_uri_unique", 2856 + "nullsNotDistinct": false, 2857 + "columns": [ 2858 + "uri" 2859 + ] 2860 + } 2861 + }, 2862 + "policies": {}, 2863 + "checkConstraints": {}, 2864 + "isRLSEnabled": false 2865 + }, 2866 + "public.user_tracks": { 2867 + "name": "user_tracks", 2868 + "schema": "", 2869 + "columns": { 2870 + "xata_id": { 2871 + "name": "xata_id", 2872 + "type": "text", 2873 + "primaryKey": true, 2874 + "notNull": true 2875 + }, 2876 + "user_id": { 2877 + "name": "user_id", 2878 + "type": "text", 2879 + "primaryKey": false, 2880 + "notNull": true 2881 + }, 2882 + "track_id": { 2883 + "name": "track_id", 2884 + "type": "text", 2885 + "primaryKey": false, 2886 + "notNull": true 2887 + }, 2888 + "xata_createdat": { 2889 + "name": "xata_createdat", 2890 + "type": "timestamp", 2891 + "primaryKey": false, 2892 + "notNull": true, 2893 + "default": "now()" 2894 + }, 2895 + "xata_updatedat": { 2896 + "name": "xata_updatedat", 2897 + "type": "timestamp", 2898 + "primaryKey": false, 2899 + "notNull": true, 2900 + "default": "now()" 2901 + }, 2902 + "xata_version": { 2903 + "name": "xata_version", 2904 + "type": "integer", 2905 + "primaryKey": false, 2906 + "notNull": false 2907 + }, 2908 + "uri": { 2909 + "name": "uri", 2910 + "type": "text", 2911 + "primaryKey": false, 2912 + "notNull": true 2913 + }, 2914 + "scrobbles": { 2915 + "name": "scrobbles", 2916 + "type": "integer", 2917 + "primaryKey": false, 2918 + "notNull": false 2919 + } 2920 + }, 2921 + "indexes": {}, 2922 + "foreignKeys": { 2923 + "user_tracks_user_id_users_xata_id_fk": { 2924 + "name": "user_tracks_user_id_users_xata_id_fk", 2925 + "tableFrom": "user_tracks", 2926 + "tableTo": "users", 2927 + "columnsFrom": [ 2928 + "user_id" 2929 + ], 2930 + "columnsTo": [ 2931 + "xata_id" 2932 + ], 2933 + "onDelete": "no action", 2934 + "onUpdate": "no action" 2935 + }, 2936 + "user_tracks_track_id_tracks_xata_id_fk": { 2937 + "name": "user_tracks_track_id_tracks_xata_id_fk", 2938 + "tableFrom": "user_tracks", 2939 + "tableTo": "tracks", 2940 + "columnsFrom": [ 2941 + "track_id" 2942 + ], 2943 + "columnsTo": [ 2944 + "xata_id" 2945 + ], 2946 + "onDelete": "no action", 2947 + "onUpdate": "no action" 2948 + } 2949 + }, 2950 + "compositePrimaryKeys": {}, 2951 + "uniqueConstraints": { 2952 + "user_tracks_uri_unique": { 2953 + "name": "user_tracks_uri_unique", 2954 + "nullsNotDistinct": false, 2955 + "columns": [ 2956 + "uri" 2957 + ] 2958 + } 2959 + }, 2960 + "policies": {}, 2961 + "checkConstraints": {}, 2962 + "isRLSEnabled": false 2963 + }, 2964 + "public.users": { 2965 + "name": "users", 2966 + "schema": "", 2967 + "columns": { 2968 + "xata_id": { 2969 + "name": "xata_id", 2970 + "type": "text", 2971 + "primaryKey": true, 2972 + "notNull": true 2973 + }, 2974 + "did": { 2975 + "name": "did", 2976 + "type": "text", 2977 + "primaryKey": false, 2978 + "notNull": true 2979 + }, 2980 + "display_name": { 2981 + "name": "display_name", 2982 + "type": "text", 2983 + "primaryKey": false, 2984 + "notNull": false 2985 + }, 2986 + "handle": { 2987 + "name": "handle", 2988 + "type": "text", 2989 + "primaryKey": false, 2990 + "notNull": true 2991 + }, 2992 + "avatar": { 2993 + "name": "avatar", 2994 + "type": "text", 2995 + "primaryKey": false, 2996 + "notNull": true 2997 + }, 2998 + "xata_createdat": { 2999 + "name": "xata_createdat", 3000 + "type": "timestamp", 3001 + "primaryKey": false, 3002 + "notNull": true, 3003 + "default": "now()" 3004 + }, 3005 + "xata_updatedat": { 3006 + "name": "xata_updatedat", 3007 + "type": "timestamp", 3008 + "primaryKey": false, 3009 + "notNull": true, 3010 + "default": "now()" 3011 + }, 3012 + "xata_version": { 3013 + "name": "xata_version", 3014 + "type": "integer", 3015 + "primaryKey": false, 3016 + "notNull": false 3017 + } 3018 + }, 3019 + "indexes": {}, 3020 + "foreignKeys": {}, 3021 + "compositePrimaryKeys": {}, 3022 + "uniqueConstraints": { 3023 + "users_did_unique": { 3024 + "name": "users_did_unique", 3025 + "nullsNotDistinct": false, 3026 + "columns": [ 3027 + "did" 3028 + ] 3029 + }, 3030 + "users_handle_unique": { 3031 + "name": "users_handle_unique", 3032 + "nullsNotDistinct": false, 3033 + "columns": [ 3034 + "handle" 3035 + ] 3036 + } 3037 + }, 3038 + "policies": {}, 3039 + "checkConstraints": {}, 3040 + "isRLSEnabled": false 3041 + }, 3042 + "public.webscrobblers": { 3043 + "name": "webscrobblers", 3044 + "schema": "", 3045 + "columns": { 3046 + "xata_id": { 3047 + "name": "xata_id", 3048 + "type": "text", 3049 + "primaryKey": true, 3050 + "notNull": true 3051 + }, 3052 + "name": { 3053 + "name": "name", 3054 + "type": "text", 3055 + "primaryKey": false, 3056 + "notNull": true 3057 + }, 3058 + "uuid": { 3059 + "name": "uuid", 3060 + "type": "text", 3061 + "primaryKey": false, 3062 + "notNull": true 3063 + }, 3064 + "description": { 3065 + "name": "description", 3066 + "type": "text", 3067 + "primaryKey": false, 3068 + "notNull": false 3069 + }, 3070 + "enabled": { 3071 + "name": "enabled", 3072 + "type": "boolean", 3073 + "primaryKey": false, 3074 + "notNull": true, 3075 + "default": true 3076 + }, 3077 + "user_id": { 3078 + "name": "user_id", 3079 + "type": "text", 3080 + "primaryKey": false, 3081 + "notNull": true 3082 + }, 3083 + "xata_createdat": { 3084 + "name": "xata_createdat", 3085 + "type": "timestamp", 3086 + "primaryKey": false, 3087 + "notNull": true, 3088 + "default": "now()" 3089 + }, 3090 + "xata_updatedat": { 3091 + "name": "xata_updatedat", 3092 + "type": "timestamp", 3093 + "primaryKey": false, 3094 + "notNull": true, 3095 + "default": "now()" 3096 + } 3097 + }, 3098 + "indexes": {}, 3099 + "foreignKeys": { 3100 + "webscrobblers_user_id_users_xata_id_fk": { 3101 + "name": "webscrobblers_user_id_users_xata_id_fk", 3102 + "tableFrom": "webscrobblers", 3103 + "tableTo": "users", 3104 + "columnsFrom": [ 3105 + "user_id" 3106 + ], 3107 + "columnsTo": [ 3108 + "xata_id" 3109 + ], 3110 + "onDelete": "no action", 3111 + "onUpdate": "no action" 3112 + } 3113 + }, 3114 + "compositePrimaryKeys": {}, 3115 + "uniqueConstraints": {}, 3116 + "policies": {}, 3117 + "checkConstraints": {}, 3118 + "isRLSEnabled": false 3119 + } 3120 + }, 3121 + "enums": {}, 3122 + "schemas": {}, 3123 + "sequences": {}, 3124 + "roles": {}, 3125 + "policies": {}, 3126 + "views": {}, 3127 + "_meta": { 3128 + "columns": {}, 3129 + "schemas": {}, 3130 + "tables": {} 3131 + } 3132 + }
+16 -2
apps/api/drizzle/meta/_journal.json
··· 5 5 { 6 6 "idx": 0, 7 7 "version": "7", 8 - "when": 1753405608015, 9 - "tag": "0000_quiet_mister_sinister", 8 + "when": 1759744201158, 9 + "tag": "0000_left_swordsman", 10 + "breakpoints": true 11 + }, 12 + { 13 + "idx": 1, 14 + "version": "7", 15 + "when": 1759746264036, 16 + "tag": "0001_fluffy_epoch", 17 + "breakpoints": true 18 + }, 19 + { 20 + "idx": 2, 21 + "version": "7", 22 + "when": 1759747596050, 23 + "tag": "0002_sweet_randall_flagg", 10 24 "breakpoints": true 11 25 } 12 26 ]
+1 -1
apps/api/src/schema/album-tracks.ts
··· 13 13 .references(() => tracks.id), 14 14 createdAt: timestamp("xata_createdat").defaultNow().notNull(), 15 15 updatedAt: timestamp("xata_updatedat").defaultNow().notNull(), 16 - xataVersion: integer("xata_version").notNull(), 16 + xataVersion: integer("xata_version"), 17 17 }); 18 18 19 19 export type SelectAlbumTrack = InferSelectModel<typeof albumTracks>;
+1 -1
apps/api/src/schema/artist-albums.ts
··· 13 13 .references(() => albums.id), 14 14 createdAt: timestamp("xata_createdat").defaultNow().notNull(), 15 15 updatedAt: timestamp("xata_updatedat").defaultNow().notNull(), 16 - xataVersion: integer("xata_version").notNull(), 16 + xataVersion: integer("xata_version"), 17 17 }); 18 18 19 19 export type SelectArtistAlbum = InferSelectModel<typeof artistAlbums>;
+1 -1
apps/api/src/schema/artist-tracks.ts
··· 13 13 .references(() => tracks.id), 14 14 createdAt: timestamp("xata_createdat").defaultNow().notNull(), 15 15 updatedAt: timestamp("xata_updatedat").defaultNow().notNull(), 16 - xataVersion: integer("xata_version").notNull(), 16 + xataVersion: integer("xata_version"), 17 17 }); 18 18 19 19 export type SelectArtistTrack = InferSelectModel<typeof artistTracks>;
+1 -1
apps/api/src/schema/dropbox-accounts.ts
··· 9 9 userId: text("user_id") 10 10 .notNull() 11 11 .references(() => users.id), 12 - xataVersion: text("xata_version").notNull(), 12 + xataVersion: text("xata_version"), 13 13 createdAt: timestamp("xata_createdat").defaultNow().notNull(), 14 14 updatedAt: timestamp("xata_updatedat").defaultNow().notNull(), 15 15 });
+1 -1
apps/api/src/schema/dropbox-directories.ts
··· 8 8 parentId: text("parent_id").references(() => dropboxDirectories.id), 9 9 dropboxId: text("dropbox_id").notNull(), 10 10 fileId: text("file_id").notNull().unique(), 11 - xataVersion: text("xata_version").notNull(), 11 + xataVersion: text("xata_version"), 12 12 createdAt: timestamp("xata_createdat").defaultNow().notNull(), 13 13 updatedAt: timestamp("xata_updatedat").defaultNow().notNull(), 14 14 });
+1 -1
apps/api/src/schema/dropbox-paths.ts
··· 10 10 trackId: text("track_id").notNull(), 11 11 directoryId: text("directory_id").references(() => dropboxDirectories.id), 12 12 fileId: text("file_id").notNull().unique(), 13 - xataVersion: text("xata_version").notNull(), 13 + xataVersion: text("xata_version"), 14 14 createdAt: timestamp("xata_createdat").defaultNow().notNull(), 15 15 updatedAt: timestamp("xata_updatedat").defaultNow().notNull(), 16 16 });
+1 -1
apps/api/src/schema/dropbox.ts
··· 11 11 dropboxTokenId: text("dropbox_token_id") 12 12 .notNull() 13 13 .references(() => dropboxTokens.id), 14 - xataVersion: text("xata_version").notNull(), 14 + xataVersion: text("xata_version"), 15 15 createdAt: timestamp("xata_createdat").defaultNow().notNull(), 16 16 updatedAt: timestamp("xata_updatedat").defaultNow().notNull(), 17 17 });
+1 -1
apps/api/src/schema/google-drive-accounts.ts
··· 9 9 userId: text("user_id") 10 10 .notNull() 11 11 .references(() => users.id), 12 - xataVersion: text("xata_version").notNull(), 12 + xataVersion: text("xata_version"), 13 13 createdAt: timestamp("xata_createdat").defaultNow().notNull(), 14 14 updatedAt: timestamp("xata_updatedat").defaultNow().notNull(), 15 15 });
+1 -1
apps/api/src/schema/google-drive-directories.ts
··· 8 8 parentId: text("parent_id").references(() => googleDriveDirectories.id), 9 9 googleDriveId: text("google_drive_id").notNull(), 10 10 fileId: text("file_id").notNull().unique(), 11 - xataVersion: text("xata_version").notNull(), 11 + xataVersion: text("xata_version"), 12 12 createdAt: timestamp("xata_createdat").defaultNow().notNull(), 13 13 updatedAt: timestamp("xata_updatedat").defaultNow().notNull(), 14 14 });
+1 -1
apps/api/src/schema/google-drive-paths.ts
··· 9 9 name: text("name").notNull(), 10 10 directoryId: text("directory_id").references(() => googleDriveDirectories.id), 11 11 fileId: text("file_id").notNull().unique(), 12 - xataVersion: text("xata_version").notNull(), 12 + xataVersion: text("xata_version"), 13 13 createdAt: timestamp("xata_createdat").defaultNow().notNull(), 14 14 updatedAt: timestamp("xata_updatedat").defaultNow().notNull(), 15 15 });
+1 -1
apps/api/src/schema/googledrive.ts
··· 11 11 userId: text("user_id") 12 12 .notNull() 13 13 .references(() => users.id), 14 - xataVersion: text("xata_version").notNull(), 14 + xataVersion: text("xata_version"), 15 15 createdAt: timestamp("xata_createdat").defaultNow().notNull(), 16 16 updatedAt: timestamp("xata_updatedat").defaultNow().notNull(), 17 17 });
+1 -1
apps/api/src/schema/spotify-accounts.ts
··· 10 10 11 11 const spotifyAccounts = pgTable("spotify_accounts", { 12 12 id: text("xata_id").primaryKey(), 13 - xataVersion: integer("xata_version").notNull(), 13 + xataVersion: integer("xata_version"), 14 14 email: text("email").notNull(), 15 15 userId: text("user_id") 16 16 .notNull()
+1 -1
apps/api/src/schema/spotify-tokens.ts
··· 4 4 5 5 const spotifyTokens = pgTable("spotify_tokens", { 6 6 id: text("xata_id").primaryKey(), 7 - xataVersion: integer("xata_version").notNull(), 7 + xataVersion: integer("xata_version"), 8 8 accessToken: text("access_token").notNull(), 9 9 refreshToken: text("refresh_token").notNull(), 10 10 userId: text("user_id")
+2 -2
apps/api/src/schema/tracks.ts
··· 23 23 label: text("label"), 24 24 copyrightMessage: text("copyright_message"), 25 25 uri: text("uri").unique(), 26 - albumUri: text("album_uri").unique(), 27 - artistUri: text("artist_uri").unique(), 26 + albumUri: text("album_uri"), 27 + artistUri: text("artist_uri"), 28 28 createdAt: timestamp("xata_createdat").defaultNow().notNull(), 29 29 updatedAt: timestamp("xata_updatedat").defaultNow().notNull(), 30 30 xataVersion: integer("xata_version"),
+1 -1
apps/api/src/schema/user-albums.ts
··· 13 13 .references(() => albums.id), 14 14 createdAt: timestamp("xata_createdat").defaultNow().notNull(), 15 15 updatedAt: timestamp("xata_updatedat").defaultNow().notNull(), 16 - xataVersion: integer("xata_version").notNull(), 16 + xataVersion: integer("xata_version"), 17 17 scrobbles: integer("scrobbles"), 18 18 uri: text("uri").unique().notNull(), 19 19 });
+1 -1
apps/api/src/schema/user-artists.ts
··· 13 13 .references(() => artists.id), 14 14 createdAt: timestamp("xata_createdat").defaultNow().notNull(), 15 15 updatedAt: timestamp("xata_updatedat").defaultNow().notNull(), 16 - xataVersion: integer("xata_version").notNull(), 16 + xataVersion: integer("xata_version"), 17 17 scrobbles: integer("scrobbles"), 18 18 uri: text("uri").unique().notNull(), 19 19 });
+3 -3
apps/api/src/schema/user-playlists.ts
··· 1 1 import type { InferInsertModel, InferSelectModel } from "drizzle-orm"; 2 2 import { pgTable, text, timestamp } from "drizzle-orm/pg-core"; 3 - import tracks from "./tracks"; 3 + import playlists from "./playlists"; 4 4 import users from "./users"; 5 5 6 6 const userPlaylists = pgTable("user_playlists", { ··· 10 10 .references(() => users.id), 11 11 playlistId: text("playlist_id") 12 12 .notNull() 13 - .references(() => tracks.id), 13 + .references(() => playlists.id), 14 14 createdAt: timestamp("xata_createdat").defaultNow().notNull(), 15 - uri: text("uri").unique().notNull(), 15 + uri: text("uri").unique(), 16 16 }); 17 17 18 18 export type SelectUserPlaylist = InferSelectModel<typeof userPlaylists>;
+1 -1
apps/api/src/schema/user-tracks.ts
··· 13 13 .references(() => tracks.id), 14 14 createdAt: timestamp("xata_createdat").defaultNow().notNull(), 15 15 updatedAt: timestamp("xata_updatedat").defaultNow().notNull(), 16 - xataVersion: integer("xata_version").notNull(), 16 + xataVersion: integer("xata_version"), 17 17 uri: text("uri").unique().notNull(), 18 18 scrobbles: integer("scrobbles"), 19 19 });
+1 -1
apps/api/src/schema/users.ts
··· 4 4 const users = pgTable("users", { 5 5 id: text("xata_id").primaryKey(), 6 6 did: text("did").unique().notNull(), 7 - displayName: text("display_name").notNull(), 7 + displayName: text("display_name"), 8 8 handle: text("handle").unique().notNull(), 9 9 avatar: text("avatar").notNull(), 10 10 createdAt: timestamp("xata_createdat").defaultNow().notNull(),
+24
crates/pgpull/Cargo.toml
··· 1 + [package] 2 + name = "rocksky-pgpull" 3 + version = "0.1.0" 4 + authors.workspace = true 5 + edition.workspace = true 6 + license.workspace = true 7 + repository.workspace = true 8 + 9 + [dependencies] 10 + serde = { version = "1.0.217", features = ["derive"] } 11 + serde_json = "1.0.139" 12 + sqlx = { version = "0.8.3", features = [ 13 + "runtime-tokio", 14 + "tls-rustls", 15 + "postgres", 16 + "chrono", 17 + "derive", 18 + "macros", 19 + ] } 20 + tokio = { version = "1.43.0", features = ["full"] } 21 + chrono = { version = "= 0.4.39", features = ["serde"] } 22 + owo-colors = "4.1.0" 23 + anyhow = "1.0.96" 24 + tracing = "0.1.41"
+698
crates/pgpull/src/lib.rs
··· 1 + use std::env; 2 + 3 + mod repo; 4 + mod xata; 5 + 6 + use anyhow::{Context, Error}; 7 + use owo_colors::OwoColorize; 8 + use sqlx::{postgres::PgPoolOptions, PgPool}; 9 + 10 + const MAX_CONNECTIONS: u32 = 5; 11 + const BATCH_SIZE: usize = 1000; 12 + const BACKUP_URL: &str = "https://backup.rocksky.app/rocksky-backup.sql"; 13 + const BACKUP_PATH: &str = "/tmp/rocksky-backup.sql"; 14 + #[derive(Clone)] 15 + pub struct DatabasePools { 16 + pub source: PgPool, 17 + pub destination: PgPool, 18 + } 19 + 20 + pub async fn pull_data() -> Result<(), Error> { 21 + if env::var("SOURCE_POSTGRES_URL").is_err() { 22 + tracing::info!( 23 + backup = %BACKUP_URL.magenta(), 24 + "SOURCE_POSTGRES_URL not set, downloading backup from Rocksky" 25 + ); 26 + download_backup().await?; 27 + return Ok(()); 28 + } 29 + 30 + let pools = setup_database_pools().await?; 31 + 32 + // Sync core entities first 33 + let album_sync = tokio::spawn({ 34 + let pools = pools.clone(); 35 + async move { sync_albums(&pools).await } 36 + }); 37 + 38 + let artist_sync = tokio::spawn({ 39 + let pools = pools.clone(); 40 + async move { sync_artists(&pools).await } 41 + }); 42 + 43 + let track_sync = tokio::spawn({ 44 + let pools = pools.clone(); 45 + async move { sync_tracks(&pools).await } 46 + }); 47 + 48 + let user_sync = tokio::spawn({ 49 + let pools = pools.clone(); 50 + async move { sync_users(&pools).await } 51 + }); 52 + 53 + let (album_sync, artist_sync, track_sync, user_sync) = 54 + tokio::join!(album_sync, artist_sync, track_sync, user_sync); 55 + 56 + album_sync.context("Album sync task failed")??; 57 + artist_sync.context("Artist sync task failed")??; 58 + track_sync.context("Track sync task failed")??; 59 + user_sync.context("User sync task failed")??; 60 + 61 + // Sync relationship entities 62 + let playlist_sync = tokio::spawn({ 63 + let pools = pools.clone(); 64 + async move { sync_playlists(&pools).await } 65 + }); 66 + 67 + let loved_track_sync = tokio::spawn({ 68 + let pools = pools.clone(); 69 + async move { sync_loved_tracks(&pools).await } 70 + }); 71 + 72 + let scrobble_sync = tokio::spawn({ 73 + let pools = pools.clone(); 74 + async move { sync_scrobbles(&pools).await } 75 + }); 76 + 77 + let (loved_track_sync, playlist_sync, scrobble_sync) = 78 + tokio::join!(loved_track_sync, playlist_sync, scrobble_sync); 79 + loved_track_sync.context("Loved track sync task failed")??; 80 + playlist_sync.context("Playlist sync task failed")??; 81 + scrobble_sync.context("Scrobble sync task failed")??; 82 + 83 + // Sync junction tables 84 + let album_track_sync = tokio::spawn({ 85 + let pools = pools.clone(); 86 + async move { sync_album_tracks(&pools).await } 87 + }); 88 + 89 + let artist_album_sync = tokio::spawn({ 90 + let pools = pools.clone(); 91 + async move { sync_artist_albums(&pools).await } 92 + }); 93 + 94 + let artist_track_sync = tokio::spawn({ 95 + let pools = pools.clone(); 96 + async move { sync_artist_tracks(&pools).await } 97 + }); 98 + 99 + let playlist_track_sync = tokio::spawn({ 100 + let pools = pools.clone(); 101 + async move { sync_playlist_tracks(&pools).await } 102 + }); 103 + 104 + let user_album_sync = tokio::spawn({ 105 + let pools = pools.clone(); 106 + async move { sync_user_albums(&pools).await } 107 + }); 108 + 109 + let user_artist_sync = tokio::spawn({ 110 + let pools = pools.clone(); 111 + async move { sync_user_artists(&pools).await } 112 + }); 113 + 114 + let user_track_sync = tokio::spawn({ 115 + let pools = pools.clone(); 116 + async move { sync_user_tracks(&pools).await } 117 + }); 118 + 119 + let user_playlist_sync = tokio::spawn({ 120 + let pools = pools.clone(); 121 + async move { sync_user_playlists(&pools).await } 122 + }); 123 + 124 + let ( 125 + album_track_sync, 126 + artist_album_sync, 127 + artist_track_sync, 128 + playlist_track_sync, 129 + user_album_sync, 130 + user_artist_sync, 131 + user_track_sync, 132 + user_playlist_sync, 133 + ) = tokio::join!( 134 + album_track_sync, 135 + artist_album_sync, 136 + artist_track_sync, 137 + playlist_track_sync, 138 + user_album_sync, 139 + user_artist_sync, 140 + user_track_sync, 141 + user_playlist_sync 142 + ); 143 + 144 + album_track_sync.context("Album track sync task failed")??; 145 + artist_album_sync.context("Artist album sync task failed")??; 146 + artist_track_sync.context("Artist track sync task failed")??; 147 + playlist_track_sync.context("Playlist track sync task failed")??; 148 + user_album_sync.context("User album sync task failed")??; 149 + user_artist_sync.context("User artist sync task failed")??; 150 + user_track_sync.context("User track sync task failed")??; 151 + user_playlist_sync.context("User playlist sync task failed")??; 152 + 153 + Ok(()) 154 + } 155 + 156 + async fn download_backup() -> Result<(), Error> { 157 + let _ = tokio::fs::remove_file(BACKUP_PATH).await; 158 + 159 + tokio::process::Command::new("curl") 160 + .arg("-o") 161 + .arg(BACKUP_PATH) 162 + .arg(BACKUP_URL) 163 + .stderr(std::process::Stdio::inherit()) 164 + .stdout(std::process::Stdio::inherit()) 165 + .spawn()? 166 + .wait() 167 + .await?; 168 + 169 + tokio::process::Command::new("psql") 170 + .arg(&env::var("XATA_POSTGRES_URL")?) 171 + .arg("-f") 172 + .arg(BACKUP_PATH) 173 + .stderr(std::process::Stdio::inherit()) 174 + .stdout(std::process::Stdio::inherit()) 175 + .spawn()? 176 + .wait() 177 + .await?; 178 + Ok(()) 179 + } 180 + 181 + async fn setup_database_pools() -> Result<DatabasePools, Error> { 182 + let source = PgPoolOptions::new() 183 + .max_connections(MAX_CONNECTIONS) 184 + .connect(&env::var("SOURCE_POSTGRES_URL")?) 185 + .await?; 186 + 187 + let destination = PgPoolOptions::new() 188 + .max_connections(MAX_CONNECTIONS) 189 + .connect(&env::var("XATA_POSTGRES_URL")?) 190 + .await?; 191 + 192 + Ok(DatabasePools { 193 + source, 194 + destination, 195 + }) 196 + } 197 + 198 + async fn sync_albums(pools: &DatabasePools) -> Result<(), Error> { 199 + let total_albums: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM albums") 200 + .fetch_one(&pools.source) 201 + .await?; 202 + let total_albums = total_albums.0; 203 + tracing::info!(total = %total_albums.magenta(), "Total albums to sync"); 204 + 205 + let start = 0; 206 + let mut i = 1; 207 + 208 + for offset in (start..total_albums).step_by(BATCH_SIZE) { 209 + let albums = 210 + repo::album::get_albums(&pools.source, offset as i64, BATCH_SIZE as i64).await?; 211 + tracing::info!( 212 + offset = %offset.magenta(), 213 + end = %((offset + albums.len() as i64).min(total_albums)).magenta(), 214 + total = %total_albums.magenta(), 215 + "Fetched albums" 216 + ); 217 + for album in &albums { 218 + tracing::info!(title = %album.title.cyan(), i = %i.magenta(), total = %total_albums.magenta(), "Inserting album"); 219 + repo::album::insert_album(&pools.destination, album).await?; 220 + i += 1; 221 + } 222 + } 223 + Ok(()) 224 + } 225 + 226 + async fn sync_artists(pools: &DatabasePools) -> Result<(), Error> { 227 + let total_artists: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM artists") 228 + .fetch_one(&pools.source) 229 + .await?; 230 + let total_artists = total_artists.0; 231 + tracing::info!(total = %total_artists.magenta(), "Total artists to sync"); 232 + 233 + let start = 0; 234 + let mut i = 1; 235 + 236 + for offset in (start..total_artists).step_by(BATCH_SIZE) { 237 + let artists = 238 + repo::artist::get_artists(&pools.source, offset as i64, BATCH_SIZE as i64).await?; 239 + tracing::info!( 240 + offset = %offset.magenta(), 241 + end = %((offset + artists.len() as i64).min(total_artists)).magenta(), 242 + total = %total_artists.magenta(), 243 + "Fetched artists" 244 + ); 245 + for artist in &artists { 246 + tracing::info!(name = %artist.name.cyan(), i = %i.magenta(), total = %total_artists.magenta(), "Inserting artist"); 247 + repo::artist::insert_artist(&pools.destination, artist).await?; 248 + i += 1; 249 + } 250 + } 251 + Ok(()) 252 + } 253 + 254 + async fn sync_tracks(pools: &DatabasePools) -> Result<(), Error> { 255 + let total_tracks: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM tracks") 256 + .fetch_one(&pools.source) 257 + .await?; 258 + let total_tracks = total_tracks.0; 259 + tracing::info!(total = %total_tracks.magenta(), "Total tracks to sync"); 260 + 261 + let start = 0; 262 + let mut i = 1; 263 + 264 + for offset in (start..total_tracks).step_by(BATCH_SIZE) { 265 + let tracks = 266 + repo::track::get_tracks(&pools.source, offset as i64, BATCH_SIZE as i64).await?; 267 + tracing::info!( 268 + offset = %offset.magenta(), 269 + end = %((offset + tracks.len() as i64).min(total_tracks)).magenta(), 270 + total = %total_tracks.magenta(), 271 + "Fetched tracks" 272 + ); 273 + 274 + for track in &tracks { 275 + tracing::info!(title = %track.title.cyan(), i = %i.magenta(), total = %total_tracks.magenta(), "Inserting track"); 276 + match repo::track::insert_track(&pools.destination, track).await { 277 + Ok(_) => {} 278 + Err(e) => { 279 + tracing::error!(error = %e, "Failed to insert track"); 280 + } 281 + } 282 + i += 1; 283 + } 284 + } 285 + Ok(()) 286 + } 287 + 288 + async fn sync_users(pools: &DatabasePools) -> Result<(), Error> { 289 + let total_users: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM users") 290 + .fetch_one(&pools.source) 291 + .await?; 292 + let total_users = total_users.0; 293 + tracing::info!(total = %total_users.magenta(), "Total users to sync"); 294 + 295 + let start = 0; 296 + let mut i = 1; 297 + 298 + for offset in (start..total_users).step_by(BATCH_SIZE) { 299 + let users = repo::user::get_users(&pools.source, offset as i64, BATCH_SIZE as i64).await?; 300 + tracing::info!( 301 + offset = %offset.magenta(), 302 + end = %((offset + users.len() as i64).min(total_users)).magenta(), 303 + total = %total_users.magenta(), 304 + "Fetched users" 305 + ); 306 + 307 + for user in &users { 308 + tracing::info!(handle = %user.handle.cyan(), i = %i.magenta(), total = %total_users.magenta(), "Inserting user"); 309 + match repo::user::insert_user(&pools.destination, user).await { 310 + Ok(_) => {} 311 + Err(e) => { 312 + tracing::error!(error = %e, "Failed to insert user"); 313 + } 314 + } 315 + i += 1; 316 + } 317 + } 318 + Ok(()) 319 + } 320 + 321 + async fn sync_playlists(pools: &DatabasePools) -> Result<(), Error> { 322 + let total_playlists: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM playlists") 323 + .fetch_one(&pools.source) 324 + .await?; 325 + let total_playlists = total_playlists.0; 326 + tracing::info!(total = %total_playlists.magenta(), "Total playlists to sync"); 327 + 328 + let start = 0; 329 + let mut i = 1; 330 + 331 + for offset in (start..total_playlists).step_by(BATCH_SIZE) { 332 + let playlists = 333 + repo::playlist::get_playlists(&pools.source, offset as i64, BATCH_SIZE as i64).await?; 334 + tracing::info!( 335 + offset = %offset.magenta(), 336 + end = %((offset + playlists.len() as i64).min(total_playlists)).magenta(), 337 + total = %total_playlists.magenta(), 338 + "Fetched playlists" 339 + ); 340 + 341 + for playlist in &playlists { 342 + tracing::info!(name = %playlist.name.cyan(), i = %i.magenta(), total = %total_playlists.magenta(), "Inserting playlist"); 343 + match repo::playlist::insert_playlist(&pools.destination, playlist).await { 344 + Ok(_) => {} 345 + Err(e) => { 346 + tracing::error!(error = %e, "Failed to insert playlist"); 347 + } 348 + } 349 + i += 1; 350 + } 351 + } 352 + Ok(()) 353 + } 354 + 355 + async fn sync_loved_tracks(pools: &DatabasePools) -> Result<(), Error> { 356 + let total_loved_tracks: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM loved_tracks") 357 + .fetch_one(&pools.source) 358 + .await?; 359 + let total_loved_tracks = total_loved_tracks.0; 360 + tracing::info!(total = %total_loved_tracks.magenta(), "Total loved tracks to sync"); 361 + 362 + let start = 0; 363 + let mut i = 1; 364 + 365 + for offset in (start..total_loved_tracks).step_by(BATCH_SIZE) { 366 + let loved_tracks = 367 + repo::loved_track::get_loved_tracks(&pools.source, offset as i64, BATCH_SIZE as i64) 368 + .await?; 369 + tracing::info!( 370 + offset = %offset.magenta(), 371 + end = %((offset + loved_tracks.len() as i64).min(total_loved_tracks)).magenta(), 372 + total = %total_loved_tracks.magenta(), 373 + "Fetched loved tracks" 374 + ); 375 + 376 + for loved_track in &loved_tracks { 377 + tracing::info!(user_id = %loved_track.user_id.cyan(), track_id = %loved_track.track_id.magenta(), i = %i.magenta(), total = %total_loved_tracks.magenta(), "Inserting loved track"); 378 + match repo::loved_track::insert_loved_track(&pools.destination, loved_track).await { 379 + Ok(_) => {} 380 + Err(e) => { 381 + tracing::error!(error = %e, "Failed to insert loved track"); 382 + } 383 + } 384 + i += 1; 385 + } 386 + } 387 + Ok(()) 388 + } 389 + 390 + async fn sync_scrobbles(pools: &DatabasePools) -> Result<(), Error> { 391 + let total_scrobbles: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM scrobbles") 392 + .fetch_one(&pools.source) 393 + .await?; 394 + let total_scrobbles = total_scrobbles.0; 395 + tracing::info!(total = %total_scrobbles.magenta(), "Total scrobbles to sync"); 396 + 397 + let start = 0; 398 + let mut i = 1; 399 + 400 + for offset in (start..total_scrobbles).step_by(BATCH_SIZE) { 401 + let scrobbles = 402 + repo::scrobble::get_scrobbles(&pools.source, offset as i64, BATCH_SIZE as i64).await?; 403 + tracing::info!( 404 + offset = %offset.magenta(), 405 + end = %((offset + scrobbles.len() as i64).min(total_scrobbles)).magenta(), 406 + total = %total_scrobbles.magenta(), 407 + "Fetched scrobbles" 408 + ); 409 + 410 + for scrobble in &scrobbles { 411 + tracing::info!(user_id = %scrobble.user_id.cyan(), track_id = %scrobble.track_id.magenta(), i = %i.magenta(), total = %total_scrobbles.magenta(), "Inserting scrobble"); 412 + match repo::scrobble::insert_scrobble(&pools.destination, scrobble).await { 413 + Ok(_) => {} 414 + Err(e) => { 415 + tracing::error!(error = %e, "Failed to insert scrobble"); 416 + } 417 + } 418 + i += 1; 419 + } 420 + } 421 + Ok(()) 422 + } 423 + 424 + async fn sync_album_tracks(pools: &DatabasePools) -> Result<(), Error> { 425 + let total_album_tracks: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM album_tracks") 426 + .fetch_one(&pools.source) 427 + .await?; 428 + let total_album_tracks = total_album_tracks.0; 429 + tracing::info!(total = %total_album_tracks.magenta(), "Total album tracks to sync"); 430 + 431 + let start = 0; 432 + let mut i = 1; 433 + 434 + for offset in (start..total_album_tracks).step_by(BATCH_SIZE) { 435 + let album_tracks = 436 + repo::album::get_album_tracks(&pools.source, offset as i64, BATCH_SIZE as i64).await?; 437 + tracing::info!( 438 + offset = %offset.magenta(), 439 + end = %((offset + album_tracks.len() as i64).min(total_album_tracks)).magenta(), 440 + total = %total_album_tracks.magenta(), 441 + "Fetched album tracks" 442 + ); 443 + 444 + for album_track in &album_tracks { 445 + tracing::info!(album_id = %album_track.album_id.cyan(), track_id = %album_track.track_id.magenta(), i = %i.magenta(), total = %total_album_tracks.magenta(), "Inserting album track"); 446 + match repo::album::insert_album_track(&pools.destination, album_track).await { 447 + Ok(_) => {} 448 + Err(e) => { 449 + tracing::error!(error = %e, "Failed to insert album track"); 450 + } 451 + } 452 + i += 1; 453 + } 454 + } 455 + Ok(()) 456 + } 457 + 458 + async fn sync_artist_albums(pools: &DatabasePools) -> Result<(), Error> { 459 + let total_artist_albums: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM artist_albums") 460 + .fetch_one(&pools.source) 461 + .await?; 462 + let total_artist_albums = total_artist_albums.0; 463 + tracing::info!(total = %total_artist_albums.magenta(), "Total artist albums to sync"); 464 + 465 + let start = 0; 466 + let mut i = 1; 467 + 468 + for offset in (start..total_artist_albums).step_by(BATCH_SIZE) { 469 + let artist_albums = 470 + repo::artist::get_artist_albums(&pools.source, offset as i64, BATCH_SIZE as i64) 471 + .await?; 472 + tracing::info!( 473 + offset = %offset.magenta(), 474 + end = %((offset + artist_albums.len() as i64).min(total_artist_albums)).magenta(), 475 + total = %total_artist_albums.magenta(), 476 + "Fetched artist albums" 477 + ); 478 + 479 + for artist_album in &artist_albums { 480 + tracing::info!(artist_id = %artist_album.artist_id.cyan(), album_id = %artist_album.album_id.magenta(), i = %i.magenta(), total = %total_artist_albums.magenta(), "Inserting artist album"); 481 + match repo::artist::insert_artist_album(&pools.destination, artist_album).await { 482 + Ok(_) => {} 483 + Err(e) => { 484 + tracing::error!(error = %e, "Failed to insert artist album"); 485 + } 486 + } 487 + i += 1; 488 + } 489 + } 490 + Ok(()) 491 + } 492 + 493 + async fn sync_artist_tracks(pools: &DatabasePools) -> Result<(), Error> { 494 + let total_artist_tracks: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM artist_tracks") 495 + .fetch_one(&pools.source) 496 + .await?; 497 + let total_artist_tracks = total_artist_tracks.0; 498 + tracing::info!(total = %total_artist_tracks.magenta(), "Total artist tracks to sync"); 499 + 500 + let start = 0; 501 + let mut i = 1; 502 + 503 + for offset in (start..total_artist_tracks).step_by(BATCH_SIZE) { 504 + let artist_tracks = 505 + repo::artist::get_artist_tracks(&pools.source, offset as i64, BATCH_SIZE as i64) 506 + .await?; 507 + tracing::info!( 508 + offset = %offset.magenta(), 509 + end = %((offset + artist_tracks.len() as i64).min(total_artist_tracks)).magenta(), 510 + total = %total_artist_tracks.magenta(), 511 + "Fetched artist tracks" 512 + ); 513 + 514 + for artist_track in &artist_tracks { 515 + tracing::info!(artist_id = %artist_track.artist_id.cyan(), track_id = %artist_track.track_id.magenta(), i = %i.magenta(), total = %total_artist_tracks.magenta(), "Inserting artist track"); 516 + match repo::artist::insert_artist_track(&pools.destination, artist_track).await { 517 + Ok(_) => {} 518 + Err(e) => { 519 + tracing::error!(error = %e, "Failed to insert artist track"); 520 + } 521 + } 522 + i += 1; 523 + } 524 + } 525 + Ok(()) 526 + } 527 + 528 + async fn sync_playlist_tracks(pools: &DatabasePools) -> Result<(), Error> { 529 + let total_playlist_tracks: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM playlist_tracks") 530 + .fetch_one(&pools.source) 531 + .await?; 532 + let total_playlist_tracks = total_playlist_tracks.0; 533 + tracing::info!(total = %total_playlist_tracks.magenta(), "Total playlist tracks to sync"); 534 + 535 + let start = 0; 536 + let mut i = 1; 537 + 538 + for offset in (start..total_playlist_tracks).step_by(BATCH_SIZE) { 539 + let playlist_tracks = 540 + repo::playlist::get_playlist_tracks(&pools.source, offset as i64, BATCH_SIZE as i64) 541 + .await?; 542 + tracing::info!( 543 + offset = %offset.magenta(), 544 + end = %((offset + playlist_tracks.len() as i64).min(total_playlist_tracks)).magenta(), 545 + total = %total_playlist_tracks.magenta(), 546 + "Fetched playlist tracks" 547 + ); 548 + 549 + for playlist_track in &playlist_tracks { 550 + tracing::info!(playlist_id = %playlist_track.playlist_id.cyan(), track_id = %playlist_track.track_id.magenta(), i = %i.magenta(), total = %total_playlist_tracks.magenta(), "Inserting playlist track"); 551 + match repo::playlist::insert_playlist_track(&pools.destination, playlist_track).await { 552 + Ok(_) => {} 553 + Err(e) => { 554 + tracing::error!(error = %e, "Failed to insert playlist track"); 555 + } 556 + } 557 + i += 1; 558 + } 559 + } 560 + Ok(()) 561 + } 562 + 563 + async fn sync_user_albums(pools: &DatabasePools) -> Result<(), Error> { 564 + let total_user_albums: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM user_albums") 565 + .fetch_one(&pools.source) 566 + .await?; 567 + let total_user_albums = total_user_albums.0; 568 + tracing::info!(total = %total_user_albums.magenta(), "Total user albums to sync"); 569 + 570 + let start = 0; 571 + let mut i = 1; 572 + 573 + for offset in (start..total_user_albums).step_by(BATCH_SIZE) { 574 + let user_albums = 575 + repo::album::get_user_albums(&pools.source, offset as i64, BATCH_SIZE as i64).await?; 576 + tracing::info!( 577 + offset = %offset.magenta(), 578 + end = %((offset + user_albums.len() as i64).min(total_user_albums)).magenta(), 579 + total = %total_user_albums.magenta(), 580 + "Fetched user albums" 581 + ); 582 + 583 + for user_album in &user_albums { 584 + tracing::info!(user_id = %user_album.user_id.cyan(), album_id = %user_album.album_id.magenta(), i = %i.magenta(), total = %total_user_albums.magenta(), "Inserting user album"); 585 + match repo::album::insert_user_album(&pools.destination, user_album).await { 586 + Ok(_) => {} 587 + Err(e) => { 588 + tracing::error!(error = %e, "Failed to insert user album"); 589 + } 590 + } 591 + i += 1; 592 + } 593 + } 594 + Ok(()) 595 + } 596 + 597 + async fn sync_user_artists(pools: &DatabasePools) -> Result<(), Error> { 598 + let total_user_artists: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM user_artists") 599 + .fetch_one(&pools.source) 600 + .await?; 601 + let total_user_artists = total_user_artists.0; 602 + tracing::info!(total = %total_user_artists.magenta(), "Total user artists to sync"); 603 + 604 + let start = 0; 605 + let mut i = 1; 606 + 607 + for offset in (start..total_user_artists).step_by(BATCH_SIZE) { 608 + let user_artists = 609 + repo::artist::get_user_artists(&pools.source, offset as i64, BATCH_SIZE as i64).await?; 610 + tracing::info!( 611 + offset = %offset.magenta(), 612 + end = %((offset + user_artists.len() as i64).min(total_user_artists)).magenta(), 613 + total = %total_user_artists.magenta(), 614 + "Fetched user artists" 615 + ); 616 + 617 + for user_artist in &user_artists { 618 + tracing::info!(user_id = %user_artist.user_id.cyan(), artist_id = %user_artist.artist_id.magenta(), i = %i.magenta(), total = %total_user_artists.magenta(), "Inserting user artist"); 619 + match repo::artist::insert_user_artist(&pools.destination, user_artist).await { 620 + Ok(_) => {} 621 + Err(e) => { 622 + tracing::error!(error = %e, "Failed to insert user artist"); 623 + } 624 + } 625 + i += 1; 626 + } 627 + } 628 + Ok(()) 629 + } 630 + 631 + async fn sync_user_tracks(pools: &DatabasePools) -> Result<(), Error> { 632 + let total_user_tracks: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM user_tracks") 633 + .fetch_one(&pools.source) 634 + .await?; 635 + let total_user_tracks = total_user_tracks.0; 636 + tracing::info!(total = %total_user_tracks.magenta(), "Total user tracks to sync"); 637 + 638 + let start = 0; 639 + let mut i = 1; 640 + 641 + for offset in (start..total_user_tracks).step_by(BATCH_SIZE) { 642 + let user_tracks = 643 + repo::track::get_user_tracks(&pools.source, offset as i64, BATCH_SIZE as i64).await?; 644 + tracing::info!( 645 + offset = %offset.magenta(), 646 + end = %((offset + user_tracks.len() as i64).min(total_user_tracks)).magenta(), 647 + total = %total_user_tracks.magenta(), 648 + "Fetched user tracks" 649 + ); 650 + 651 + for user_track in &user_tracks { 652 + tracing::info!(user_id = %user_track.user_id.cyan(), track_id = %user_track.track_id.magenta(), i = %i.magenta(), total = %total_user_tracks.magenta(), "Inserting user track"); 653 + match repo::track::insert_user_track(&pools.destination, user_track).await { 654 + Ok(_) => {} 655 + Err(e) => { 656 + tracing::error!(error = %e, "Failed to insert user track"); 657 + } 658 + } 659 + i += 1; 660 + } 661 + } 662 + Ok(()) 663 + } 664 + 665 + async fn sync_user_playlists(pools: &DatabasePools) -> Result<(), Error> { 666 + let total_user_playlists: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM user_playlists") 667 + .fetch_one(&pools.source) 668 + .await?; 669 + let total_user_playlists = total_user_playlists.0; 670 + tracing::info!(total = %total_user_playlists.magenta(), "Total user playlists to sync"); 671 + 672 + let start = 0; 673 + let mut i = 1; 674 + 675 + for offset in (start..total_user_playlists).step_by(BATCH_SIZE) { 676 + let user_playlists = 677 + repo::playlist::get_user_playlists(&pools.source, offset as i64, BATCH_SIZE as i64) 678 + .await?; 679 + tracing::info!( 680 + offset = %offset.magenta(), 681 + end = %((offset + user_playlists.len() as i64).min(total_user_playlists)).magenta(), 682 + total = %total_user_playlists.magenta(), 683 + "Fetched user playlists" 684 + ); 685 + 686 + for user_playlist in &user_playlists { 687 + tracing::info!(user_id = %user_playlist.user_id.cyan(), playlist_id = %user_playlist.playlist_id.magenta(), i = %i.magenta(), total = %total_user_playlists.magenta(), "Inserting user playlist"); 688 + match repo::playlist::insert_user_playlist(&pools.destination, user_playlist).await { 689 + Ok(_) => {} 690 + Err(e) => { 691 + tracing::error!(error = %e, "Failed to insert user playlist"); 692 + } 693 + } 694 + i += 1; 695 + } 696 + } 697 + Ok(()) 698 + }
+126
crates/pgpull/src/repo/album.rs
··· 1 + use anyhow::Error; 2 + use sqlx::{Pool, Postgres}; 3 + 4 + use crate::xata::{album::Album, album_track::AlbumTrack, user_album::UserAlbum}; 5 + 6 + pub async fn get_albums( 7 + pool: &Pool<Postgres>, 8 + offset: i64, 9 + limit: i64, 10 + ) -> Result<Vec<Album>, Error> { 11 + let albums: Vec<Album> = sqlx::query_as("SELECT * FROM albums OFFSET $1 LIMIT $2") 12 + .bind(offset) 13 + .bind(limit) 14 + .fetch_all(pool) 15 + .await?; 16 + Ok(albums) 17 + } 18 + 19 + pub async fn get_album_tracks( 20 + pool: &Pool<Postgres>, 21 + offset: i64, 22 + limit: i64, 23 + ) -> Result<Vec<AlbumTrack>, Error> { 24 + let album_tracks: Vec<AlbumTrack> = 25 + sqlx::query_as("SELECT * FROM album_tracks OFFSET $1 LIMIT $2") 26 + .bind(offset) 27 + .bind(limit) 28 + .fetch_all(pool) 29 + .await?; 30 + Ok(album_tracks) 31 + } 32 + 33 + pub async fn get_user_albums( 34 + pool: &Pool<Postgres>, 35 + offset: i64, 36 + limit: i64, 37 + ) -> Result<Vec<UserAlbum>, Error> { 38 + let user_albums: Vec<UserAlbum> = 39 + sqlx::query_as("SELECT * FROM user_albums OFFSET $1 LIMIT $2") 40 + .bind(offset) 41 + .bind(limit) 42 + .fetch_all(pool) 43 + .await?; 44 + Ok(user_albums) 45 + } 46 + 47 + pub async fn insert_album(pool: &Pool<Postgres>, album: &Album) -> Result<(), Error> { 48 + sqlx::query( 49 + r#"INSERT INTO albums ( 50 + xata_id, 51 + title, 52 + artist, 53 + release_date, 54 + album_art, 55 + year, 56 + spotify_link, 57 + tidal_link, 58 + youtube_link, 59 + apple_music_link, 60 + sha256, 61 + uri, 62 + artist_uri, 63 + xata_createdat 64 + ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) 65 + ON CONFLICT (xata_id) DO NOTHING"#, 66 + ) 67 + .bind(&album.xata_id) 68 + .bind(&album.title) 69 + .bind(&album.artist) 70 + .bind(&album.release_date) 71 + .bind(&album.album_art) 72 + .bind(album.year) 73 + .bind(&album.spotify_link) 74 + .bind(&album.tidal_link) 75 + .bind(&album.youtube_link) 76 + .bind(&album.apple_music_link) 77 + .bind(&album.sha256) 78 + .bind(&album.uri) 79 + .bind(&album.artist_uri) 80 + .bind(album.xata_createdat) 81 + .execute(pool) 82 + .await?; 83 + 84 + Ok(()) 85 + } 86 + 87 + pub async fn insert_album_track( 88 + pool: &Pool<Postgres>, 89 + album_track: &AlbumTrack, 90 + ) -> Result<(), Error> { 91 + sqlx::query( 92 + r#"INSERT INTO album_tracks ( 93 + xata_id, 94 + album_id, 95 + track_id 96 + ) VALUES ($1, $2, $3) 97 + ON CONFLICT (xata_id) DO NOTHING"#, 98 + ) 99 + .bind(&album_track.xata_id) 100 + .bind(&album_track.album_id) 101 + .bind(&album_track.track_id) 102 + .execute(pool) 103 + .await?; 104 + Ok(()) 105 + } 106 + 107 + pub async fn insert_user_album(pool: &Pool<Postgres>, user_album: &UserAlbum) -> Result<(), Error> { 108 + sqlx::query( 109 + r#"INSERT INTO user_albums ( 110 + xata_id, 111 + user_id, 112 + album_id, 113 + uri, 114 + xata_createdat 115 + ) VALUES ($1, $2, $3, $4, $5) 116 + ON CONFLICT (xata_id) DO NOTHING"#, 117 + ) 118 + .bind(&user_album.xata_id) 119 + .bind(&user_album.user_id) 120 + .bind(&user_album.album_id) 121 + .bind(&user_album.uri) 122 + .bind(user_album.xata_createdat) 123 + .execute(pool) 124 + .await?; 125 + Ok(()) 126 + }
+170
crates/pgpull/src/repo/artist.rs
··· 1 + use anyhow::Error; 2 + use sqlx::{Pool, Postgres}; 3 + 4 + use crate::xata::{ 5 + artist::Artist, artist_album::ArtistAlbum, artist_track::ArtistTrack, user_artist::UserArtist, 6 + }; 7 + 8 + pub async fn get_artists( 9 + pool: &Pool<Postgres>, 10 + offset: i64, 11 + limit: i64, 12 + ) -> Result<Vec<Artist>, Error> { 13 + let artists = sqlx::query_as::<_, Artist>("SELECT * FROM artists OFFSET $1 LIMIT $2") 14 + .bind(offset) 15 + .bind(limit) 16 + .fetch_all(pool) 17 + .await?; 18 + Ok(artists) 19 + } 20 + 21 + pub async fn insert_artist(pool: &Pool<Postgres>, artist: &Artist) -> Result<(), Error> { 22 + sqlx::query( 23 + r#"INSERT INTO artists ( 24 + xata_id, 25 + name, 26 + biography, 27 + born, 28 + born_in, 29 + died, 30 + picture, 31 + sha256, 32 + spotify_link, 33 + tidal_link, 34 + youtube_link, 35 + apple_music_link, 36 + uri, 37 + genres, 38 + xata_createdat 39 + ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) 40 + ON CONFLICT (xata_id) DO NOTHING"#, 41 + ) 42 + .bind(&artist.xata_id) 43 + .bind(&artist.name) 44 + .bind(&artist.biography) 45 + .bind(&artist.born) 46 + .bind(&artist.born_in) 47 + .bind(&artist.died) 48 + .bind(&artist.picture) 49 + .bind(&artist.sha256) 50 + .bind(&artist.spotify_link) 51 + .bind(&artist.tidal_link) 52 + .bind(&artist.youtube_link) 53 + .bind(&artist.apple_music_link) 54 + .bind(&artist.uri) 55 + .bind(&artist.genres) 56 + .bind(artist.xata_createdat) 57 + .execute(pool) 58 + .await?; 59 + Ok(()) 60 + } 61 + 62 + pub async fn get_artist_albums( 63 + pool: &Pool<Postgres>, 64 + offset: i64, 65 + limit: i64, 66 + ) -> Result<Vec<ArtistAlbum>, Error> { 67 + let artist_albums = 68 + sqlx::query_as::<_, ArtistAlbum>("SELECT * FROM artist_albums OFFSET $1 LIMIT $2") 69 + .bind(offset) 70 + .bind(limit) 71 + .fetch_all(pool) 72 + .await?; 73 + Ok(artist_albums) 74 + } 75 + 76 + pub async fn get_artist_tracks( 77 + pool: &Pool<Postgres>, 78 + offset: i64, 79 + limit: i64, 80 + ) -> Result<Vec<ArtistTrack>, Error> { 81 + let artist_tracks = 82 + sqlx::query_as::<_, ArtistTrack>("SELECT * FROM artist_tracks OFFSET $1 LIMIT $2") 83 + .bind(offset) 84 + .bind(limit) 85 + .fetch_all(pool) 86 + .await?; 87 + Ok(artist_tracks) 88 + } 89 + 90 + pub async fn get_user_artists( 91 + pool: &Pool<Postgres>, 92 + offset: i64, 93 + limit: i64, 94 + ) -> Result<Vec<UserArtist>, Error> { 95 + let user_artists = 96 + sqlx::query_as::<_, UserArtist>("SELECT * FROM user_artists OFFSET $1 LIMIT $2") 97 + .bind(offset) 98 + .bind(limit) 99 + .fetch_all(pool) 100 + .await?; 101 + Ok(user_artists) 102 + } 103 + 104 + pub async fn insert_artist_album( 105 + pool: &Pool<Postgres>, 106 + artist_album: &ArtistAlbum, 107 + ) -> Result<(), Error> { 108 + sqlx::query( 109 + r#"INSERT INTO artist_albums ( 110 + xata_id, 111 + artist_id, 112 + album_id, 113 + xata_createdat 114 + ) VALUES ($1, $2, $3, $4) 115 + ON CONFLICT (xata_id) DO NOTHING"#, 116 + ) 117 + .bind(&artist_album.xata_id) 118 + .bind(&artist_album.artist_id) 119 + .bind(&artist_album.album_id) 120 + .bind(artist_album.xata_createdat) 121 + .execute(pool) 122 + .await?; 123 + Ok(()) 124 + } 125 + 126 + pub async fn insert_artist_track( 127 + pool: &Pool<Postgres>, 128 + artist_track: &ArtistTrack, 129 + ) -> Result<(), Error> { 130 + sqlx::query( 131 + r#"INSERT INTO artist_tracks ( 132 + xata_id, 133 + artist_id, 134 + track_id, 135 + xata_createdat 136 + ) VALUES ($1, $2, $3, $4) 137 + ON CONFLICT (xata_id) DO NOTHING"#, 138 + ) 139 + .bind(&artist_track.xata_id) 140 + .bind(&artist_track.artist_id) 141 + .bind(&artist_track.track_id) // Reusing album_id field for track_id 142 + .bind(artist_track.xata_createdat) 143 + .execute(pool) 144 + .await?; 145 + Ok(()) 146 + } 147 + 148 + pub async fn insert_user_artist( 149 + pool: &Pool<Postgres>, 150 + user_artist: &UserArtist, 151 + ) -> Result<(), Error> { 152 + sqlx::query( 153 + r#"INSERT INTO user_artists ( 154 + xata_id, 155 + user_id, 156 + artist_id, 157 + uri, 158 + xata_createdat 159 + ) VALUES ($1, $2, $3, $4, $5) 160 + ON CONFLICT (xata_id) DO NOTHING"#, 161 + ) 162 + .bind(&user_artist.xata_id) 163 + .bind(&user_artist.user_id) 164 + .bind(&user_artist.artist_id) 165 + .bind(&user_artist.uri) 166 + .bind(user_artist.xata_createdat) 167 + .execute(pool) 168 + .await?; 169 + Ok(()) 170 + }
+40
crates/pgpull/src/repo/loved_track.rs
··· 1 + use anyhow::Error; 2 + use sqlx::{Pool, Postgres}; 3 + 4 + use crate::xata::loved_track::LovedTrack; 5 + 6 + pub async fn get_loved_tracks( 7 + pool: &Pool<Postgres>, 8 + offset: i64, 9 + limit: i64, 10 + ) -> Result<Vec<LovedTrack>, Error> { 11 + let loved_tracks = 12 + sqlx::query_as::<_, LovedTrack>("SELECT * FROM loved_tracks OFFSET $1 LIMIT $2") 13 + .bind(offset) 14 + .bind(limit) 15 + .fetch_all(pool) 16 + .await?; 17 + Ok(loved_tracks) 18 + } 19 + 20 + pub async fn insert_loved_track( 21 + pool: &Pool<Postgres>, 22 + loved_track: &LovedTrack, 23 + ) -> Result<(), Error> { 24 + sqlx::query( 25 + r#"INSERT INTO loved_tracks ( 26 + xata_id, 27 + user_id, 28 + track_id, 29 + xata_createdat 30 + ) VALUES ($1, $2, $3, $4) 31 + ON CONFLICT (xata_id) DO NOTHING"#, 32 + ) 33 + .bind(&loved_track.xata_id) 34 + .bind(&loved_track.user_id) 35 + .bind(&loved_track.track_id) 36 + .bind(loved_track.xata_createdat) 37 + .execute(pool) 38 + .await?; 39 + Ok(()) 40 + }
+7
crates/pgpull/src/repo/mod.rs
··· 1 + pub mod album; 2 + pub mod artist; 3 + pub mod loved_track; 4 + pub mod playlist; 5 + pub mod scrobble; 6 + pub mod track; 7 + pub mod user;
+124
crates/pgpull/src/repo/playlist.rs
··· 1 + use anyhow::Error; 2 + use sqlx::{Pool, Postgres}; 3 + 4 + use crate::xata::{playlist::Playlist, playlist_track::PlaylistTrack, user_playlist::UserPlaylist}; 5 + 6 + pub async fn get_playlists( 7 + pool: &Pool<Postgres>, 8 + offset: i64, 9 + limit: i64, 10 + ) -> Result<Vec<Playlist>, Error> { 11 + let playlists: Vec<Playlist> = sqlx::query_as("SELECT * FROM playlists OFFSET $1 LIMIT $2") 12 + .bind(offset) 13 + .bind(limit) 14 + .fetch_all(pool) 15 + .await?; 16 + Ok(playlists) 17 + } 18 + 19 + pub async fn get_playlist_tracks( 20 + pool: &Pool<Postgres>, 21 + offset: i64, 22 + limit: i64, 23 + ) -> Result<Vec<PlaylistTrack>, Error> { 24 + let playlist_tracks: Vec<PlaylistTrack> = 25 + sqlx::query_as("SELECT * FROM playlist_tracks OFFSET $1 LIMIT $2") 26 + .bind(offset) 27 + .bind(limit) 28 + .fetch_all(pool) 29 + .await?; 30 + Ok(playlist_tracks) 31 + } 32 + 33 + pub async fn get_user_playlists( 34 + pool: &Pool<Postgres>, 35 + offset: i64, 36 + limit: i64, 37 + ) -> Result<Vec<UserPlaylist>, Error> { 38 + let user_playlists: Vec<UserPlaylist> = 39 + sqlx::query_as("SELECT * FROM user_playlists OFFSET $1 LIMIT $2") 40 + .bind(offset) 41 + .bind(limit) 42 + .fetch_all(pool) 43 + .await?; 44 + Ok(user_playlists) 45 + } 46 + 47 + pub async fn insert_playlist(pool: &Pool<Postgres>, playlist: &Playlist) -> Result<(), Error> { 48 + sqlx::query( 49 + r#"INSERT INTO playlists ( 50 + xata_id, 51 + name, 52 + description, 53 + picture, 54 + spotify_link, 55 + tidal_link, 56 + apple_music_link, 57 + xata_createdat, 58 + xata_updatedat, 59 + uri, 60 + created_by 61 + ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) 62 + ON CONFLICT (xata_id) DO NOTHING"#, 63 + ) 64 + .bind(&playlist.xata_id) 65 + .bind(&playlist.name) 66 + .bind(&playlist.description) 67 + .bind(&playlist.picture) 68 + .bind(&playlist.spotify_link) 69 + .bind(&playlist.tidal_link) 70 + .bind(&playlist.apple_music_link) 71 + .bind(playlist.xata_createdat) 72 + .bind(playlist.xata_updatedat) 73 + .bind(&playlist.uri) 74 + .bind(&playlist.created_by) 75 + .execute(pool) 76 + .await?; 77 + Ok(()) 78 + } 79 + 80 + pub async fn insert_playlist_track( 81 + pool: &Pool<Postgres>, 82 + playlist_track: &PlaylistTrack, 83 + ) -> Result<(), Error> { 84 + sqlx::query( 85 + r#"INSERT INTO playlist_tracks ( 86 + xata_id, 87 + playlist_id, 88 + track_id, 89 + xata_createdat 90 + ) VALUES ($1, $2, $3, $4) 91 + ON CONFLICT (xata_id) DO NOTHING"#, 92 + ) 93 + .bind(&playlist_track.xata_id) 94 + .bind(&playlist_track.playlist_id) 95 + .bind(&playlist_track.track_id) 96 + .bind(playlist_track.xata_createdat) 97 + .execute(pool) 98 + .await?; 99 + Ok(()) 100 + } 101 + 102 + pub async fn insert_user_playlist( 103 + pool: &Pool<Postgres>, 104 + user_playlist: &UserPlaylist, 105 + ) -> Result<(), Error> { 106 + sqlx::query( 107 + r#"INSERT INTO user_playlists ( 108 + xata_id, 109 + user_id, 110 + playlist_id, 111 + uri, 112 + xata_createdat 113 + ) VALUES ($1, $2, $3, $4, $5) 114 + ON CONFLICT (xata_id) DO NOTHING"#, 115 + ) 116 + .bind(&user_playlist.xata_id) 117 + .bind(&user_playlist.user_id) 118 + .bind(&user_playlist.playlist_id) 119 + .bind(&user_playlist.uri) 120 + .bind(user_playlist.xata_createdat) 121 + .execute(pool) 122 + .await?; 123 + Ok(()) 124 + }
+44
crates/pgpull/src/repo/scrobble.rs
··· 1 + use anyhow::Error; 2 + use sqlx::{Pool, Postgres}; 3 + 4 + use crate::xata::scrobble::Scrobble; 5 + 6 + pub async fn get_scrobbles( 7 + pool: &Pool<Postgres>, 8 + offset: i64, 9 + limit: i64, 10 + ) -> Result<Vec<Scrobble>, Error> { 11 + let scrobbles = sqlx::query_as::<_, Scrobble>("SELECT * FROM scrobbles OFFSET $1 LIMIT $2") 12 + .bind(offset) 13 + .bind(limit) 14 + .fetch_all(pool) 15 + .await?; 16 + Ok(scrobbles) 17 + } 18 + 19 + pub async fn insert_scrobble(pool: &Pool<Postgres>, scrobble: &Scrobble) -> Result<(), Error> { 20 + sqlx::query( 21 + r#"INSERT INTO scrobbles ( 22 + xata_id, 23 + user_id, 24 + track_id, 25 + album_id, 26 + artist_id, 27 + uri, 28 + xata_createdat, 29 + timestamp 30 + ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) 31 + ON CONFLICT (xata_id) DO NOTHING"#, 32 + ) 33 + .bind(&scrobble.xata_id) 34 + .bind(&scrobble.user_id) 35 + .bind(&scrobble.track_id) 36 + .bind(&scrobble.album_id) 37 + .bind(&scrobble.artist_id) 38 + .bind(&scrobble.uri) 39 + .bind(scrobble.xata_createdat) 40 + .bind(scrobble.timestamp) 41 + .execute(pool) 42 + .await?; 43 + Ok(()) 44 + }
+113
crates/pgpull/src/repo/track.rs
··· 1 + use anyhow::Error; 2 + use sqlx::{Pool, Postgres}; 3 + 4 + use crate::xata::{track::Track, user_track::UserTrack}; 5 + 6 + pub async fn get_tracks( 7 + pool: &Pool<Postgres>, 8 + offset: i64, 9 + limit: i64, 10 + ) -> Result<Vec<Track>, Error> { 11 + let tracks: Vec<Track> = sqlx::query_as("SELECT * FROM tracks OFFSET $1 LIMIT $2") 12 + .bind(offset) 13 + .bind(limit) 14 + .fetch_all(pool) 15 + .await?; 16 + Ok(tracks) 17 + } 18 + 19 + pub async fn get_user_tracks( 20 + pool: &Pool<Postgres>, 21 + offset: i64, 22 + limit: i64, 23 + ) -> Result<Vec<UserTrack>, Error> { 24 + let user_tracks: Vec<UserTrack> = 25 + sqlx::query_as("SELECT * FROM user_tracks OFFSET $1 LIMIT $2") 26 + .bind(offset) 27 + .bind(limit) 28 + .fetch_all(pool) 29 + .await?; 30 + Ok(user_tracks) 31 + } 32 + 33 + pub async fn insert_track(pool: &Pool<Postgres>, track: &Track) -> Result<(), Error> { 34 + sqlx::query( 35 + r#"INSERT INTO tracks ( 36 + xata_id, 37 + title, 38 + artist, 39 + album_artist, 40 + album_art, 41 + album, 42 + track_number, 43 + duration, 44 + mb_id, 45 + youtube_link, 46 + spotify_link, 47 + tidal_link, 48 + apple_music_link, 49 + sha256, 50 + lyrics, 51 + composer, 52 + genre, 53 + disc_number, 54 + copyright_message, 55 + label, 56 + uri, 57 + artist_uri, 58 + album_uri, 59 + xata_createdat 60 + ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24) 61 + ON CONFLICT (xata_id) DO NOTHING 62 + "#, 63 + ) 64 + .bind(&track.xata_id) 65 + .bind(&track.title) 66 + .bind(&track.artist) 67 + .bind(&track.album_artist) 68 + .bind(&track.album_art) 69 + .bind(&track.album) 70 + .bind(track.track_number) 71 + .bind(track.duration) 72 + .bind(&track.mb_id) 73 + .bind(&track.youtube_link) 74 + .bind(&track.spotify_link) 75 + .bind(&track.tidal_link) 76 + .bind(&track.apple_music_link) 77 + .bind(&track.sha256) 78 + .bind(&track.lyrics) 79 + .bind(&track.composer) 80 + .bind(&track.genre) 81 + .bind(track.disc_number) 82 + .bind(&track.copyright_message) 83 + .bind(&track.label) 84 + .bind(&track.uri) 85 + .bind(&track.artist_uri) 86 + .bind(&track.album_uri) 87 + .bind(track.xata_createdat) 88 + .execute(pool) 89 + .await?; 90 + Ok(()) 91 + } 92 + 93 + pub async fn insert_user_track(pool: &Pool<Postgres>, user_track: &UserTrack) -> Result<(), Error> { 94 + sqlx::query( 95 + r#"INSERT INTO user_tracks ( 96 + xata_id, 97 + user_id, 98 + track_id, 99 + uri, 100 + xata_createdat 101 + ) VALUES ($1, $2, $3, $4, $5) 102 + ON CONFLICT (xata_id) DO NOTHING 103 + "#, 104 + ) 105 + .bind(&user_track.xata_id) 106 + .bind(&user_track.user_id) 107 + .bind(&user_track.track_id) 108 + .bind(&user_track.uri) 109 + .bind(user_track.xata_createdat) 110 + .execute(pool) 111 + .await?; 112 + Ok(()) 113 + }
+36
crates/pgpull/src/repo/user.rs
··· 1 + use anyhow::Error; 2 + use sqlx::{Pool, Postgres}; 3 + 4 + use crate::xata::user::User; 5 + 6 + pub async fn get_users(pool: &Pool<Postgres>, offset: i64, limit: i64) -> Result<Vec<User>, Error> { 7 + let users = sqlx::query_as::<_, User>("SELECT * FROM users OFFSET $1 LIMIT $2") 8 + .bind(offset) 9 + .bind(limit) 10 + .fetch_all(pool) 11 + .await?; 12 + Ok(users) 13 + } 14 + 15 + pub async fn insert_user(pool: &Pool<Postgres>, user: &User) -> Result<(), Error> { 16 + sqlx::query( 17 + r#"INSERT INTO users ( 18 + xata_id, 19 + display_name, 20 + did, 21 + handle, 22 + avatar, 23 + xata_createdat 24 + ) VALUES ($1, $2, $3, $4, $5, $6) 25 + ON CONFLICT (xata_id) DO NOTHING"#, 26 + ) 27 + .bind(&user.xata_id) 28 + .bind(&user.display_name) 29 + .bind(&user.did) 30 + .bind(&user.handle) 31 + .bind(&user.avatar) 32 + .bind(user.xata_createdat) 33 + .execute(pool) 34 + .await?; 35 + Ok(()) 36 + }
+21
crates/pgpull/src/xata/album.rs
··· 1 + use chrono::{DateTime, Utc}; 2 + use serde::Deserialize; 3 + 4 + #[derive(Debug, sqlx::FromRow, Deserialize, Clone)] 5 + pub struct Album { 6 + pub xata_id: String, 7 + pub title: String, 8 + pub artist: String, 9 + pub release_date: Option<String>, 10 + pub album_art: Option<String>, 11 + pub year: Option<i32>, 12 + pub spotify_link: Option<String>, 13 + pub tidal_link: Option<String>, 14 + pub youtube_link: Option<String>, 15 + pub apple_music_link: Option<String>, 16 + pub sha256: String, 17 + pub uri: Option<String>, 18 + pub artist_uri: Option<String>, 19 + #[serde(with = "chrono::serde::ts_seconds")] 20 + pub xata_createdat: DateTime<Utc>, 21 + }
+8
crates/pgpull/src/xata/album_track.rs
··· 1 + use serde::Deserialize; 2 + 3 + #[derive(Debug, sqlx::FromRow, Deserialize, Clone)] 4 + pub struct AlbumTrack { 5 + pub xata_id: String, 6 + pub album_id: String, 7 + pub track_id: String, 8 + }
+24
crates/pgpull/src/xata/artist.rs
··· 1 + use chrono::{DateTime, Utc}; 2 + use serde::Deserialize; 3 + 4 + #[derive(Debug, sqlx::FromRow, Deserialize, Clone)] 5 + pub struct Artist { 6 + pub xata_id: String, 7 + pub name: String, 8 + pub biography: Option<String>, 9 + #[serde(with = "chrono::serde::ts_seconds_option")] 10 + pub born: Option<DateTime<Utc>>, 11 + pub born_in: Option<String>, 12 + #[serde(with = "chrono::serde::ts_seconds_option")] 13 + pub died: Option<DateTime<Utc>>, 14 + pub picture: Option<String>, 15 + pub sha256: String, 16 + pub spotify_link: Option<String>, 17 + pub tidal_link: Option<String>, 18 + pub youtube_link: Option<String>, 19 + pub apple_music_link: Option<String>, 20 + pub uri: Option<String>, 21 + pub genres: Option<Vec<String>>, 22 + #[serde(with = "chrono::serde::ts_seconds")] 23 + pub xata_createdat: DateTime<Utc>, 24 + }
+10
crates/pgpull/src/xata/artist_album.rs
··· 1 + use serde::Deserialize; 2 + 3 + #[derive(Debug, sqlx::FromRow, Deserialize, Clone)] 4 + pub struct ArtistAlbum { 5 + pub xata_id: String, 6 + pub artist_id: String, 7 + pub album_id: String, 8 + #[serde(with = "chrono::serde::ts_seconds")] 9 + pub xata_createdat: chrono::DateTime<chrono::Utc>, 10 + }
+10
crates/pgpull/src/xata/artist_track.rs
··· 1 + use serde::Deserialize; 2 + 3 + #[derive(Debug, sqlx::FromRow, Deserialize, Clone)] 4 + pub struct ArtistTrack { 5 + pub xata_id: String, 6 + pub artist_id: String, 7 + pub track_id: String, 8 + #[serde(with = "chrono::serde::ts_seconds")] 9 + pub xata_createdat: chrono::DateTime<chrono::Utc>, 10 + }
+10
crates/pgpull/src/xata/loved_track.rs
··· 1 + use serde::Deserialize; 2 + 3 + #[derive(Debug, sqlx::FromRow, Deserialize, Clone)] 4 + pub struct LovedTrack { 5 + pub xata_id: String, 6 + pub user_id: String, 7 + pub track_id: String, 8 + #[serde(with = "chrono::serde::ts_seconds")] 9 + pub xata_createdat: chrono::DateTime<chrono::Utc>, 10 + }
+15
crates/pgpull/src/xata/mod.rs
··· 1 + pub mod album; 2 + pub mod album_track; 3 + pub mod artist; 4 + pub mod artist_album; 5 + pub mod artist_track; 6 + pub mod loved_track; 7 + pub mod playlist; 8 + pub mod playlist_track; 9 + pub mod scrobble; 10 + pub mod track; 11 + pub mod user; 12 + pub mod user_album; 13 + pub mod user_artist; 14 + pub mod user_playlist; 15 + pub mod user_track;
+19
crates/pgpull/src/xata/playlist.rs
··· 1 + use chrono::{DateTime, Utc}; 2 + use serde::Deserialize; 3 + 4 + #[derive(Debug, sqlx::FromRow, Deserialize, Clone)] 5 + pub struct Playlist { 6 + pub xata_id: String, 7 + pub name: String, 8 + pub description: Option<String>, 9 + pub picture: Option<String>, 10 + pub spotify_link: Option<String>, 11 + pub tidal_link: Option<String>, 12 + pub apple_music_link: Option<String>, 13 + #[serde(with = "chrono::serde::ts_seconds")] 14 + pub xata_createdat: DateTime<Utc>, 15 + #[serde(with = "chrono::serde::ts_seconds")] 16 + pub xata_updatedat: DateTime<Utc>, 17 + pub uri: Option<String>, 18 + pub created_by: String, 19 + }
+10
crates/pgpull/src/xata/playlist_track.rs
··· 1 + use serde::Deserialize; 2 + 3 + #[derive(Debug, sqlx::FromRow, Deserialize, Clone)] 4 + pub struct PlaylistTrack { 5 + pub xata_id: String, 6 + pub playlist_id: String, 7 + pub track_id: String, 8 + #[serde(with = "chrono::serde::ts_seconds")] 9 + pub xata_createdat: chrono::DateTime<chrono::Utc>, 10 + }
+16
crates/pgpull/src/xata/scrobble.rs
··· 1 + use chrono::{DateTime, Utc}; 2 + use serde::Deserialize; 3 + 4 + #[derive(Debug, sqlx::FromRow, Deserialize, Clone)] 5 + pub struct Scrobble { 6 + pub xata_id: String, 7 + pub user_id: String, 8 + pub track_id: String, 9 + pub album_id: Option<String>, 10 + pub artist_id: Option<String>, 11 + pub uri: Option<String>, 12 + #[serde(with = "chrono::serde::ts_seconds")] 13 + pub xata_createdat: DateTime<Utc>, 14 + #[serde(with = "chrono::serde::ts_seconds")] 15 + pub timestamp: DateTime<Utc>, 16 + }
+31
crates/pgpull/src/xata/track.rs
··· 1 + use chrono::{DateTime, Utc}; 2 + use serde::{Deserialize, Serialize}; 3 + 4 + #[derive(Debug, sqlx::FromRow, Serialize, Deserialize, Clone)] 5 + pub struct Track { 6 + pub xata_id: String, 7 + pub title: String, 8 + pub artist: String, 9 + pub album_artist: String, 10 + pub album_art: Option<String>, 11 + pub album: String, 12 + pub track_number: i32, 13 + pub duration: i32, 14 + pub mb_id: Option<String>, 15 + pub youtube_link: Option<String>, 16 + pub spotify_link: Option<String>, 17 + pub tidal_link: Option<String>, 18 + pub apple_music_link: Option<String>, 19 + pub sha256: String, 20 + pub lyrics: Option<String>, 21 + pub composer: Option<String>, 22 + pub genre: Option<String>, 23 + pub disc_number: i32, 24 + pub copyright_message: Option<String>, 25 + pub label: Option<String>, 26 + pub uri: Option<String>, 27 + pub artist_uri: Option<String>, 28 + pub album_uri: Option<String>, 29 + #[serde(with = "chrono::serde::ts_seconds")] 30 + pub xata_createdat: DateTime<Utc>, 31 + }
+13
crates/pgpull/src/xata/user.rs
··· 1 + use chrono::{DateTime, Utc}; 2 + use serde::Deserialize; 3 + 4 + #[derive(Debug, sqlx::FromRow, Deserialize, Clone)] 5 + pub struct User { 6 + pub xata_id: String, 7 + pub display_name: String, 8 + pub did: String, 9 + pub handle: String, 10 + pub avatar: String, 11 + #[serde(with = "chrono::serde::ts_seconds")] 12 + pub xata_createdat: DateTime<Utc>, 13 + }
+11
crates/pgpull/src/xata/user_album.rs
··· 1 + use serde::Deserialize; 2 + 3 + #[derive(Debug, sqlx::FromRow, Deserialize, Clone)] 4 + pub struct UserAlbum { 5 + pub xata_id: String, 6 + pub user_id: String, 7 + pub album_id: String, 8 + pub uri: Option<String>, 9 + #[serde(with = "chrono::serde::ts_seconds")] 10 + pub xata_createdat: chrono::DateTime<chrono::Utc>, 11 + }
+11
crates/pgpull/src/xata/user_artist.rs
··· 1 + use serde::Deserialize; 2 + 3 + #[derive(Debug, sqlx::FromRow, Deserialize, Clone)] 4 + pub struct UserArtist { 5 + pub xata_id: String, 6 + pub user_id: String, 7 + pub artist_id: String, 8 + pub uri: Option<String>, 9 + #[serde(with = "chrono::serde::ts_seconds")] 10 + pub xata_createdat: chrono::DateTime<chrono::Utc>, 11 + }
+11
crates/pgpull/src/xata/user_playlist.rs
··· 1 + use serde::Deserialize; 2 + 3 + #[derive(Debug, sqlx::FromRow, Deserialize, Clone)] 4 + pub struct UserPlaylist { 5 + pub xata_id: String, 6 + pub user_id: String, 7 + pub playlist_id: String, 8 + pub uri: Option<String>, 9 + #[serde(with = "chrono::serde::ts_seconds")] 10 + pub xata_createdat: chrono::DateTime<chrono::Utc>, 11 + }
+11
crates/pgpull/src/xata/user_track.rs
··· 1 + use serde::Deserialize; 2 + 3 + #[derive(Debug, sqlx::FromRow, Deserialize, Clone)] 4 + pub struct UserTrack { 5 + pub xata_id: String, 6 + pub user_id: String, 7 + pub track_id: String, 8 + pub uri: Option<String>, 9 + #[serde(with = "chrono::serde::ts_seconds")] 10 + pub xata_createdat: chrono::DateTime<chrono::Utc>, 11 + }
+1
crates/rockskyd/Cargo.toml
··· 20 20 rocksky-spotify = { path = "../spotify" } 21 21 rocksky-tracklist = { path = "../tracklist" } 22 22 rocksky-webscrobbler = { path = "../webscrobbler" } 23 + rocksky-pgpull = { path = "../pgpull" } 23 24 tracing = "0.1.41" 24 25 tracing-subscriber = "0.3.20" 25 26 tracing-log = "0.2.0"
+1
crates/rockskyd/src/cmd/mod.rs
··· 3 3 pub mod googledrive; 4 4 pub mod jetstream; 5 5 pub mod playlist; 6 + pub mod pull; 6 7 pub mod scrobbler; 7 8 pub mod spotify; 8 9 pub mod tracklist;
+5
crates/rockskyd/src/cmd/pull.rs
··· 1 + use anyhow::Error; 2 + 3 + pub async fn pull_data() -> Result<(), Error> { 4 + rocksky_pgpull::pull_data().await 5 + }
+8
crates/rockskyd/src/main.rs
··· 32 32 .subcommand(Command::new("spotify").about("Start Spotify Listener Service")) 33 33 .subcommand(Command::new("tracklist").about("Start User Current Track Queue Service")) 34 34 .subcommand(Command::new("webscrobbler").about("Start Webscrobbler API")) 35 + .subcommand( 36 + Command::new("pull") 37 + .about("Pull data from a remote PostgreSQL database to your local PostgresSQL instance") 38 + .long_about("Pull data from a remote PostgreSQL database to your local PostgresSQL instance. Ensure that the SOURCE_POSTGRES_URL environment variable is set to your remote PostgreSQL connection string."), 39 + ) 35 40 } 36 41 37 42 #[tokio::main] ··· 84 89 } 85 90 Some(("webscrobbler", _)) => { 86 91 cmd::webscrobbler::start_webscrobbler_service().await?; 92 + } 93 + Some(("pull", _)) => { 94 + cmd::pull::pull_data().await?; 87 95 } 88 96 _ => { 89 97 println!("No valid subcommand was used. Use --help to see available commands.");