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

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:
2026-03-25 01:43:32 +01:00
commit f1cff74d83
56 changed files with 9891 additions and 0 deletions

View 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');
}
}