├── .nvmrc ├── packages ├── server │ ├── .gitignore │ ├── src │ │ ├── prisma │ │ │ ├── migrations │ │ │ │ ├── migration_lock.toml │ │ │ │ └── 20220919003636_init │ │ │ │ │ └── migration.sql │ │ │ ├── generated │ │ │ │ └── prisma │ │ │ │ │ ├── enums.ts │ │ │ │ │ ├── models.ts │ │ │ │ │ ├── browser.ts │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── internal │ │ │ │ │ ├── prismaNamespaceBrowser.ts │ │ │ │ │ └── class.ts │ │ │ │ │ └── commonInputTypes.ts │ │ │ ├── seed.ts │ │ │ ├── schema.prisma │ │ │ └── client.ts │ │ ├── types │ │ │ └── index.ts │ │ ├── routes │ │ │ └── v1 │ │ │ │ ├── webhook.route.ts │ │ │ │ ├── index.ts │ │ │ │ ├── user.route.ts │ │ │ │ ├── preference.route.ts │ │ │ │ └── bangumi.route.ts │ │ ├── middlewares │ │ │ ├── authAdmin.moddleware.ts │ │ │ ├── githubWebhook.middleware.ts │ │ │ ├── auth.middleware.ts │ │ │ └── validator.middleware.ts │ │ ├── config.ts │ │ ├── app.ts │ │ ├── logger.ts │ │ ├── controllers │ │ │ ├── bangumiPreference.controller.ts │ │ │ ├── userPreference.controller.ts │ │ │ ├── bangumi.controller.ts │ │ │ └── user.controller.ts │ │ ├── models │ │ │ ├── token.model.ts │ │ │ ├── bangumiPreference.model.ts │ │ │ ├── userPreference.model.ts │ │ │ ├── user.model.ts │ │ │ └── bangumi.model.ts │ │ └── misc │ │ │ └── legacyDBMigrate.sql │ ├── tsconfig.json │ ├── prisma.config.ts │ ├── tsconfig.build.json │ ├── .env.example │ ├── package.json │ └── API.md ├── client │ ├── constants │ │ ├── user.ts │ │ ├── storage.ts │ │ ├── weekday.ts │ │ └── links.ts │ ├── pages │ │ ├── archive │ │ │ ├── [season].module.css │ │ │ ├── index.module.css │ │ │ ├── index.tsx │ │ │ └── [season].tsx │ │ ├── index.module.css │ │ ├── config │ │ │ ├── index.module.css │ │ │ └── index.tsx │ │ ├── _app.tsx │ │ ├── signup │ │ │ ├── index.module.css │ │ │ └── index.tsx │ │ ├── _document.tsx │ │ ├── login │ │ │ ├── index.module.css │ │ │ └── index.tsx │ │ ├── me │ │ │ ├── index.module.css │ │ │ └── index.tsx │ │ └── index.tsx │ ├── public │ │ ├── favicon.ico │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── mstile-150x150.png │ │ ├── apple-touch-icon.png │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-384x384.png │ │ ├── browserconfig.xml │ │ ├── manifest.json │ │ └── safari-pinned-tab.svg │ ├── .vscode │ │ └── settings.json │ ├── styles │ │ ├── variables.css │ │ ├── layout.css │ │ └── reset.css │ ├── components │ │ ├── common │ │ │ ├── Container.module.css │ │ │ ├── Footer.module.css │ │ │ ├── Container.tsx │ │ │ ├── Footer.tsx │ │ │ ├── Top.tsx │ │ │ ├── SearchInput.module.css │ │ │ ├── Top.module.css │ │ │ ├── Header.module.css │ │ │ ├── SearchInput.tsx │ │ │ └── Header.tsx │ │ ├── BangumiItemTable.module.css │ │ ├── layout │ │ │ └── Layout.tsx │ │ ├── BangumiLinkItem.tsx │ │ ├── WeekdayTab.module.css │ │ ├── WeekdayTab.tsx │ │ ├── Core.tsx │ │ ├── BangumiItemTable.tsx │ │ ├── BangumiItem.module.css │ │ └── BangumiItem.tsx │ ├── next-env.d.ts │ ├── eslint.config.js │ ├── types │ │ ├── custom.d.ts │ │ └── index.ts │ ├── utils │ │ ├── quarterToMonth.ts │ │ ├── formatSeason.ts │ │ ├── getBroadcastTimeString.ts │ │ ├── api.ts │ │ └── bangumiItemUtils.ts │ ├── images │ │ ├── clear.svg │ │ ├── close.svg │ │ ├── favorite-full.svg │ │ ├── user.svg │ │ ├── logout.svg │ │ └── favorite-empty.svg │ ├── models │ │ ├── bangumi.model.ts │ │ ├── user.model.ts │ │ ├── preference.model.ts │ │ └── preferenceLocal.model.ts │ ├── next.config.js │ ├── tsconfig.json │ ├── package.json │ └── contexts │ │ ├── userContext.tsx │ │ └── preferenceContext.tsx └── shared │ ├── src │ ├── types │ │ ├── User.interface.ts │ │ ├── Preference.interface.ts │ │ └── Bangumi.interface.ts │ └── index.ts │ ├── tsconfig.json │ ├── tsconfig.build.json │ └── package.json ├── .prettierignore ├── .dockerignore ├── .husky └── pre-commit ├── start.sh ├── tsconfig.json ├── .prettierrc ├── Dockerfile ├── tsconfig.build.json ├── .github └── workflows │ └── docker-image.yml ├── README.md ├── .editorconfig ├── .vscode └── launch.json ├── .gitignore ├── package.json └── eslint.config.js /.nvmrc: -------------------------------------------------------------------------------- 1 | v24.11.1 2 | -------------------------------------------------------------------------------- /packages/server/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | .run/ 3 | *.d.ts 4 | generated/ 5 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | **/node_modules 3 | **/stats.json 4 | .run 5 | .env 6 | -------------------------------------------------------------------------------- /packages/client/constants/user.ts: -------------------------------------------------------------------------------- 1 | export const PASSWORD_MIN_LENGTH = 8; 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /packages/client/pages/archive/[season].module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | margin: 12px auto; 3 | } 4 | -------------------------------------------------------------------------------- /packages/shared/src/types/User.interface.ts: -------------------------------------------------------------------------------- 1 | export interface User { 2 | id: string; 3 | email: string; 4 | } 5 | -------------------------------------------------------------------------------- /packages/shared/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wxt2005/bangumi-list-v3/HEAD/packages/client/public/favicon.ico -------------------------------------------------------------------------------- /packages/client/public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wxt2005/bangumi-list-v3/HEAD/packages/client/public/favicon-16x16.png -------------------------------------------------------------------------------- /packages/client/public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wxt2005/bangumi-list-v3/HEAD/packages/client/public/favicon-32x32.png -------------------------------------------------------------------------------- /packages/client/public/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wxt2005/bangumi-list-v3/HEAD/packages/client/public/mstile-150x150.png -------------------------------------------------------------------------------- /packages/client/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wxt2005/bangumi-list-v3/HEAD/packages/client/public/apple-touch-icon.png -------------------------------------------------------------------------------- /packages/client/public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wxt2005/bangumi-list-v3/HEAD/packages/client/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /packages/client/public/android-chrome-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wxt2005/bangumi-list-v3/HEAD/packages/client/public/android-chrome-384x384.png -------------------------------------------------------------------------------- /packages/client/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "../../node_modules/typescript/lib", 3 | "typescript.enablePromptUseWorkspaceTsdk": true 4 | } -------------------------------------------------------------------------------- /packages/shared/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './types/Bangumi.interface'; 2 | export * from './types/Preference.interface'; 3 | export * from './types/User.interface'; 4 | -------------------------------------------------------------------------------- /packages/server/src/prisma/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (i.e. Git) 3 | provider = "sqlite" -------------------------------------------------------------------------------- /packages/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "typeRoots": [ 5 | "./src/types", "node_modules/@types" 6 | ] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | HOST=127.0.0.1 PORT=3001 npm run start -w packages/server & 4 | HOST=127.0.0.1 PORT=3000 API_HOST=http://127.0.0.1:3001 npm run start -w packages/client & 5 | 6 | wait 7 | 8 | exit $? 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.build.json", 3 | "compilerOptions": { 4 | "baseUrl": "./packages", 5 | "skipLibCheck": true, 6 | "paths": { 7 | "bangumi-list-v3-*": ["*/src"] 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/client/styles/variables.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --color-theme-green: #1abc9c; 3 | --color-theme-blue: #34495e; 4 | --content-max-width: 1024px; 5 | --breakpoint-sm: 640px; 6 | --active-opacity: 0.6; 7 | --disable-opacity: 0.4; 8 | } 9 | -------------------------------------------------------------------------------- /packages/client/constants/storage.ts: -------------------------------------------------------------------------------- 1 | export const STORAGE_CREDENTIAL_NAME = 'bgmlist:credential'; 2 | 3 | export const STORAGE_COMMON_PREFERENCE_NAME = 'bmglist:preference:common'; 4 | 5 | export const STORAGE_BANGUMI_PREFERENCE_NAME = 'bgmlist:preference:bangumi'; 6 | -------------------------------------------------------------------------------- /packages/client/components/common/Container.module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | max-width: var(--content-max-width); 3 | margin: 0 auto; 4 | padding: 0 12px; 5 | } 6 | 7 | @media (min-width: var(--content-max-width)) { 8 | .root { 9 | padding: 0; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/client/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. 6 | -------------------------------------------------------------------------------- /packages/shared/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.build.json", 3 | "compilerOptions": { 4 | "composite": true, 5 | "declaration": true, 6 | "rootDir": "./src", 7 | "outDir": "./dist" 8 | }, 9 | "include": [ 10 | "src/**/*" 11 | ] 12 | } -------------------------------------------------------------------------------- /packages/client/constants/weekday.ts: -------------------------------------------------------------------------------- 1 | export const WEEKDAY_CN = [ 2 | '周日', 3 | '周一', 4 | '周二', 5 | '周三', 6 | '周四', 7 | '周五', 8 | '周六', 9 | ]; 10 | 11 | export const WEEKDAY_JP = [ 12 | '日曜', 13 | '月曜', 14 | '火曜', 15 | '水曜', 16 | '木曜', 17 | '金曜', 18 | '土曜', 19 | ]; 20 | -------------------------------------------------------------------------------- /packages/client/public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #1abc9c 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /packages/client/styles/layout.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 0; 3 | margin: 0; 4 | color: var(--color-theme-blue); 5 | font-family: system-ui ,sans-serif; 6 | min-width: 320px; 7 | } 8 | 9 | a:hover, 10 | a:active { 11 | color: var(--color-theme-green); 12 | } 13 | 14 | button { 15 | cursor: pointer; 16 | } 17 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "useTabs": false, 4 | "semi": true, 5 | "singleQuote": true, 6 | "quoteProps": "as-needed", 7 | "jsxSingleQuote": false, 8 | "trailingComma": "es5", 9 | "bracketSpacing": true, 10 | "bracketSameLine": false, 11 | "arrowParens": "always", 12 | "endOfLine": "lf" 13 | } -------------------------------------------------------------------------------- /packages/client/components/BangumiItemTable.module.css: -------------------------------------------------------------------------------- 1 | .content { 2 | background: #fff; 3 | } 4 | 5 | .item { 6 | width: 100%; 7 | } 8 | 9 | .item + .item { 10 | margin-top: 12px; 11 | } 12 | 13 | .empty { 14 | display: flex; 15 | height: 80px; 16 | justify-content: center; 17 | align-items: center; 18 | } 19 | -------------------------------------------------------------------------------- /packages/server/src/types/index.ts: -------------------------------------------------------------------------------- 1 | import { UserRole } from '../models/user.model'; 2 | 3 | export interface RequestUser { 4 | id: string; 5 | role: UserRole; 6 | token: string; 7 | } 8 | 9 | declare module 'express-serve-static-core' { 10 | export interface Request { 11 | user?: RequestUser; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/server/prisma.config.ts: -------------------------------------------------------------------------------- 1 | import 'dotenv/config'; 2 | import { defineConfig, env } from 'prisma/config'; 3 | 4 | export default defineConfig({ 5 | schema: './src/prisma/schema.prisma', 6 | datasource: { 7 | url: env('DATABASE_URL'), 8 | }, 9 | migrations: { 10 | seed: 'ts-node ./src/prisma/seed.ts', 11 | }, 12 | }); 13 | -------------------------------------------------------------------------------- /packages/client/eslint.config.js: -------------------------------------------------------------------------------- 1 | const nextPlugin = require('@next/eslint-plugin-next'); 2 | 3 | module.exports = [ 4 | { 5 | plugins: { 6 | '@next/next': nextPlugin, 7 | }, 8 | rules: { 9 | ...nextPlugin.configs.recommended.rules, 10 | ...nextPlugin.configs['core-web-vitals'].rules, 11 | }, 12 | }, 13 | ]; 14 | -------------------------------------------------------------------------------- /packages/client/types/custom.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '*.module.css' { 4 | const classes: { [key: string]: string }; 5 | export default classes; 6 | } 7 | 8 | declare module "*.svg" { 9 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 10 | const content: any; 11 | export default content; 12 | } 13 | -------------------------------------------------------------------------------- /packages/client/utils/quarterToMonth.ts: -------------------------------------------------------------------------------- 1 | export default function quarterToMonth(quarter: number): number { 2 | switch (quarter) { 3 | case 1: 4 | return 1; 5 | case 2: 6 | return 4; 7 | case 3: 8 | return 7; 9 | case 4: 10 | return 10; 11 | default: 12 | throw new TypeError('Unknown quarter'); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/server/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.build.json", 3 | "compilerOptions": { 4 | "composite": true, 5 | "declaration": true, 6 | "rootDir": "./src", 7 | "outDir": "./dist" 8 | }, 9 | "references": [{ 10 | "path": "../shared/tsconfig.build.json" 11 | }], 12 | "include": [ 13 | "src/**/*" 14 | ] 15 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM node:24.11.1-bookworm 4 | 5 | ARG GA_ID 6 | 7 | ENV TZ=Asia/Shanghai 8 | ENV NEXT_PUBLIC_GA_ID=${GA_ID} 9 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone 10 | 11 | WORKDIR /app 12 | COPY . . 13 | 14 | EXPOSE 3000 15 | 16 | RUN ./build.sh 17 | 18 | CMD [ "/bin/sh", "./start.sh" ] 19 | -------------------------------------------------------------------------------- /packages/client/components/layout/Layout.tsx: -------------------------------------------------------------------------------- 1 | import Header from '../common/Header'; 2 | import Footer from '../common/Footer'; 3 | 4 | export default function Layout({ 5 | children, 6 | }: { 7 | children: JSX.Element; 8 | }): JSX.Element { 9 | return ( 10 | <> 11 |
12 |
{children}
13 |