From facb928dffc7997d8bc872cf6fa08c8b03a471e5 Mon Sep 17 00:00:00 2001 From: alikia2x Date: Wed, 4 Feb 2026 14:39:15 +0800 Subject: [PATCH] update: a more comprehensive prisma schema --- .idea/cvsa.iml | 9 + .idea/data_source_mapping.xml | 14 +- .idea/db-forest-config.xml | 2 +- packages/core/.gitignore | 2 +- packages/core/prisma.config.ts | 2 +- packages/core/prisma/models/auth/README.md | 6 + .../core/prisma/models/auth/session.prisma | 13 + packages/core/prisma/models/auth/user.prisma | 22 ++ packages/core/prisma/models/core/README.md | 17 + packages/core/prisma/models/core/album.prisma | 17 + .../core/prisma/models/core/artist.prisma | 19 + .../core/prisma/models/core/artistRole.prisma | 16 + .../prisma/models/core/externalLink.prisma | 44 +++ packages/core/prisma/models/core/group.prisma | 11 + .../core/prisma/models/core/lyrics.prisma | 15 + .../core/prisma/models/core/singer.prisma | 20 + packages/core/prisma/models/core/song.prisma | 47 +++ .../core/prisma/models/core/songSeries.prisma | 16 + .../core/prisma/models/core/svsEngine.prisma | 16 + .../models/core/svsEngineVersion.prisma | 15 + packages/core/prisma/models/core/tag.prisma | 16 + .../core/prisma/models/core/voicebank.prisma | 18 + packages/core/prisma/models/meta/README.md | 6 + .../core/prisma/models/meta/history.prisma | 25 ++ .../core/prisma/models/meta/permission.prisma | 11 + packages/core/prisma/models/meta/role.prisma | 14 + packages/core/prisma/models/models.prisma | 11 + .../core/prisma/models/platform/README.md | 15 + .../core/prisma/models/platform/file.prisma | 11 + .../core/prisma/models/platform/post.prisma | 12 + .../models/platform/reputationHistory.prisma | 13 + packages/core/prisma/relations/README.md | 6 + .../prisma/relations/core/singerOfSong.prisma | 22 ++ .../relations/core/singerSvsEngine.prisma | 13 + .../core/singerSvsEngineVersion.prisma | 13 + .../prisma/relations/core/songInAlbum.prisma | 15 + .../prisma/relations/core/songInSeries.prisma | 14 + .../core/prisma/relations/core/songTag.prisma | 13 + .../relations/meta/rolePermission.prisma | 10 + .../core/prisma/relations/relations.prisma | 11 + packages/core/prisma/schema.prisma | 342 ++---------------- 41 files changed, 617 insertions(+), 317 deletions(-) create mode 100644 packages/core/prisma/models/auth/README.md create mode 100644 packages/core/prisma/models/auth/session.prisma create mode 100644 packages/core/prisma/models/auth/user.prisma create mode 100644 packages/core/prisma/models/core/README.md create mode 100644 packages/core/prisma/models/core/album.prisma create mode 100644 packages/core/prisma/models/core/artist.prisma create mode 100644 packages/core/prisma/models/core/artistRole.prisma create mode 100644 packages/core/prisma/models/core/externalLink.prisma create mode 100644 packages/core/prisma/models/core/group.prisma create mode 100644 packages/core/prisma/models/core/lyrics.prisma create mode 100644 packages/core/prisma/models/core/singer.prisma create mode 100644 packages/core/prisma/models/core/song.prisma create mode 100644 packages/core/prisma/models/core/songSeries.prisma create mode 100644 packages/core/prisma/models/core/svsEngine.prisma create mode 100644 packages/core/prisma/models/core/svsEngineVersion.prisma create mode 100644 packages/core/prisma/models/core/tag.prisma create mode 100644 packages/core/prisma/models/core/voicebank.prisma create mode 100644 packages/core/prisma/models/meta/README.md create mode 100644 packages/core/prisma/models/meta/history.prisma create mode 100644 packages/core/prisma/models/meta/permission.prisma create mode 100644 packages/core/prisma/models/meta/role.prisma create mode 100644 packages/core/prisma/models/models.prisma create mode 100644 packages/core/prisma/models/platform/README.md create mode 100644 packages/core/prisma/models/platform/file.prisma create mode 100644 packages/core/prisma/models/platform/post.prisma create mode 100644 packages/core/prisma/models/platform/reputationHistory.prisma create mode 100644 packages/core/prisma/relations/README.md create mode 100644 packages/core/prisma/relations/core/singerOfSong.prisma create mode 100644 packages/core/prisma/relations/core/singerSvsEngine.prisma create mode 100644 packages/core/prisma/relations/core/singerSvsEngineVersion.prisma create mode 100644 packages/core/prisma/relations/core/songInAlbum.prisma create mode 100644 packages/core/prisma/relations/core/songInSeries.prisma create mode 100644 packages/core/prisma/relations/core/songTag.prisma create mode 100644 packages/core/prisma/relations/meta/rolePermission.prisma create mode 100644 packages/core/prisma/relations/relations.prisma diff --git a/.idea/cvsa.iml b/.idea/cvsa.iml index f6f712f..0b90050 100644 --- a/.idea/cvsa.iml +++ b/.idea/cvsa.iml @@ -40,6 +40,15 @@ + + + + + + + + + diff --git a/.idea/data_source_mapping.xml b/.idea/data_source_mapping.xml index 8d18a04..a230540 100644 --- a/.idea/data_source_mapping.xml +++ b/.idea/data_source_mapping.xml @@ -1,17 +1,7 @@ - - - - - - - - - - - - + + \ No newline at end of file diff --git a/.idea/db-forest-config.xml b/.idea/db-forest-config.xml index 53aee3b..d092723 100644 --- a/.idea/db-forest-config.xml +++ b/.idea/db-forest-config.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/packages/core/.gitignore b/packages/core/.gitignore index 014185e..5360788 100644 --- a/packages/core/.gitignore +++ b/packages/core/.gitignore @@ -2,4 +2,4 @@ node_modules # Keep environment variables out of version control .env -/prisma/prisma +/prisma/generated diff --git a/packages/core/prisma.config.ts b/packages/core/prisma.config.ts index d65471d..2cf485b 100644 --- a/packages/core/prisma.config.ts +++ b/packages/core/prisma.config.ts @@ -2,7 +2,7 @@ import { defineConfig, env } from "prisma/config"; export default defineConfig({ - schema: "prisma/schema.prisma", + schema: "prisma/", migrations: { path: "prisma/migrations", }, diff --git a/packages/core/prisma/models/auth/README.md b/packages/core/prisma/models/auth/README.md new file mode 100644 index 0000000..624b014 --- /dev/null +++ b/packages/core/prisma/models/auth/README.md @@ -0,0 +1,6 @@ +# Auth Schema + +This schema handles Identity and Access Management (IAM). + +- **User:** The central account entity. +- **Session:** Tracks active logins, device information. Used in authorization. diff --git a/packages/core/prisma/models/auth/session.prisma b/packages/core/prisma/models/auth/session.prisma new file mode 100644 index 0000000..f999826 --- /dev/null +++ b/packages/core/prisma/models/auth/session.prisma @@ -0,0 +1,13 @@ +model Session { + id String @id + user User @relation(fields: [userId], references: [id]) + userId Int @map("user_id") + ipAddress String? @map("ip_address") + userAgent String? @map("user_agent") + secretHash String @map("secret_hash") + createdAt DateTime @default(now()) @map("created_at") + lastVerifiedAt DateTime @map("last_verified_at") + + @@map("session") + @@schema("auth") +} diff --git a/packages/core/prisma/models/auth/user.prisma b/packages/core/prisma/models/auth/user.prisma new file mode 100644 index 0000000..e316ebf --- /dev/null +++ b/packages/core/prisma/models/auth/user.prisma @@ -0,0 +1,22 @@ +model User { + id Int @id @default(autoincrement()) + username String @unique + displayName String @map("display_name") + email String @unique + password String + changesMade History[] + role Role? @relation(fields: [roleId], references: [id]) + roleId Int? @map("role_id") + sessions Session[] + artist Artist? + reputation Int @default(0) + reputationHistories ReputationHistory[] + posts Post[] + files File[] + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + deletedAt DateTime? @map("deleted_at") + + @@map("user") + @@schema("auth") +} diff --git a/packages/core/prisma/models/core/README.md b/packages/core/prisma/models/core/README.md new file mode 100644 index 0000000..feb25a4 --- /dev/null +++ b/packages/core/prisma/models/core/README.md @@ -0,0 +1,17 @@ +# Core Schema + +The `core` schema is the heart of the CVSA database. It functions as a structured encyclopedia for the Chinese Singing Voice Synthesis community. + +## Design Philosophy + +The data here is designed to be **objective and universal**. It models the relationships between music, creators, etc. + +## Sub-domains + +1. **Music Entities:** Songs, Albums, Lyrics, and Song Series. +2. **Talent:** Artists (producers/illustrators) and Singers (virtual characters). +3. **Classification:** A hierarchical Tag system for genre and thematic categorization. + +## Localization + +Most entities use `LocalizedField` (JSON) to support multi-language names and descriptions (e.g., Chinese, Japanese, and English). diff --git a/packages/core/prisma/models/core/album.prisma b/packages/core/prisma/models/core/album.prisma new file mode 100644 index 0000000..2c22677 --- /dev/null +++ b/packages/core/prisma/models/core/album.prisma @@ -0,0 +1,17 @@ +model Album { + id Int @id @default(autoincrement()) + name String? + /// [LocalizedField] + localizedNames Json? @map("localized_names") + description String? + /// [LocalizedField] + localizedDescriptions Json? @map("localized_descriptions") + songInAlbums SongInAlbum[] + coverUrl String? @map("cover_url") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + deletedAt DateTime? @map("deleted_at") + + @@map("album") + @@schema("core") +} diff --git a/packages/core/prisma/models/core/artist.prisma b/packages/core/prisma/models/core/artist.prisma new file mode 100644 index 0000000..cfbe2d2 --- /dev/null +++ b/packages/core/prisma/models/core/artist.prisma @@ -0,0 +1,19 @@ +model Artist { + id Int @id @default(autoincrement()) + name String? + /// [LocalizedField] + localizedNames Json? @map("localized_names") + aliases String[] + description String? + /// [LocalizedField] + localizedDescriptions Json? @map("localized_descriptions") + artistSongRoles ArtistRole[] + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + user User? @relation(fields: [userId], references: [id]) + userId Int? @unique @map("user_id") + deletedAt DateTime? @map("deleted_at") + + @@map("artist") + @@schema("core") +} diff --git a/packages/core/prisma/models/core/artistRole.prisma b/packages/core/prisma/models/core/artistRole.prisma new file mode 100644 index 0000000..40dc72b --- /dev/null +++ b/packages/core/prisma/models/core/artistRole.prisma @@ -0,0 +1,16 @@ +model ArtistRole { + id Int @id @default(autoincrement()) + role String + /// [LocalizedField] + localizedRoles Json? @map("localized_roles") + song Song @relation(fields: [songId], references: [id]) + songId Int @map("song_id") + artist Artist @relation(fields: [artistId], references: [id]) + artistId Int @map("artist_id") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + deletedAt DateTime? @map("deleted_at") + + @@map("artist_role") + @@schema("core") +} diff --git a/packages/core/prisma/models/core/externalLink.prisma b/packages/core/prisma/models/core/externalLink.prisma new file mode 100644 index 0000000..4f97458 --- /dev/null +++ b/packages/core/prisma/models/core/externalLink.prisma @@ -0,0 +1,44 @@ +model ExternalLink { + id Int @id @default(autoincrement()) + label String? + url String + platform Platform? + platformId String? @map("platform_id") + song Song @relation(fields: [songId], references: [id]) + songId Int @map("song_id") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + deletedAt DateTime? @map("deleted_at") + + @@map("external_link") + @@schema("core") +} + +enum Platform { + YOUTUBE + NICONICO + BILIBILI + VOCALOID_WIKI + VOCALOID_LYRICS_WIKI + SOUNDCLOUD + NETEASE_MUSIC + QQ_MUSIC + FIVE_SING // 5sing.kugou.com + KUGOU + SPOTIFY + APPLE_MUSIC + YOUTUBE_MUSIC + WIKIPEDIA + BAIDU_BAIKE + PIXIV + WEIBO + TWITTER + VOCADB + MOEGIRLPEDIA + MUSICBRAINZ + VCPEDIA + XIAOHONGSHU // aka RedNote + + @@map("third_party_platform") + @@schema("core") +} diff --git a/packages/core/prisma/models/core/group.prisma b/packages/core/prisma/models/core/group.prisma new file mode 100644 index 0000000..c8ac718 --- /dev/null +++ b/packages/core/prisma/models/core/group.prisma @@ -0,0 +1,11 @@ +model Group { + id Int @id @default(autoincrement()) + name String + description String? + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + deletedAt DateTime? @map("deleted_at") + + @@map("group") + @@schema("core") +} diff --git a/packages/core/prisma/models/core/lyrics.prisma b/packages/core/prisma/models/core/lyrics.prisma new file mode 100644 index 0000000..7ee4257 --- /dev/null +++ b/packages/core/prisma/models/core/lyrics.prisma @@ -0,0 +1,15 @@ +model Lyrics { + id Int @id @default(autoincrement()) + song Song @relation(fields: [songId], references: [id]) + songId Int @map("song_id") + language String? + plainText String? @map("plain_text") + ttml String? + lrc String? + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + deletedAt DateTime? @map("deleted_at") + + @@map("lyrics") + @@schema("core") +} diff --git a/packages/core/prisma/models/core/singer.prisma b/packages/core/prisma/models/core/singer.prisma new file mode 100644 index 0000000..7cd3152 --- /dev/null +++ b/packages/core/prisma/models/core/singer.prisma @@ -0,0 +1,20 @@ +model Singer { + id Int @id @default(autoincrement()) + name String? + avatarUrl String? @map("avatar_url") + /// [LocalizedField] + localizedNames Json? @map("localized_names") + description String? + /// [LocalizedField] + localizedDescriptions Json? @map("localized_descriptions") + singerSvsEngineVersions SingerSvsEngineVersion[] + singerSvsEngines SingerSvsEngine[] + voicebanks Voicebank[] + singerOfSongs SingerOfSong[] + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + deletedAt DateTime? @map("deleted_at") + + @@map("singer") + @@schema("core") +} diff --git a/packages/core/prisma/models/core/song.prisma b/packages/core/prisma/models/core/song.prisma new file mode 100644 index 0000000..48e6e62 --- /dev/null +++ b/packages/core/prisma/models/core/song.prisma @@ -0,0 +1,47 @@ +model Song { + id Int @id @default(autoincrement()) + type SongType? + originalVersion Song? @relation(name: "originalSong", fields: [originalSongId], references: [id]) + originalSongId Int? @map("original_song_id") + derivations Song[] @relation(name: "originalSong") + name String? + duration Int? + lyrics Lyrics[] + songTags SongTag[] + /// [LocalizedField] + localizedNames Json? @map("localized_names") + description String? + /// [LocalizedField] + localizedDescriptions Json? @map("localized_descriptions") + songInAlbums SongInAlbum[] + bilibiliAid BigInt? @map("bilibili_aid") + bilibiliBvid String? @map("bilibili_bvid") + vocadbId Int? @map("vocadb_id") + vcpediaId Int? @map("vcpedia_id") + moegirlId Int? @map("moegirl_id") + externalLinks ExternalLink[] + coverUrl String? @map("cover_url") + artists ArtistRole[] + singerOfSongs SingerOfSong[] + publishedAt DateTime? @map("published_at") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + deletedAt DateTime? @map("deleted_at") + songInSeries SongInSeries[] + + @@map("song") + @@schema("core") +} + +enum SongType { + ORIGINAL + COVER + REMIX + REMASTER + MASHUP + INSTRUMENTAL + OTHERS + + @@map("song_type") + @@schema("core") +} diff --git a/packages/core/prisma/models/core/songSeries.prisma b/packages/core/prisma/models/core/songSeries.prisma new file mode 100644 index 0000000..414cadb --- /dev/null +++ b/packages/core/prisma/models/core/songSeries.prisma @@ -0,0 +1,16 @@ +model SongSeries { + id Int @id @default(autoincrement()) + name String? + /// [LocalizedField] + localizedNames Json? @map("localized_names") + description String? + /// [LocalizedField] + localizedDescriptions Json? @map("localized_descriptions") + songInSeries SongInSeries[] + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + deletedAt DateTime? @map("deleted_at") + + @@map("song_series") + @@schema("core") +} diff --git a/packages/core/prisma/models/core/svsEngine.prisma b/packages/core/prisma/models/core/svsEngine.prisma new file mode 100644 index 0000000..ac999f5 --- /dev/null +++ b/packages/core/prisma/models/core/svsEngine.prisma @@ -0,0 +1,16 @@ +model SvsEngine { + id Int @id @default(autoincrement()) + name String + description String? + /// [LocalizedField] + localizedDescriptions Json? @map("localized_descriptions") + svsEngineVersions SvsEngineVersion[] + singerSvsEngines SingerSvsEngine[] + performances SingerOfSong[] + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + deletedAt DateTime? @map("deleted_at") + + @@map("svs_engine") + @@schema("core") +} diff --git a/packages/core/prisma/models/core/svsEngineVersion.prisma b/packages/core/prisma/models/core/svsEngineVersion.prisma new file mode 100644 index 0000000..154acd7 --- /dev/null +++ b/packages/core/prisma/models/core/svsEngineVersion.prisma @@ -0,0 +1,15 @@ +model SvsEngineVersion { + id Int @id @default(autoincrement()) + versionString String @map("version_string") + singerSvsEngineVersions SingerSvsEngineVersion[] + svsEngine SvsEngine @relation(fields: [svsEngineId], references: [id]) + svsEngineId Int @map("svs_engine_id") + singerOfSongs SingerOfSong[] + voicebanks Voicebank[] + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + deletedAt DateTime? @map("deleted_at") + + @@map("svs_engine_version") + @@schema("core") +} diff --git a/packages/core/prisma/models/core/tag.prisma b/packages/core/prisma/models/core/tag.prisma new file mode 100644 index 0000000..bcd0f28 --- /dev/null +++ b/packages/core/prisma/models/core/tag.prisma @@ -0,0 +1,16 @@ +model Tag { + id Int @id @default(autoincrement()) + name String + /// [LocalizedField] + localizedNames Json? @map("localized_names") + songTags SongTag[] + parent Tag? @relation(name: "tagTree", fields: [parentId], references: [id]) + parentId Int? @map("parent_id") + children Tag[] @relation(name: "tagTree") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + deletedAt DateTime? @map("deleted_at") + + @@map("tag") + @@schema("core") +} diff --git a/packages/core/prisma/models/core/voicebank.prisma b/packages/core/prisma/models/core/voicebank.prisma new file mode 100644 index 0000000..a7adae2 --- /dev/null +++ b/packages/core/prisma/models/core/voicebank.prisma @@ -0,0 +1,18 @@ +model Voicebank { + id Int @id @default(autoincrement()) + singer Singer @relation(fields: [singerId], references: [id]) + description String? + /// [LocalizedField] + localizedDescriptions Json? @map("localized_descriptions") + singerId Int @map("singer_id") + language String + svsEngineVersion SvsEngineVersion @relation(fields: [svsEngineVersionId], references: [id]) + svsEngineVersionId Int @map("svs_engine_version_id") + singerOfSongs SingerOfSong[] + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + deletedAt DateTime? @map("deleted_at") + + @@map("voicebank") + @@schema("core") +} diff --git a/packages/core/prisma/models/meta/README.md b/packages/core/prisma/models/meta/README.md new file mode 100644 index 0000000..73b0609 --- /dev/null +++ b/packages/core/prisma/models/meta/README.md @@ -0,0 +1,6 @@ +# Meta Schema + +The `meta` schema manages system governance, auditing, and configuration. + +- **Auditing:** The `History` table tracks every change made to the `core` entities (Create/Update/Delete) for rollback and accountability. +- **RBAC:** Implements Role-Based Access Control via `Role` and `Permission` entities. diff --git a/packages/core/prisma/models/meta/history.prisma b/packages/core/prisma/models/meta/history.prisma new file mode 100644 index 0000000..4c6839c --- /dev/null +++ b/packages/core/prisma/models/meta/history.prisma @@ -0,0 +1,25 @@ +model History { + id Int @id @default(autoincrement()) + user User @relation(fields: [userId], references: [id]) + userId Int @map("user_id") + table String + objectId Int @map("object_id") + type ChangeType + comment String? + old Json? + new Json? + createdAt DateTime @default(now()) @map("created_at") + + @@map("history") + @@schema("meta") +} + +enum ChangeType { + CREATE + UPDATE + DELETE + RESTORE + + @@map("change_type") + @@schema("meta") +} diff --git a/packages/core/prisma/models/meta/permission.prisma b/packages/core/prisma/models/meta/permission.prisma new file mode 100644 index 0000000..4a7be38 --- /dev/null +++ b/packages/core/prisma/models/meta/permission.prisma @@ -0,0 +1,11 @@ +model Permission { + id Int @id @default(autoincrement()) + action String + roles RolePermission[] + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + deletedAt DateTime? @map("deleted_at") + + @@map("permission") + @@schema("meta") +} diff --git a/packages/core/prisma/models/meta/role.prisma b/packages/core/prisma/models/meta/role.prisma new file mode 100644 index 0000000..e4d04c2 --- /dev/null +++ b/packages/core/prisma/models/meta/role.prisma @@ -0,0 +1,14 @@ +model Role { + id Int @id @default(autoincrement()) + name String @unique + /// [LocalizedField] + localizedNames Json? @map("localized_names") + permissions RolePermission[] + users User[] + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + deletedAt DateTime? @map("deleted_at") + + @@map("role") + @@schema("meta") +} diff --git a/packages/core/prisma/models/models.prisma b/packages/core/prisma/models/models.prisma new file mode 100644 index 0000000..d1a3861 --- /dev/null +++ b/packages/core/prisma/models/models.prisma @@ -0,0 +1,11 @@ +/* +NOTE: +This is an empty placeholder file to ensure the Prisma Language Server functions +correctly, serving as a temporary workaround. + +See the link below for details: +https://github.com/prisma/prisma/discussions/24413#discussioncomment-9670600 + +This issue persists as of Prisma v7.3.0, which was the latest +version at the time of writing. +*/ \ No newline at end of file diff --git a/packages/core/prisma/models/platform/README.md b/packages/core/prisma/models/platform/README.md new file mode 100644 index 0000000..aa40682 --- /dev/null +++ b/packages/core/prisma/models/platform/README.md @@ -0,0 +1,15 @@ +# Platform Schema + +This schema contains tables and logic specific to the **CVSA as a platform**. + +## Purpose + +While the `core` schema focuses on universal SVS (Singing Voice Synthesis) data that could theoretically be shared across different encyclopedia projects, the `platform` schema manages data unique to this specific implementation. + +It includes: + +- **User Activity:** Personal notes, bookmarks, and interaction history. +- **System State:** File storage metadata and upload tracking. + +and so on. + diff --git a/packages/core/prisma/models/platform/file.prisma b/packages/core/prisma/models/platform/file.prisma new file mode 100644 index 0000000..0fcea7e --- /dev/null +++ b/packages/core/prisma/models/platform/file.prisma @@ -0,0 +1,11 @@ +model File { + id Int @id @default(autoincrement()) + originalName String? @map("original_name") + path String? + user User @relation(fields: [userId], references: [id]) + userId Int @map("user_id") + createdAt DateTime @default(now()) @map("created_at") + + @@map("file") + @@schema("platform") +} diff --git a/packages/core/prisma/models/platform/post.prisma b/packages/core/prisma/models/platform/post.prisma new file mode 100644 index 0000000..3ee4c3c --- /dev/null +++ b/packages/core/prisma/models/platform/post.prisma @@ -0,0 +1,12 @@ +model Post { + id Int @id @default(autoincrement()) + text String? + user User @relation(fields: [userId], references: [id]) + userId Int @map("user_id") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + deletedAt DateTime? @map("deleted_at") + + @@map("post") + @@schema("platform") +} diff --git a/packages/core/prisma/models/platform/reputationHistory.prisma b/packages/core/prisma/models/platform/reputationHistory.prisma new file mode 100644 index 0000000..4b635bc --- /dev/null +++ b/packages/core/prisma/models/platform/reputationHistory.prisma @@ -0,0 +1,13 @@ +model ReputationHistory { + id Int @id @default(autoincrement()) + user User @relation(fields: [userId], references: [id]) + userId Int @map("user_id") + actionType String @map("action_type") + points Int + sourceType Int @map("source_type") + sourceId Int @map("source_id") + createdAt DateTime @default(now()) @map("created_at") + + @@map("reputation_history") + @@schema("platform") +} diff --git a/packages/core/prisma/relations/README.md b/packages/core/prisma/relations/README.md new file mode 100644 index 0000000..9c6d866 --- /dev/null +++ b/packages/core/prisma/relations/README.md @@ -0,0 +1,6 @@ +# Relations + +This directory contains join tables for Many-to-Many relationships. + +To maintain a clean and modular Prisma schema, we separate core data models (in `/models`) from their association entities. +This structure helps prevent individual `.prisma` files from becoming too large and complex. diff --git a/packages/core/prisma/relations/core/singerOfSong.prisma b/packages/core/prisma/relations/core/singerOfSong.prisma new file mode 100644 index 0000000..bd42ca5 --- /dev/null +++ b/packages/core/prisma/relations/core/singerOfSong.prisma @@ -0,0 +1,22 @@ +// Sometimes, we only know the SVS engine used in the song (e.g. VOCALOID, Synthesizer V, UTAU, etc.). +// In other cases, we know exactly which voicebank was used in the song. (e.g. Luo Tianyi VOCALOID 5 Chinese). +// That's why we need to have both SvsEngine and SvsEngineVersion. +model SingerOfSong { + id Int @id @default(autoincrement()) + song Song @relation(fields: [songId], references: [id]) + songId Int @map("song_id") + singer Singer @relation(fields: [singerId], references: [id]) + singerId Int @map("singer_id") + voicebank Voicebank? @relation(fields: [voicebankId], references: [id]) + voicebankId Int? @map("voicebank_id") + svsEngine SvsEngine? @relation(fields: [svsEngineId], references: [id]) + svsEngineId Int? @map("svs_engine_id") + svsEngineVersion SvsEngineVersion? @relation(fields: [svsEngineVersionId], references: [id]) + svsEngineVersionId Int? @map("svs_engine_version_id") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + deletedAt DateTime? @map("deleted_at") + + @@map("singer_of_song") + @@schema("core") +} diff --git a/packages/core/prisma/relations/core/singerSvsEngine.prisma b/packages/core/prisma/relations/core/singerSvsEngine.prisma new file mode 100644 index 0000000..f4440b0 --- /dev/null +++ b/packages/core/prisma/relations/core/singerSvsEngine.prisma @@ -0,0 +1,13 @@ +model SingerSvsEngine { + id Int @id @default(autoincrement()) + singer Singer @relation(fields: [singerId], references: [id]) + singerId Int @map("singer_id") + svsEngine SvsEngine @relation(fields: [svsEngineId], references: [id]) + svsEngineId Int @map("svs_engine_id") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + deletedAt DateTime? @map("deleted_at") + + @@map("singer_svs_engine") + @@schema("core") +} diff --git a/packages/core/prisma/relations/core/singerSvsEngineVersion.prisma b/packages/core/prisma/relations/core/singerSvsEngineVersion.prisma new file mode 100644 index 0000000..bc21e73 --- /dev/null +++ b/packages/core/prisma/relations/core/singerSvsEngineVersion.prisma @@ -0,0 +1,13 @@ +model SingerSvsEngineVersion { + id Int @id @default(autoincrement()) + singer Singer @relation(fields: [singerId], references: [id]) + singerId Int @map("singer_id") + svsEngineVersion SvsEngineVersion @relation(fields: [svsEngineVersionId], references: [id]) + svsEngineVersionId Int @map("svs_engine_version_id") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + deletedAt DateTime? @map("deleted_at") + + @@map("singer_svs_engine_version") + @@schema("core") +} diff --git a/packages/core/prisma/relations/core/songInAlbum.prisma b/packages/core/prisma/relations/core/songInAlbum.prisma new file mode 100644 index 0000000..939d0bf --- /dev/null +++ b/packages/core/prisma/relations/core/songInAlbum.prisma @@ -0,0 +1,15 @@ +model SongInAlbum { + id Int @id @default(autoincrement()) + song Song @relation(fields: [songId], references: [id]) + songId Int @map("song_id") + album Album @relation(fields: [albumId], references: [id]) + albumId Int @map("album_id") + trackNumber Int @map("track_number") + discNumber Int @default(1) @map("disc_number") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + deletedAt DateTime? @map("deleted_at") + + @@map("song_in_album") + @@schema("core") +} diff --git a/packages/core/prisma/relations/core/songInSeries.prisma b/packages/core/prisma/relations/core/songInSeries.prisma new file mode 100644 index 0000000..0e35608 --- /dev/null +++ b/packages/core/prisma/relations/core/songInSeries.prisma @@ -0,0 +1,14 @@ +model SongInSeries { + id Int @id @default(autoincrement()) + song Song @relation(fields: [songId], references: [id]) + songId Int @map("song_id") + series SongSeries @relation(fields: [seriesId], references: [id]) + seriesId Int @map("series_id") + order Int + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + deletedAt DateTime? @map("deleted_at") + + @@map("song_in_series") + @@schema("core") +} diff --git a/packages/core/prisma/relations/core/songTag.prisma b/packages/core/prisma/relations/core/songTag.prisma new file mode 100644 index 0000000..1a05034 --- /dev/null +++ b/packages/core/prisma/relations/core/songTag.prisma @@ -0,0 +1,13 @@ +model SongTag { + id Int @id @default(autoincrement()) + song Song @relation(fields: [songId], references: [id]) + songId Int @map("song_id") + tag Tag @relation(fields: [tagId], references: [id]) + tagId Int @map("tag_id") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + deletedAt DateTime? @map("deleted_at") + + @@map("song_tag") + @@schema("core") +} diff --git a/packages/core/prisma/relations/meta/rolePermission.prisma b/packages/core/prisma/relations/meta/rolePermission.prisma new file mode 100644 index 0000000..8908d72 --- /dev/null +++ b/packages/core/prisma/relations/meta/rolePermission.prisma @@ -0,0 +1,10 @@ +model RolePermission { + id Int @id @default(autoincrement()) + role Role @relation(fields: [roleId], references: [id]) + roleId Int @map("role_id") + permission Permission @relation(fields: [permissionId], references: [id]) + permissionId Int @map("permission_id") + + @@map("role_permission") + @@schema("meta") +} diff --git a/packages/core/prisma/relations/relations.prisma b/packages/core/prisma/relations/relations.prisma new file mode 100644 index 0000000..d1a3861 --- /dev/null +++ b/packages/core/prisma/relations/relations.prisma @@ -0,0 +1,11 @@ +/* +NOTE: +This is an empty placeholder file to ensure the Prisma Language Server functions +correctly, serving as a temporary workaround. + +See the link below for details: +https://github.com/prisma/prisma/discussions/24413#discussioncomment-9670600 + +This issue persists as of Prisma v7.3.0, which was the latest +version at the time of writing. +*/ \ No newline at end of file diff --git a/packages/core/prisma/schema.prisma b/packages/core/prisma/schema.prisma index aefd8bf..34645f1 100644 --- a/packages/core/prisma/schema.prisma +++ b/packages/core/prisma/schema.prisma @@ -1,322 +1,60 @@ -// This is your Prisma schema file, -// learn more about it in the docs: https://pris.ly/d/prisma-schema - -// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions? -// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init - generator client { provider = "prisma-client" - output = "./prisma" + output = "./generated" } generator json { - provider = "prisma-json-types-generator" + provider = "prisma-json-types-generator" } datasource db { provider = "postgresql" + schemas = ["core", "meta", "platform", "auth"] } -// TODO -// Note: -// 1. All the IDs are 12-digit string generated via Nano ID with a custom 32-character alphabet. -// 2. Auth-related model has not yet been defined. -// 3. Indexes and unique constraints have not yet been considered in the design. +/* +# CVSA (Chinese Vocal Synthesis Archive) Database Schema -enum SongType { - ORIGINAL - COVER - REMIX - REMASTER - MASHUP - INSTRUMENTAL - OTHERS +## Overview +The CVSA is a structured encyclopedia and data platform dedicated to the Chinese Vocal Synthesis (SVS/VC) community. +It aims to bridge the gap between traditional wikis (like MediaWiki) and structured databases by providing: +- Automation: Fully automated song discovery, metadata extraction, and statistical tracking. +- Structured Data: Moving beyond flat text to rich, relational data for songs, artists, engines, and albums. +- Collaboration: A platform for contributors to provide descriptive content, translations, and corrections. - @@map("song_type") -} +## Schemas +The database is partitioned into several PostgreSQL schemas. -enum Platform { - YOUTUBE - NICONICO - BILIBILI - VOCALOID_WIKI - VOCALOID_LYRIC_SWIKI - SOUNDCLOUD - NETEASE_MUSIC - QQ_MUSIC - FIVE_SING // 5sing.kugou.com - KUGOU - SPOTIFY - APPLE_MUSIC - YOUTUBE_MUSIC - WIKIPEDIA - BAIDU_BAIKE - PIXIV - WEIBO - TWITTER - VOCADB - MOEGIRLPEDIA - MUSICBRAINZ - VCPEDIA - XIAOHONGSHU // aka RedNote +### A. [core] - The Encyclopedia +This is the heart of the project. It stores universal SVS data that remains objective across platforms. +- Entities: Songs, Singers (virtual characters), Artists (producers/illustrators), +SVS Engines (Vocaloid, Synthesizer V), Voicebanks, Albums, and Tags. +- Localization: Uses `LocalizedField` (JSON) to support multi-language metadata +(Simplified Chinese, Traditional Chinese, Japanese, English, etc.). +- Versioning: Supports tracking of specific SVS engine versions and voicebank iterations. - @@map("platform") -} +### B. [platform] - The Service Layer +Contains data specific to the CVSA website as a functional service. +- Features: User-uploaded files, personal notes, etc. +- Scope: Data here is specific to this implementation and not necessarily shared with other archives. -model Song { - id String @id - type SongType? - originalVersion Song? @relation(name: "originalSong", fields: [originalSongId], references: [id]) - originalSongId String? @map("original_song_id") - derivations Song[] @relation(name: "originalSong") - name String? - duration Int? - lyrics Lyrics[] - songTags SongTag[] - /// [LocalizedField] - localizedNames Json? - description String? - /// [LocalizedField] - localizedDescriptions Json? - songInAlbums SongInAlbum[] - bilibiliAid BigInt? @map("bilibili_aid") - bilibiliBvid String? @map("bilibili_bvid") - vocadbId Int? @map("vocadb_id") - vcpediaId Int? @map("vcpedia_id") - moegirlId Int? @map("moegirl_id") - externalLinks ExternalLink[] - coverUrl String? @map("cover_url") - artists ArtistRole[] - singerOfSongs SingerOfSong[] - publishedAt DateTime? @map("published_at") - createdAt DateTime @default(now()) @map("created_at") - updatedAt DateTime @updatedAt @map("updated_at") - deletedAt DateTime? @map("deleted_at") +### C. [auth] - Identity & Access +Handles user accounts, session persistence, and secure authentication. +- RBAC: Connects users to roles defined in the `meta` schema. - @@map("song") -} +### D. [meta] - Governance & Auditing +Manages the "data about data." +- Audit Logs: The `History` table tracks every mutation (Create/Update/Delete) +in the `core` schema for accountability and rollbacks. +- Permissions: Granular action-based permissions assigned to roles. -model Tag { - id String @id - name String - /// [LocalizedField] - localizedNames Json? - songTags SongTag[] - parent Tag? @relation(name: "tagTree", fields: [parentId], references: [id]) - parentId String? @map("parent_id") - children Tag[] @relation(name: "tagTree") - createdAt DateTime @default(now()) @map("created_at") - updatedAt DateTime @updatedAt @map("updated_at") +## 3. Technical Constraints & Design Patterns +- Soft Deletion: Most core entities implement a `deletedAt` timestamp for soft-deletion support. +- Modular Relations: Many-to-Many relations are extracted into separate files +within the `/relations` directory to keep entity definitions clean and prevent Prisma file bloat. - @@map("tag") -} +## 4. Current Status & TODO +- Indexing: Performance indexes and unique constraints beyond primary keys are still under design. +- Constraints: Complex business logic constraints (e.g., preventing circular tag parents) are handled at the application layer. -model Lyrics { - id String @id - song Song @relation(fields: [songId], references: [id]) - songId String @map("song_id") - language String? - plaintext String? - ttml String? - lrc String? - createdAt DateTime @default(now()) @map("created_at") - updatedAt DateTime @updatedAt @map("updated_at") - - @@map("lyrics") -} - -model History { - id String @id - table String - objectId String @map("object_id") - old Json? - new Json? - createdAt DateTime @default(now()) @map("created_at") - - @@map("history") -} - -model ExternalLink { - id String @id - label String? - url String - platform Platform? - platformId String? @map("platform_id") - song Song @relation(fields: [songId], references: [id]) - songId String @map("song_id") - createdAt DateTime @default(now()) @map("created_at") - updatedAt DateTime @updatedAt @map("updated_at") - - @@map("external_link") -} - -// Sometimes, we only know the SVS engine (e.g. VOCALOID, Synthesizer V) used in the song. -// And in other cases, we know exactly the voicebank used in the song. (e.g. Luo Tianyi VOCALOID 5 Chinese) -// That's why we need to have both SvsEngine and SvsEngineVersion. -model SingerOfSong { - id String @id - song Song @relation(fields: [songId], references: [id]) - songId String @map("song_id") - singer Singer @relation(fields: [singerId], references: [id]) - singerId String @map("singer_id") - voicebank Voicebank? @relation(fields: [voicebankId], references: [id]) - voicebankId String? @map("voicebank_id") - svsEngine SvsEngine? @relation(fields: [svsEngineId], references: [id]) - svsEngineId String? @map("svs_engine_id") - svsEngineVersion SvsEngineVersion? @relation(fields: [svsEngineVersionId], references: [id]) - svsEngineVersionId String? @map("svs_engine_version_id") - createdAt DateTime @default(now()) @map("created_at") - updatedAt DateTime @updatedAt @map("updated_at") - - @@map("singer_of_song") -} - -model Voicebank { - id String @id - singer Singer @relation(fields: [singerId], references: [id]) - description String? - /// [LocalizedField] - localizedDescriptions Json? - singerId String @map("singer_id") - language String - svsEngineVersion SvsEngineVersion @relation(fields: [svsEngineVersionId], references: [id]) - svsEngineVersionId String @map("svs_engine_version_id") - singerOfSongs SingerOfSong[] - createdAt DateTime @default(now()) @map("created_at") - updatedAt DateTime @updatedAt @map("updated_at") - - @@map("voicebank") -} - -model Album { - id String @id - name String? - /// [LocalizedField] - localizedNames Json? - description String? - /// [LocalizedField] - localizedDescriptions Json? - songInAlbums SongInAlbum[] - coverUrl String? @map("cover_url") - createdAt DateTime @default(now()) @map("created_at") - updatedAt DateTime @updatedAt @map("updated_at") - - @@map("album") -} - -model SongInAlbum { - song Song @relation(fields: [songId], references: [id]) - songId String @map("song_id") - album Album @relation(fields: [albumId], references: [id]) - albumId String @map("album_id") - trackNumber Int @map("track_number") - discNumber Int @default(1) @map("disc_number") - - @@id([songId, albumId]) - @@map("song_in_album") -} - -model SongTag { - song Song @relation(fields: [songId], references: [id]) - songId String @map("song_id") - tag Tag @relation(fields: [tagId], references: [id]) - tagId String @map("tag_id") - - @@id([songId, tagId]) - @@map("song_tag") -} - -model SingerSvsEngineVersion { - singer Singer @relation(fields: [singerId], references: [id]) - singerId String @map("singer_id") - svsEngineVersion SvsEngineVersion @relation(fields: [svsEngineVersionId], references: [id]) - svsEngineVersionId String @map("svs_engine_version_id") - - @@id([singerId, svsEngineVersionId]) - @@map("singer_svs_engine_version") -} - -model SingerSvsEngine { - singer Singer @relation(fields: [singerId], references: [id]) - singerId String @map("singer_id") - svsEngine SvsEngine @relation(fields: [svsEngineId], references: [id]) - svsEngineId String @map("svs_engine_id") - - @@id([singerId, svsEngineId]) - @@map("singer_svs_engine") -} - -model Singer { - id String @id - name String? - avatarUrl String? @map("avatar_url") - /// [LocalizedField] - localizedNames Json? - singerSvsEngineVersions SingerSvsEngineVersion[] - singerSvsEngines SingerSvsEngine[] - voicebanks Voicebank[] - singerOfSongs SingerOfSong[] - createdAt DateTime @default(now()) @map("created_at") - updatedAt DateTime @updatedAt @map("updated_at") - - @@map("singer") -} - -model SvsEngine { - id String @id - name String - description String? - /// [LocalizedField] - localizedDescriptions Json? - svsEngineVersions SvsEngineVersion[] - singerSvsEngines SingerSvsEngine[] - performances SingerOfSong[] - createdAt DateTime @default(now()) @map("created_at") - updatedAt DateTime @updatedAt @map("updated_at") - - @@map("svs_engine") -} - -model SvsEngineVersion { - id String @id - versionString String @map("version_string") - singerSvsEngineVersions SingerSvsEngineVersion[] - svsEngine SvsEngine @relation(fields: [svsEngineId], references: [id]) - svsEngineId String @map("svs_engine_id") - singerOfSongs SingerOfSong[] - voicebanks Voicebank[] - createdAt DateTime @default(now()) @map("created_at") - updatedAt DateTime @updatedAt @map("updated_at") - - @@map("svs_engine_version") -} - -model Artist { - id String @id - name String? - /// [LocalizedField] - localizedNames Json? - description String? - /// [LocalizedField] - localizedDescriptions Json? - artistSongRoles ArtistRole[] - createdAt DateTime @default(now()) @map("created_at") - updatedAt DateTime @updatedAt @map("updated_at") - - @@map("artist") -} - -model ArtistRole { - id String @id - role String - /// [LocalizedField] - localizedRoles Json? - song Song @relation(fields: [songId], references: [id]) - songId String @map("song_id") - artist Artist @relation(fields: [artistId], references: [id]) - artistId String @map("artist_id") - createdAt DateTime @default(now()) @map("created_at") - updatedAt DateTime @updatedAt @map("updated_at") - - @@map("artist_role") -} +*/ \ No newline at end of file