init: scaffold complet Sakuin — backend NestJS + frontend SvelteKit + CI/CD + deploy VPS
All checks were successful
CI/CD — Build & Deploy / Build & Deploy (push) Successful in 29s
All checks were successful
CI/CD — Build & Deploy / Build & Deploy (push) Successful in 29s
Backend: 5 modules (auth, user, work, list, health), AniList GraphQL proxy, SuperOAuth PKCE introspection, XP system, migrations TypeORM. Frontend: SvelteKit adapter-node, PWA manifest, dark theme, pages home/search/list/profile/callback. Infra: CI/CD Gitea vps-runner, Apache vhost SSL, pm2 sakuin-backend + sakuin-frontend, port 4002. License: BSL 1.1 (Apache 2.0 en 2028).
This commit is contained in:
69
backend/src/migrations/1711360000000-Init.ts
Normal file
69
backend/src/migrations/1711360000000-Init.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class Init1711360000000 implements MigrationInterface {
|
||||
name = 'Init1711360000000';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
CREATE TABLE users (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
superOauthId VARCHAR(255) NOT NULL,
|
||||
username VARCHAR(255) NOT NULL,
|
||||
avatarUrl VARCHAR(512) NULL,
|
||||
xp INT NOT NULL DEFAULT 0,
|
||||
level INT NOT NULL DEFAULT 1,
|
||||
createdAt DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
updatedAt DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
|
||||
UNIQUE INDEX IDX_users_superOauthId (superOauthId),
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB
|
||||
`);
|
||||
|
||||
await queryRunner.query(`
|
||||
CREATE TABLE works (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
anilistId INT NOT NULL,
|
||||
type ENUM('anime', 'manga') NOT NULL,
|
||||
titleRomaji VARCHAR(512) NOT NULL,
|
||||
titleEnglish VARCHAR(512) NULL,
|
||||
titleNative VARCHAR(512) NULL,
|
||||
posterUrl VARCHAR(1024) NULL,
|
||||
synopsis TEXT NULL,
|
||||
totalEpisodes INT NULL,
|
||||
totalChapters INT NULL,
|
||||
status ENUM('releasing', 'finished', 'not_yet_released', 'cancelled', 'hiatus') NULL,
|
||||
genres JSON NULL,
|
||||
cachedAt DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
UNIQUE INDEX IDX_works_anilistId (anilistId),
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB
|
||||
`);
|
||||
|
||||
await queryRunner.query(`
|
||||
CREATE TABLE user_works (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
userId INT NOT NULL,
|
||||
workId INT NOT NULL,
|
||||
status ENUM('watching', 'reading', 'completed', 'dropped', 'paused', 'plan_to') NOT NULL,
|
||||
progress INT NOT NULL DEFAULT 0,
|
||||
score DECIMAL(3,1) NULL,
|
||||
startedAt DATETIME NULL,
|
||||
completedAt DATETIME NULL,
|
||||
createdAt DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
updatedAt DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
|
||||
UNIQUE INDEX IDX_user_works_userId_workId (userId, workId),
|
||||
INDEX IDX_user_works_userId (userId),
|
||||
INDEX IDX_user_works_workId (workId),
|
||||
PRIMARY KEY (id),
|
||||
CONSTRAINT FK_user_works_userId FOREIGN KEY (userId) REFERENCES users(id) ON DELETE CASCADE,
|
||||
CONSTRAINT FK_user_works_workId FOREIGN KEY (workId) REFERENCES works(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB
|
||||
`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP TABLE IF EXISTS user_works');
|
||||
await queryRunner.query('DROP TABLE IF EXISTS works');
|
||||
await queryRunner.query('DROP TABLE IF EXISTS users');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user