itpdp/api/src/db/schema.ts
2026-04-25 18:20:07 +02:00

590 lines
12 KiB
TypeScript

import { defineRelations } from "drizzle-orm";
import {
boolean,
index,
integer,
json,
pgEnum,
pgTable,
primaryKey,
text,
timestamp,
uniqueIndex,
uuid,
} from "drizzle-orm/pg-core";
import { user } from "./auth-schema";
export * from "./auth-schema";
export const partyStatus = pgEnum("party_status", [
"created",
"started",
"ended",
]);
export const party = pgTable("party", {
id: uuid().defaultRandom().primaryKey().notNull(),
hostId: text()
.references(() => user.id)
.notNull(),
data: json(),
analysisData: json(),
createdAt: timestamp().defaultNow().notNull(),
lastUpdated: timestamp().defaultNow().notNull(),
status: partyStatus().notNull(),
});
export const memberStatus = pgEnum("member_status", [
"connected",
"disconnected",
]);
export const partyMember = pgTable(
"party_member",
{
id: uuid().defaultRandom().primaryKey().notNull(),
partyId: uuid()
.references(() => party.id)
.notNull(),
userId: text()
.references(() => user.id)
.notNull(),
joinedAt: timestamp().defaultNow().notNull(),
lastSeen: timestamp().defaultNow().notNull(),
},
(partyMember) => [uniqueIndex().on(partyMember.partyId, partyMember.userId)],
);
export const platform = pgEnum("enum_platform", ["spotify", "apple"]);
export const artist = pgTable("artist", {
id: uuid().defaultRandom().primaryKey().notNull(),
platform_id: text().notNull(),
platform: platform().notNull(),
name: text().notNull(),
type: text(),
popularity: integer(),
});
export const genre = pgTable("genre", {
id: uuid().defaultRandom().primaryKey().notNull(),
name: text().notNull().unique(),
});
export const artistGenre = pgTable(
"artist_genre",
{
artistId: uuid()
.references(() => artist.id)
.notNull(),
genreId: uuid()
.references(() => genre.id)
.notNull(),
},
(artistGenres) => [
primaryKey({
columns: [artistGenres.artistId, artistGenres.genreId],
}),
],
);
export const platformImage = pgTable(
"platform_image",
{
id: uuid().defaultRandom().primaryKey().notNull(),
platform: platform().notNull(),
url: text(),
height: integer(),
width: integer(),
},
(platformImage) => [
uniqueIndex().on(platformImage.platform, platformImage.url),
index().on(platformImage.url),
],
);
export const artistImage = pgTable(
"artist_image",
{
artistId: uuid()
.references(() => artist.id)
.notNull(),
imageId: uuid()
.references(() => platformImage.id)
.notNull(),
},
(artistImages) => [
primaryKey({
columns: [artistImages.artistId, artistImages.imageId],
}),
],
);
export const album = pgTable("album", {
id: uuid().defaultRandom().primaryKey().notNull(),
type: text(),
name: text(),
platform: platform(),
platform_id: text(),
popularity: integer(),
release_date: timestamp(),
label: text(),
});
export const albumImage = pgTable(
"album_image",
{
albumId: uuid()
.references(() => album.id)
.notNull(),
imageId: uuid()
.references(() => platformImage.id)
.notNull(),
},
(albumImage) => [
primaryKey({
columns: [albumImage.albumId, albumImage.imageId],
}),
],
);
export const albumArtist = pgTable(
"album_artist",
{
albumId: uuid()
.references(() => album.id)
.notNull(),
artistId: uuid()
.references(() => artist.id)
.notNull(),
},
(albumArtist) => [
primaryKey({
columns: [albumArtist.albumId, albumArtist.artistId],
}),
],
);
export const albumGenre = pgTable(
"album_genre",
{
albumId: uuid()
.references(() => album.id)
.notNull(),
genreId: uuid()
.references(() => genre.id)
.notNull(),
},
(albumGenre) => [
primaryKey({
columns: [albumGenre.albumId, albumGenre.genreId],
}),
],
);
export const track = pgTable(
"track",
{
id: uuid().defaultRandom().primaryKey().notNull(),
albumId: uuid()
.references(() => album.id)
.notNull(),
name: text(),
platform: platform(),
platform_id: text(),
popularity: integer(),
duration: integer(),
explicit: boolean(),
disc_number: integer(),
track_number: integer(),
},
(track) => [index().on(track.albumId)],
);
export const trackArtist = pgTable(
"track_artist",
{
trackId: uuid()
.references(() => track.id)
.notNull(),
artistId: uuid()
.references(() => artist.id)
.notNull(),
},
(trackArtist) => [
primaryKey({
columns: [trackArtist.trackId, trackArtist.artistId],
}),
index("track_artist_track_id_idx").on(trackArtist.trackId),
index("track_artist_artist_id_idx").on(trackArtist.artistId),
],
);
export const topTimeline = pgEnum("top_timeline", [
"short_term",
"medium_term",
"long_term",
]);
export const topTrack = pgTable(
"top_track",
{
trackId: uuid()
.references(() => track.id)
.notNull(),
position: integer().notNull(),
userId: text()
.references(() => user.id)
.notNull(),
timeline: topTimeline().notNull(),
},
(topTrack) => [
primaryKey({
columns: [topTrack.trackId, topTrack.userId, topTrack.timeline],
}),
index().on(topTrack.userId),
],
);
export const topArtist = pgTable(
"top_artist",
{
artistId: uuid()
.references(() => artist.id)
.notNull(),
position: integer().notNull(),
userId: text()
.references(() => user.id)
.notNull(),
timeline: topTimeline().notNull(),
},
(topArtist) => [
primaryKey({
columns: [topArtist.artistId, topArtist.userId, topArtist.timeline],
}),
index().on(topArtist.userId),
],
);
export const savedAlbum = pgTable(
"saved_album",
{
albumId: uuid()
.references(() => album.id)
.notNull(),
userId: text()
.references(() => user.id)
.notNull(),
saved_at: timestamp("saved_at").defaultNow().notNull(),
},
(savedAlbum) => [
primaryKey({
columns: [savedAlbum.albumId, savedAlbum.userId],
}),
index().on(savedAlbum.userId),
],
);
export const savedTrack = pgTable(
"saved_track",
{
trackId: uuid()
.references(() => track.id)
.notNull(),
userId: text()
.references(() => user.id)
.notNull(),
saved_at: timestamp("saved_at").defaultNow().notNull(),
},
(savedTrack) => [
primaryKey({
columns: [savedTrack.trackId, savedTrack.userId],
}),
index().on(savedTrack.userId),
],
);
export const followedArtist = pgTable(
"followed_artist",
{
artistId: uuid()
.references(() => artist.id)
.notNull(),
userId: text()
.references(() => user.id)
.notNull(),
},
(followedArtist) => [
primaryKey({
columns: [followedArtist.artistId, followedArtist.userId],
}),
index().on(followedArtist.userId),
],
);
export const playbackHistory = pgTable(
"playback_history",
{
id: uuid().defaultRandom().primaryKey().notNull(),
trackId: uuid()
.references(() => track.id)
.notNull(),
userId: text()
.references(() => user.id)
.notNull(),
played_at: timestamp("played_at").defaultNow().notNull(),
},
(playbackHistory) => [index().on(playbackHistory.userId)],
);
export const relations = defineRelations(
{
album,
albumImage,
albumArtist,
albumGenre,
artist,
artistGenre,
artistImage,
followedArtist,
genre,
party,
partyMember,
platformImage,
playbackHistory,
savedAlbum,
savedTrack,
topArtist,
topTrack,
track,
trackArtist,
user,
},
(r) => ({
artist: {
artistGenres: r.many.artistGenre(),
artistImages: r.many.artistImage(),
trackArtists: r.many.trackArtist(),
albumArtists: r.many.albumArtist(),
topArtists: r.many.topArtist(),
followedArtists: r.many.followedArtist(),
genres: r.many.genre({
from: r.artist.id.through(r.artistGenre.artistId),
to: r.genre.id.through(r.artistGenre.genreId),
}),
images: r.many.platformImage({
from: r.artist.id.through(r.artistImage.artistId),
to: r.platformImage.id.through(r.artistImage.imageId),
}),
albums: r.many.album({
from: r.artist.id.through(r.albumArtist.artistId),
to: r.album.id.through(r.albumArtist.albumId),
}),
tracks: r.many.track({
from: r.artist.id.through(r.trackArtist.artistId),
to: r.track.id.through(r.trackArtist.trackId),
}),
},
genre: {
artistGenres: r.many.artistGenre(),
albumGenres: r.many.albumGenre(),
artists: r.many.artist({
from: r.genre.id.through(r.artistGenre.genreId),
to: r.artist.id.through(r.artistGenre.artistId),
}),
albums: r.many.album({
from: r.genre.id.through(r.albumGenre.genreId),
to: r.album.id.through(r.albumGenre.albumId),
}),
},
artistGenre: {
artist: r.one.artist({
from: r.artistGenre.artistId,
to: r.artist.id,
}),
genre: r.one.genre({
from: r.artistGenre.genreId,
to: r.genre.id,
}),
},
platformImage: {
artistImages: r.many.artistImage(),
albumImages: r.many.albumImage(),
artists: r.many.artist({
from: r.platformImage.id.through(r.artistImage.imageId),
to: r.artist.id.through(r.artistImage.artistId),
}),
albums: r.many.album({
from: r.platformImage.id.through(r.albumImage.imageId),
to: r.album.id.through(r.albumImage.albumId),
}),
},
party: {
members: r.many.partyMember(),
host: r.one.user({
from: r.party.hostId,
to: r.user.id,
}),
},
partyMember: {
party: r.one.party({
from: r.partyMember.partyId,
to: r.party.id,
}),
user: r.one.user({
from: r.partyMember.userId,
to: r.user.id,
}),
},
artistImage: {
artist: r.one.artist({
from: r.artistImage.artistId,
to: r.artist.id,
}),
image: r.one.platformImage({
from: r.artistImage.imageId,
to: r.platformImage.id,
}),
},
album: {
tracks: r.many.track(),
albumImages: r.many.albumImage(),
albumArtists: r.many.albumArtist(),
albumGenres: r.many.albumGenre(),
savedAlbums: r.many.savedAlbum(),
images: r.many.platformImage({
from: r.album.id.through(r.albumImage.albumId),
to: r.platformImage.id.through(r.albumImage.imageId),
}),
artists: r.many.artist({
from: r.album.id.through(r.albumArtist.albumId),
to: r.artist.id.through(r.albumArtist.artistId),
}),
genres: r.many.genre({
from: r.album.id.through(r.albumGenre.albumId),
to: r.genre.id.through(r.albumGenre.genreId),
}),
},
albumImage: {
album: r.one.album({
from: r.albumImage.albumId,
to: r.album.id,
}),
image: r.one.platformImage({
from: r.albumImage.imageId,
to: r.platformImage.id,
}),
},
albumArtist: {
album: r.one.album({
from: r.albumArtist.albumId,
to: r.album.id,
}),
artist: r.one.artist({
from: r.albumArtist.artistId,
to: r.artist.id,
}),
},
albumGenre: {
album: r.one.album({
from: r.albumGenre.albumId,
to: r.album.id,
}),
genre: r.one.genre({
from: r.albumGenre.genreId,
to: r.genre.id,
}),
},
track: {
album: r.one.album({
from: r.track.albumId,
to: r.album.id,
}),
trackArtists: r.many.trackArtist(),
topTracks: r.many.topTrack(),
savedTracks: r.many.savedTrack(),
playbackHistory: r.many.playbackHistory(),
artists: r.many.artist({
from: r.track.id.through(r.trackArtist.trackId),
to: r.artist.id.through(r.trackArtist.artistId),
}),
},
trackArtist: {
track: r.one.track({
from: r.trackArtist.trackId,
to: r.track.id,
}),
artist: r.one.artist({
from: r.trackArtist.artistId,
to: r.artist.id,
}),
},
topTrack: {
track: r.one.track({
from: r.topTrack.trackId,
to: r.track.id,
}),
user: r.one.user({
from: r.topTrack.userId,
to: r.user.id,
}),
},
topArtist: {
artist: r.one.artist({
from: r.topArtist.artistId,
to: r.artist.id,
}),
user: r.one.user({
from: r.topArtist.userId,
to: r.user.id,
}),
},
savedAlbum: {
album: r.one.album({
from: r.savedAlbum.albumId,
to: r.album.id,
}),
user: r.one.user({
from: r.savedAlbum.userId,
to: r.user.id,
}),
},
savedTrack: {
track: r.one.track({
from: r.savedTrack.trackId,
to: r.track.id,
}),
user: r.one.user({
from: r.savedTrack.userId,
to: r.user.id,
}),
},
followedArtist: {
artist: r.one.artist({
from: r.followedArtist.artistId,
to: r.artist.id,
}),
user: r.one.user({
from: r.followedArtist.userId,
to: r.user.id,
}),
},
playbackHistory: {
track: r.one.track({
from: r.playbackHistory.trackId,
to: r.track.id,
}),
user: r.one.user({
from: r.playbackHistory.userId,
to: r.user.id,
}),
},
user: {
partyMembers: r.many.partyMember(),
hostedParties: r.many.party({
from: r.user.id,
to: r.party.hostId,
}),
},
}),
);