prepping for our deploy2
This commit is contained in:
parent
02f31a628d
commit
aaf4355e36
23
services/our/.dockerignore
Normal file
23
services/our/.dockerignore
Normal file
@ -0,0 +1,23 @@
|
||||
node_modules
|
||||
|
||||
# Output
|
||||
.output
|
||||
.vercel
|
||||
.netlify
|
||||
.wrangler
|
||||
/.svelte-kit
|
||||
/build
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Env
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
|
||||
# Vite
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
171
services/our/.gitignore
vendored
Normal file
171
services/our/.gitignore
vendored
Normal file
@ -0,0 +1,171 @@
|
||||
generated
|
||||
|
||||
backup
|
||||
node_modules
|
||||
|
||||
# Output
|
||||
.output
|
||||
.vercel
|
||||
.netlify
|
||||
.wrangler
|
||||
/.svelte-kit
|
||||
/build
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Env
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
|
||||
# Vite
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/node
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=node
|
||||
|
||||
### Node ###
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
### Node Patch ###
|
||||
# Serverless Webpack directories
|
||||
.webpack/
|
||||
|
||||
# Optional stylelint cache
|
||||
|
||||
# SvelteKit build / generate output
|
||||
.svelte-kit
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/node
|
1
services/our/.npmrc
Normal file
1
services/our/.npmrc
Normal file
@ -0,0 +1 @@
|
||||
engine-strict=true
|
1
services/our/.nvmrc
Normal file
1
services/our/.nvmrc
Normal file
@ -0,0 +1 @@
|
||||
lts/jod
|
14
services/our/Caddyfile
Normal file
14
services/our/Caddyfile
Normal file
@ -0,0 +1,14 @@
|
||||
:80
|
||||
|
||||
## reverse_proxy /api/* nocodb:8080
|
||||
## reverse_proxy /download/* nocodb:8080
|
||||
|
||||
## twitch mock api server
|
||||
reverse_proxy /mock/* 172.17.0.1:8080
|
||||
|
||||
## fastify server
|
||||
reverse_proxy 172.17.0.1:3000
|
||||
|
||||
|
||||
## file_server
|
||||
## root * /srv
|
80
services/our/README.md
Normal file
80
services/our/README.md
Normal file
@ -0,0 +1,80 @@
|
||||
# futureporn
|
||||
|
||||
## projekt requirements
|
||||
|
||||
* [x] NO BUNDLER (esbuild/vite/webpack/parcel/swc/etc.). IF YOU REACH FOR A BUNDLER, YOU'RE OVERCOMPLICATING IT!
|
||||
* [x] NO JWT. IF YOU REACH FOR JWT, YOU'RE OVERCOMPLICATING IT!
|
||||
* [x] Uploads backend
|
||||
* [x] Uploads frontend
|
||||
* [x] backend task processor
|
||||
* [x] Vods
|
||||
* [x] HLS
|
||||
* [ ] Deploy
|
||||
* [ ] Write
|
||||
* [ ] Publish
|
||||
* [ ] Streams
|
||||
* [x] VTubers
|
||||
* [ ] Tags
|
||||
* [ ] Toys
|
||||
* [ ] Monetized affiliate links
|
||||
|
||||
## Tiers & Privs
|
||||
|
||||
* user - view, torrent, download
|
||||
* supporterTier1 - view, torrent, download, adfree, upload
|
||||
* supporterTier6 - view, torrent, download, adfree, upload, csv, sql
|
||||
|
||||
## troubleshooting
|
||||
|
||||
### keyv
|
||||
|
||||
We use tsup to convert ESM to CJS for graphile-worker.
|
||||
We're staying on version ^4 because that's the one that supports CJS. https://github.com/jaredwray/keyv/issues/1224
|
||||
|
||||
|
||||
### sharp
|
||||
|
||||
sharp is often a pain in the ass to install.
|
||||
|
||||
```
|
||||
[dev:serve] /home/cj/Documents/futureporn-monorepo/services/our/node_modules/.pnpm/sharp@0.32.6/node_modules/sharp/lib/sharp.js:37
|
||||
[dev:serve] throw new Error(help.join('\n'));
|
||||
[dev:serve] ^
|
||||
[dev:serve]
|
||||
[dev:serve] Error:
|
||||
[dev:serve] Something went wrong installing the "sharp" module
|
||||
[dev:serve]
|
||||
[dev:serve] Cannot find module '../build/Release/sharp-linux-x64.node'
|
||||
[dev:serve] Require stack:
|
||||
[dev:serve] - /home/cj/Documents/futureporn-monorepo/services/our/node_modules/.pnpm/sharp@0.32.6/node_modules/sharp/lib/sharp.js
|
||||
[dev:serve] - /home/cj/Documents/futureporn-monorepo/services/our/node_modules/.pnpm/sharp@0.32.6/node_modules/sharp/lib/constructor.js
|
||||
[dev:serve] - /home/cj/Documents/futureporn-monorepo/services/our/node_modules/.pnpm/sharp@0.32.6/node_modules/sharp/lib/index.js
|
||||
[dev:serve]
|
||||
[dev:serve] Possible solutions:
|
||||
[dev:serve] - Install with verbose logging and look for errors: "npm install --ignore-scripts=false --foreground-scripts --verbose sharp"
|
||||
[dev:serve] - Install for the current linux-x64 runtime: "npm install --platform=linux --arch=x64 sharp"
|
||||
[dev:serve] - Consult the installation documentation: https://sharp.pixelplumbing.com/install
|
||||
[dev:serve] at Object.<anonymous> (/home/cj/Documents/futureporn-monorepo/services/our/node_modules/.pnpm/sharp@0.32.6/node_modules/sharp/lib/sharp.js:37:9)
|
||||
[dev:serve] at Module._compile (node:internal/modules/cjs/loader:1469:14)
|
||||
[dev:serve] at Object.transformer (/home/cj/Documents/node_modules/.pnpm/tsx@4.15.7/node_modules/tsx/dist/register-BujtrvNV.cjs:2:1265)
|
||||
[dev:serve] at Module.load (node:internal/modules/cjs/loader:1288:32)
|
||||
[dev:serve] at Module._load (node:internal/modules/cjs/loader:1104:12)
|
||||
[dev:serve] at Module.require (node:internal/modules/cjs/loader:1311:19)
|
||||
[dev:serve] at require (node:internal/modules/helpers:179:18)
|
||||
[dev:serve] at Object.<anonymous> (/home/cj/Documents/futureporn-monorepo/services/our/node_modules/.pnpm/sharp@0.32.6/node_modules/sharp/lib/constructor.js:11:1)
|
||||
[dev:serve] at Module._compile (node:internal/modules/cjs/loader:1469:14)
|
||||
[dev:serve] at Object.transformer (/home/cj/Documents/node_modules/.pnpm/tsx@4.15.7/node_modules/tsx/dist/register-BujtrvNV.cjs:2:1265)
|
||||
[dev:serve]
|
||||
|
||||
```
|
||||
|
||||
|
||||
If you have trouble installing sharp, try ignoring the system's installed libvips.
|
||||
|
||||
SHARP_IGNORE_GLOBAL_LIBVIPS=1 npm install --ignore-scripts=false --foreground-scripts --verbose --platform=linux --arch=x64 sharp
|
||||
|
||||
SHARP_IGNORE_GLOBAL_LIBVIPS=1 npm install --ignore-scripts=false --foreground-scripts --verbose sharp
|
||||
|
||||
Actually, better advice is to probably **remove libvips from the system**. This way, a compatible libvips is always pulled during `npm install`.
|
||||
|
||||
Annoyingly, it might be necessary to re-install sharp after every new npm package, even if said package is unrelated to sharp.
|
15
services/our/crontab
Normal file
15
services/our/crontab
Normal file
@ -0,0 +1,15 @@
|
||||
# ┌───────────── UTC minute (0 - 59)
|
||||
# │ ┌───────────── UTC hour (0 - 23)
|
||||
# │ │ ┌───────────── UTC day of the month (1 - 31)
|
||||
# │ │ │ ┌───────────── UTC month (1 - 12)
|
||||
# │ │ │ │ ┌───────────── UTC day of the week (0 - 6) (Sunday to Saturday)
|
||||
# │ │ │ │ │ ┌───────────── task (identifier) to schedule
|
||||
# │ │ │ │ │ │ ┌────────── optional scheduling options
|
||||
# │ │ │ │ │ │ │ ┌────── optional payload to merge
|
||||
# │ │ │ │ │ │ │ │
|
||||
# │ │ │ │ │ │ │ │
|
||||
# * * * * * task ?opts {payload}
|
||||
|
||||
# * * * * * hello {name: "MY DUDE"}
|
||||
* * * * * findWork
|
||||
# * */1 * * * cleanup # faulty-- deletes files too soon, files that were created just a minute ago.
|
62
services/our/docker-compose.yml
Normal file
62
services/our/docker-compose.yml
Normal file
@ -0,0 +1,62 @@
|
||||
|
||||
services:
|
||||
|
||||
|
||||
# caddy:
|
||||
# image: caddy:alpine
|
||||
# ports:
|
||||
# - "8081:80"
|
||||
# volumes:
|
||||
# - ./public:/srv
|
||||
# - ./Caddyfile:/etc/caddy/Caddyfile
|
||||
|
||||
|
||||
postgres:
|
||||
container_name: our-postgres
|
||||
image: postgres:17
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: ${DB_USER}
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
POSTGRES_DB: ${DB_NAME}
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD", "pg_isready", "-U", "postgres"]
|
||||
interval: 10s
|
||||
retries: 5
|
||||
start_period: 10s
|
||||
timeout: 10s
|
||||
|
||||
pgweb:
|
||||
container_name: out-pgweb
|
||||
image: sosedoff/pgweb
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
PGWEB_DATABASE_URL: postgres://${DB_USER}:${DB_PASSWORD}@postgres:5432/${DB_NAME}?sslmode=disable
|
||||
ports:
|
||||
- "8091:8081"
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8081"]
|
||||
interval: 10s
|
||||
retries: 5
|
||||
start_period: 10s
|
||||
timeout: 10s
|
||||
|
||||
# pgadmin:
|
||||
# image: dpage/pgadmin4:latest
|
||||
# environment:
|
||||
# PGADMIN_LISTEN_PORT: 5050
|
||||
# PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL}
|
||||
# PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD}
|
||||
# PGADMIN_DISABLE_POSTFIX: true
|
||||
# ports:
|
||||
# - "5050:5050"
|
||||
|
||||
|
||||
volumes:
|
||||
pgdata:
|
22
services/our/graphile.config.ts
Normal file
22
services/our/graphile.config.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { WorkerPreset } from "graphile-worker"
|
||||
import { env } from "./src/config/env"
|
||||
import path from 'node:path'
|
||||
const __dirname = import.meta.dirname;
|
||||
|
||||
const preset: GraphileConfig.Preset = {
|
||||
extends: [WorkerPreset],
|
||||
worker: {
|
||||
connectionString: env.DATABASE_URL,
|
||||
maxPoolSize: 10,
|
||||
pollInterval: 2000,
|
||||
preparedStatements: true,
|
||||
schema: "graphile_worker",
|
||||
crontabFile: "crontab",
|
||||
concurrentJobs: 1,
|
||||
fileExtensions: [".cjs"],
|
||||
taskDirectory: path.join(__dirname, 'dist', 'tasks')
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
export default preset
|
12764
services/our/package-lock.json
generated
Normal file
12764
services/our/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
112
services/our/package.json
Normal file
112
services/our/package.json
Normal file
@ -0,0 +1,112 @@
|
||||
{
|
||||
"name": "futureporn",
|
||||
"private": true,
|
||||
"version": "2.0.1",
|
||||
"type": "module",
|
||||
"overrides": {
|
||||
"@sinclair/typebox": "0.34.0"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "concurrently npm:dev:serve npm:dev:build npm:dev:worker",
|
||||
"dev:serve": "tsx watch ./src/index.ts",
|
||||
"start": "node ./src/index.ts",
|
||||
"preview": "vite preview",
|
||||
"prepare": "svelte-kit sync || echo ''",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"dev:worker": "tsx watch ./src/worker.ts",
|
||||
"dev:build": "chokidar 'src/**/*.{js,ts}' -c tsup --clean",
|
||||
"build": "tsup --clean",
|
||||
"lint": "eslint .",
|
||||
"postinstall": "npm install --platform=linux --arch=x64 sharp"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/compat": "^1.2.5",
|
||||
"@eslint/js": "^9.18.0",
|
||||
"@fontsource/fira-mono": "^5.0.0",
|
||||
"@neoconfetti/svelte": "^2.0.0",
|
||||
"@sveltejs/adapter-auto": "^6.0.0",
|
||||
"@sveltejs/kit": "^2.16.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
||||
"eslint": "^9.18.0",
|
||||
"eslint-plugin-svelte": "^3.0.0",
|
||||
"globals": "^16.0.0",
|
||||
"nodemon": "^3.1.10",
|
||||
"prisma": "6.8.2",
|
||||
"svelte": "^5.25.0",
|
||||
"svelte-check": "^4.0.0",
|
||||
"typescript": "^5.0.0",
|
||||
"typescript-eslint": "^8.20.0",
|
||||
"vite": "^6.2.6"
|
||||
},
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
"esbuild"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@auth0/auth0-spa-js": "^2.1.3",
|
||||
"@aws-sdk/client-s3": "3.726.1",
|
||||
"@aws-sdk/s3-request-presigner": "^3.824.0",
|
||||
"@bogeychan/elysia-logger": "^0.1.8",
|
||||
"@dotenvx/dotenvx": "^1.44.1",
|
||||
"@elysiajs/static": "^1.3.0",
|
||||
"@elysiajs/swagger": "^1.3.0",
|
||||
"@fastify/flash": "^6.0.3",
|
||||
"@fastify/formbody": "^8.0.2",
|
||||
"@fastify/multipart": "^9.0.3",
|
||||
"@fastify/oauth2": "^8.1.2",
|
||||
"@fastify/secure-session": "^8.2.0",
|
||||
"@fastify/static": "^8.1.1",
|
||||
"@fastify/swagger": "^9.5.1",
|
||||
"@fastify/swagger-ui": "^5.2.3",
|
||||
"@fastify/view": "^11.1.0",
|
||||
"@imgly/background-removal-node": "^1.4.5",
|
||||
"@imqueue/pg-pubsub": "^1.10.0",
|
||||
"@keyv/postgres": "^1.4.11",
|
||||
"@paralleldrive/cuid2": "^2.2.2",
|
||||
"@prisma/client": "6.8.2",
|
||||
"@prisma/extension-accelerate": "^1.3.0",
|
||||
"@types/node": "^22.15.17",
|
||||
"@types/node-fetch": "^2.6.12",
|
||||
"arctic": "^3.6.0",
|
||||
"axios": "^1.9.0",
|
||||
"cache-manager": "^7.0.0",
|
||||
"chokidar-cli": "^3.0.0",
|
||||
"concurrently": "^9.1.2",
|
||||
"date-fns": "^4.1.0",
|
||||
"drizzle-orm": "^0.43.1",
|
||||
"drizzle-typebox": "^0.3.2",
|
||||
"ejs": "^3.1.10",
|
||||
"elysia": "^1.2.25",
|
||||
"elysia-clerk": "^0.9.10",
|
||||
"elysia-connect-middleware": "^0.0.5",
|
||||
"elysia-oauth2": "^2.0.0",
|
||||
"fastify": "^5.3.2",
|
||||
"fastify-plugin": "^5.0.1",
|
||||
"fastify-sse-v2": "^4.2.1",
|
||||
"form-data": "^4.0.2",
|
||||
"fs-extra": "^11.3.0",
|
||||
"graphile-worker": "^0.16.6",
|
||||
"handlebars": "4.7.8",
|
||||
"jdenticon": "^3.3.0",
|
||||
"keyv": "^4.5.4",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mime-types": "^3.0.1",
|
||||
"nanoid": "^5.1.5",
|
||||
"nocodb-sdk": "^0.263.0",
|
||||
"node-fetch": "^3.3.2",
|
||||
"rate-limiter-flexible": "^7.1.1",
|
||||
"rimraf": "6.0.1",
|
||||
"sharp": "^0.34.2",
|
||||
"slugify": "^1.6.6",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsup": "^8.5.0",
|
||||
"vitest": "^3.2.1",
|
||||
"zod": "^3.25.49"
|
||||
},
|
||||
"packageManager": "pnpm@10.8.1+sha512.c50088ba998c67b8ca8c99df8a5e02fd2ae2e2b29aaf238feaa9e124248d3f48f9fb6db2424949ff901cffbb5e0f0cc1ad6aedb602cd29450751d11c35023677",
|
||||
"prisma": {
|
||||
"seed": "tsx prisma/seed.ts"
|
||||
}
|
||||
}
|
8617
services/our/pnpm-lock.yaml
generated
Normal file
8617
services/our/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
113
services/our/prisma/migrations/20250604010204_init/migration.sql
Normal file
113
services/our/prisma/migrations/20250604010204_init/migration.sql
Normal file
@ -0,0 +1,113 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "User" (
|
||||
"id" TEXT NOT NULL,
|
||||
"patreonId" TEXT NOT NULL,
|
||||
"patreonFullName" TEXT,
|
||||
"imageUrl" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "RateLimiterFlexible" (
|
||||
"key" TEXT NOT NULL,
|
||||
"points" INTEGER NOT NULL,
|
||||
"expire" TIMESTAMP(3),
|
||||
|
||||
CONSTRAINT "RateLimiterFlexible_pkey" PRIMARY KEY ("key")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "stream_entity" (
|
||||
"id" TEXT NOT NULL,
|
||||
"date" TIMESTAMP(3) NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"announcementUrl" VARCHAR(2048),
|
||||
|
||||
CONSTRAINT "stream_entity_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Vod" (
|
||||
"id" TEXT NOT NULL,
|
||||
"streamId" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "Vod_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Upload" (
|
||||
"id" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"vodId" TEXT,
|
||||
"fileKeys" TEXT[],
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Upload_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Vtuber" (
|
||||
"id" TEXT NOT NULL,
|
||||
"image" TEXT,
|
||||
"slug" TEXT,
|
||||
"displayName" TEXT,
|
||||
"chaturbate" TEXT,
|
||||
"twitter" TEXT,
|
||||
"patreon" TEXT,
|
||||
"twitch" TEXT,
|
||||
"tiktok" TEXT,
|
||||
"onlyfans" TEXT,
|
||||
"youtube" TEXT,
|
||||
"linktree" TEXT,
|
||||
"carrd" TEXT,
|
||||
"fansly" TEXT,
|
||||
"pornhub" TEXT,
|
||||
"discord" TEXT,
|
||||
"reddit" TEXT,
|
||||
"throne" TEXT,
|
||||
"instagram" TEXT,
|
||||
"facebook" TEXT,
|
||||
"merch" TEXT,
|
||||
"description" TEXT,
|
||||
"themeColor" TEXT,
|
||||
"fanslyId" TEXT,
|
||||
"chaturbateId" TEXT,
|
||||
"twitterId" TEXT,
|
||||
|
||||
CONSTRAINT "Vtuber_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "_VodToVtuber" (
|
||||
"A" TEXT NOT NULL,
|
||||
"B" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "_VodToVtuber_AB_pkey" PRIMARY KEY ("A","B")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "User_patreonId_key" ON "User"("patreonId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Upload_vodId_key" ON "Upload"("vodId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "_VodToVtuber_B_index" ON "_VodToVtuber"("B");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Vod" ADD CONSTRAINT "Vod_streamId_fkey" FOREIGN KEY ("streamId") REFERENCES "stream_entity"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Upload" ADD CONSTRAINT "Upload_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Upload" ADD CONSTRAINT "Upload_vodId_fkey" FOREIGN KEY ("vodId") REFERENCES "Vod"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_VodToVtuber" ADD CONSTRAINT "_VodToVtuber_A_fkey" FOREIGN KEY ("A") REFERENCES "Vod"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_VodToVtuber" ADD CONSTRAINT "_VodToVtuber_B_fkey" FOREIGN KEY ("B") REFERENCES "Vtuber"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Upload" ADD COLUMN "hlsPlaylist" TEXT;
|
@ -0,0 +1,30 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Upload" ADD COLUMN "thumbnail" TEXT;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Role" (
|
||||
"id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "Role_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "_UserRoles" (
|
||||
"A" TEXT NOT NULL,
|
||||
"B" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "_UserRoles_AB_pkey" PRIMARY KEY ("A","B")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Role_name_key" ON "Role"("name");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "_UserRoles_B_index" ON "_UserRoles"("B");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_UserRoles" ADD CONSTRAINT "_UserRoles_A_fkey" FOREIGN KEY ("A") REFERENCES "Role"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_UserRoles" ADD CONSTRAINT "_UserRoles_B_fkey" FOREIGN KEY ("B") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the `_UserRoles` table. If the table is not empty, all the data it contains will be lost.
|
||||
|
||||
*/
|
||||
-- CreateEnum
|
||||
CREATE TYPE "RoleName" AS ENUM ('ADMIN', 'USER', 'MODERATOR', 'SUPPORTER_LEVEL_1', 'SUPPORTER_LEVEL_2', 'SUPPORTER_LEVEL_3', 'SUPPORTER_LEVEL_4', 'SUPPORTER_LEVEL_5', 'SUPPORTER_LEVEL_6');
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "_UserRoles" DROP CONSTRAINT "_UserRoles_A_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "_UserRoles" DROP CONSTRAINT "_UserRoles_B_fkey";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "User" ALTER COLUMN "imageUrl" DROP NOT NULL;
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE "_UserRoles";
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "_RoleToUser" (
|
||||
"A" TEXT NOT NULL,
|
||||
"B" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "_RoleToUser_AB_pkey" PRIMARY KEY ("A","B")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "_RoleToUser_B_index" ON "_RoleToUser"("B");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_RoleToUser" ADD CONSTRAINT "_RoleToUser_A_fkey" FOREIGN KEY ("A") REFERENCES "Role"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_RoleToUser" ADD CONSTRAINT "_RoleToUser_B_fkey" FOREIGN KEY ("B") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
@ -0,0 +1,17 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- The values [SUPPORTER_LEVEL_1,SUPPORTER_LEVEL_2,SUPPORTER_LEVEL_3,SUPPORTER_LEVEL_4,SUPPORTER_LEVEL_5,SUPPORTER_LEVEL_6] on the enum `RoleName` will be removed. If these variants are still used in the database, this will fail.
|
||||
|
||||
*/
|
||||
-- AlterEnum
|
||||
BEGIN;
|
||||
CREATE TYPE "RoleName_new" AS ENUM ('USER', 'SUPPORTER_TIER_1', 'SUPPORTER_TIER_2', 'SUPPORTER_TIER_3', 'SUPPORTER_TIER_4', 'SUPPORTER_TIER_5', 'SUPPORTER_TIER_6', 'MODERATOR', 'ADMIN');
|
||||
ALTER TYPE "RoleName" RENAME TO "RoleName_old";
|
||||
ALTER TYPE "RoleName_new" RENAME TO "RoleName";
|
||||
DROP TYPE "RoleName_old";
|
||||
COMMIT;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "Upload" ADD COLUMN "notes" TEXT,
|
||||
ADD COLUMN "streamDate" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Upload" ALTER COLUMN "streamDate" DROP DEFAULT;
|
@ -0,0 +1,19 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Vtuber" ADD COLUMN "uploadId" TEXT;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "_UploadToVtuber" (
|
||||
"A" TEXT NOT NULL,
|
||||
"B" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "_UploadToVtuber_AB_pkey" PRIMARY KEY ("A","B")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "_UploadToVtuber_B_index" ON "_UploadToVtuber"("B");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_UploadToVtuber" ADD CONSTRAINT "_UploadToVtuber_A_fkey" FOREIGN KEY ("A") REFERENCES "Upload"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_UploadToVtuber" ADD CONSTRAINT "_UploadToVtuber_B_fkey" FOREIGN KEY ("B") REFERENCES "Vtuber"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
@ -0,0 +1,5 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "UploadStatus" AS ENUM ('PENDING', 'APPROVED', 'REJECTED');
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "Upload" ADD COLUMN "status" "UploadStatus" NOT NULL DEFAULT 'PENDING';
|
@ -0,0 +1,19 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- The values [PENDING,APPROVED,REJECTED] on the enum `UploadStatus` will be removed. If these variants are still used in the database, this will fail.
|
||||
|
||||
*/
|
||||
-- AlterEnum
|
||||
BEGIN;
|
||||
CREATE TYPE "UploadStatus_new" AS ENUM ('pending', 'approved', 'rejected');
|
||||
ALTER TABLE "Upload" ALTER COLUMN "status" DROP DEFAULT;
|
||||
ALTER TABLE "Upload" ALTER COLUMN "status" TYPE "UploadStatus_new" USING ("status"::text::"UploadStatus_new");
|
||||
ALTER TYPE "UploadStatus" RENAME TO "UploadStatus_old";
|
||||
ALTER TYPE "UploadStatus_new" RENAME TO "UploadStatus";
|
||||
DROP TYPE "UploadStatus_old";
|
||||
ALTER TABLE "Upload" ALTER COLUMN "status" SET DEFAULT 'pending';
|
||||
COMMIT;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "Upload" ALTER COLUMN "status" SET DEFAULT 'pending';
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- The values [USER,SUPPORTER_TIER_1,SUPPORTER_TIER_2,SUPPORTER_TIER_3,SUPPORTER_TIER_4,SUPPORTER_TIER_5,SUPPORTER_TIER_6,MODERATOR,ADMIN] on the enum `RoleName` will be removed. If these variants are still used in the database, this will fail.
|
||||
- You are about to drop the column `uploadId` on the `Vtuber` table. All the data in the column will be lost.
|
||||
- You are about to drop the `Upload` table. If the table is not empty, all the data it contains will be lost.
|
||||
- You are about to drop the `_UploadToVtuber` table. If the table is not empty, all the data it contains will be lost.
|
||||
- Added the required column `streamDate` to the `Vod` table without a default value. This is not possible if the table is not empty.
|
||||
- Added the required column `updatedAt` to the `Vod` table without a default value. This is not possible if the table is not empty.
|
||||
- Added the required column `uploaderId` to the `Vod` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- CreateEnum
|
||||
CREATE TYPE "VodStatus" AS ENUM ('pending', 'approved', 'rejected', 'processing', 'processed');
|
||||
|
||||
-- AlterEnum
|
||||
BEGIN;
|
||||
CREATE TYPE "RoleName_new" AS ENUM ('user', 'supporterTier1', 'supporterTier2', 'supporterTier3', 'supporterTier4', 'supporterTier5', 'supporterTier6', 'moderator', 'admin');
|
||||
ALTER TYPE "RoleName" RENAME TO "RoleName_old";
|
||||
ALTER TYPE "RoleName_new" RENAME TO "RoleName";
|
||||
DROP TYPE "RoleName_old";
|
||||
COMMIT;
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "Upload" DROP CONSTRAINT "Upload_userId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "Upload" DROP CONSTRAINT "Upload_vodId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "_UploadToVtuber" DROP CONSTRAINT "_UploadToVtuber_A_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "_UploadToVtuber" DROP CONSTRAINT "_UploadToVtuber_B_fkey";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "Vod" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
ADD COLUMN "hlsPlaylist" TEXT,
|
||||
ADD COLUMN "notes" TEXT,
|
||||
ADD COLUMN "segmentKeys" JSONB,
|
||||
ADD COLUMN "status" "VodStatus" NOT NULL DEFAULT 'pending',
|
||||
ADD COLUMN "streamDate" TIMESTAMP(3) NOT NULL,
|
||||
ADD COLUMN "thumbnail" TEXT,
|
||||
ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
ADD COLUMN "uploaderId" TEXT NOT NULL;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "Vtuber" DROP COLUMN "uploadId";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "stream_entity" ALTER COLUMN "announcementUrl" SET DATA TYPE TEXT;
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE "Upload";
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE "_UploadToVtuber";
|
||||
|
||||
-- DropEnum
|
||||
DROP TYPE "UploadStatus";
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Vod" ADD CONSTRAINT "Vod_uploaderId_fkey" FOREIGN KEY ("uploaderId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
@ -0,0 +1,8 @@
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "Vod" DROP CONSTRAINT "Vod_streamId_fkey";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "Vod" ALTER COLUMN "streamId" DROP NOT NULL;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Vod" ADD CONSTRAINT "Vod_streamId_fkey" FOREIGN KEY ("streamId") REFERENCES "stream_entity"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Vod" ADD COLUMN "sourceVideo" TEXT;
|
@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Vod" ADD COLUMN "asrVtt" TEXT;
|
@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Vod" ADD COLUMN "sha256sum" TEXT;
|
@ -0,0 +1,9 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- Added the required column `updatedAt` to the `Vtuber` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "Vtuber" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL;
|
@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Vod" ADD COLUMN "cidv1" TEXT;
|
@ -0,0 +1,11 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- Added the required column `uploaderId` to the `Vtuber` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "Vtuber" ADD COLUMN "uploaderId" TEXT NOT NULL;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Vtuber" ADD CONSTRAINT "Vtuber_uploaderId_fkey" FOREIGN KEY ("uploaderId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
@ -0,0 +1,2 @@
|
||||
-- AlterEnum
|
||||
ALTER TYPE "VodStatus" ADD VALUE 'ordering';
|
3
services/our/prisma/migrations/migration_lock.toml
Normal file
3
services/our/prisma/migrations/migration_lock.toml
Normal file
@ -0,0 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (e.g., Git)
|
||||
provider = "postgresql"
|
125
services/our/prisma/schema.prisma
Normal file
125
services/our/prisma/schema.prisma
Normal file
@ -0,0 +1,125 @@
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
output = "../generated/prisma"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(uuid())
|
||||
patreonId String @unique
|
||||
patreonFullName String?
|
||||
imageUrl String?
|
||||
roles Role[]
|
||||
vods Vod[]
|
||||
Vtuber Vtuber[]
|
||||
}
|
||||
|
||||
enum RoleName {
|
||||
user
|
||||
supporterTier1
|
||||
supporterTier2
|
||||
supporterTier3
|
||||
supporterTier4
|
||||
supporterTier5
|
||||
supporterTier6
|
||||
moderator
|
||||
admin
|
||||
}
|
||||
|
||||
model Role {
|
||||
id String @id @default(cuid())
|
||||
name String @unique
|
||||
users User[]
|
||||
}
|
||||
|
||||
model RateLimiterFlexible {
|
||||
key String @id
|
||||
points Int
|
||||
expire DateTime?
|
||||
}
|
||||
|
||||
model Stream {
|
||||
id String @id @default(cuid(2))
|
||||
date DateTime
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
announcementUrl String?
|
||||
|
||||
vods Vod[]
|
||||
|
||||
@@map("stream_entity")
|
||||
}
|
||||
|
||||
enum VodStatus {
|
||||
ordering
|
||||
pending
|
||||
approved
|
||||
rejected
|
||||
processing
|
||||
processed
|
||||
}
|
||||
|
||||
model Vod {
|
||||
id String @id @default(cuid(2))
|
||||
streamId String?
|
||||
stream Stream? @relation(fields: [streamId], references: [id])
|
||||
uploaderId String // previously in Upload
|
||||
uploader User @relation(fields: [uploaderId], references: [id])
|
||||
|
||||
streamDate DateTime
|
||||
notes String?
|
||||
segmentKeys Json?
|
||||
sourceVideo String?
|
||||
hlsPlaylist String?
|
||||
thumbnail String?
|
||||
asrVtt String?
|
||||
status VodStatus @default(pending)
|
||||
sha256sum String?
|
||||
cidv1 String?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
vtubers Vtuber[]
|
||||
}
|
||||
|
||||
model Vtuber {
|
||||
id String @id @default(cuid(2))
|
||||
image String?
|
||||
slug String?
|
||||
displayName String?
|
||||
chaturbate String?
|
||||
twitter String?
|
||||
patreon String?
|
||||
twitch String?
|
||||
tiktok String?
|
||||
onlyfans String?
|
||||
youtube String?
|
||||
linktree String?
|
||||
carrd String?
|
||||
fansly String?
|
||||
pornhub String?
|
||||
discord String?
|
||||
reddit String?
|
||||
throne String?
|
||||
instagram String?
|
||||
facebook String?
|
||||
merch String?
|
||||
description String?
|
||||
themeColor String?
|
||||
|
||||
fanslyId String?
|
||||
chaturbateId String?
|
||||
twitterId String?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
vods Vod[]
|
||||
uploaderId String
|
||||
uploader User @relation(fields: [uploaderId], references: [id])
|
||||
}
|
91
services/our/prisma/seed.ts
Normal file
91
services/our/prisma/seed.ts
Normal file
@ -0,0 +1,91 @@
|
||||
import { randomInt } from 'crypto';
|
||||
import { PrismaClient } from '../generated/prisma';
|
||||
import { withAccelerate } from '@prisma/extension-accelerate';
|
||||
|
||||
const prisma = new PrismaClient().$extends(withAccelerate());
|
||||
|
||||
const statuses = ['pending', 'approved', 'rejected'] as const;
|
||||
|
||||
function getRandomStatus() {
|
||||
return statuses[Math.floor(Math.random() * statuses.length)];
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('🌱 Seeding database...');
|
||||
|
||||
const length = 9;
|
||||
|
||||
// Create Roles
|
||||
await Promise.all([
|
||||
prisma.role.create({ data: { name: 'user' } }),
|
||||
prisma.role.create({ data: { name: 'admin' } }),
|
||||
prisma.role.create({ data: { name: 'supporterTier1' } }),
|
||||
prisma.role.create({ data: { name: 'supporterTier2' } }),
|
||||
prisma.role.create({ data: { name: 'supporterTier3' } }),
|
||||
prisma.role.create({ data: { name: 'supporterTier4' } }),
|
||||
prisma.role.create({ data: { name: 'supporterTier5' } }),
|
||||
prisma.role.create({ data: { name: 'supporterTier6' } }),
|
||||
]);
|
||||
|
||||
// Create Users
|
||||
const users = await Promise.all(
|
||||
Array.from({ length }).map((_, i) =>
|
||||
prisma.user.create({
|
||||
data: {
|
||||
patreonFullName: `User ${i + 1}`,
|
||||
patreonId: `${randomInt(9558925894)}`,
|
||||
imageUrl: 'https://placehold.co/48',
|
||||
},
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// Create Vtubers
|
||||
const vtubers = await Promise.all(
|
||||
['Alpha', 'Bravo', 'Charlie'].map((name) =>
|
||||
prisma.vtuber.create({
|
||||
data: {
|
||||
displayName: name,
|
||||
slug: name.toLowerCase(),
|
||||
},
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// Create Streams and VODs (with embedded upload data)
|
||||
await Promise.all(
|
||||
users.map((user, i) =>
|
||||
prisma.vod.create({
|
||||
data: {
|
||||
uploader: { connect: { id: user.id } },
|
||||
segmentKeys: [`vod${i + 1}_part1.mp4`, `vod${i + 1}_part2.mp4`],
|
||||
streamDate: new Date(),
|
||||
notes: `Seeded VOD ${i + 1}`,
|
||||
status: getRandomStatus(),
|
||||
hlsPlaylist: `https://cdn.example.com/hls/vod${i + 1}/index.m3u8`,
|
||||
thumbnail: `https://placehold.co/320x180?text=VOD${i + 1}`,
|
||||
vtubers: {
|
||||
connect: [{ id: vtubers[i % vtubers.length].id }],
|
||||
},
|
||||
stream: {
|
||||
create: {
|
||||
date: new Date(),
|
||||
announcementUrl: `https://example.com/announcement/${i + 1}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
console.log(`✅ Seed complete with ${length} users and VODs (with vtubers)!`);
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((e) => {
|
||||
console.error('❌ Seed error:', e);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(async () => {
|
||||
await prisma.$disconnect();
|
||||
});
|
181
services/our/src/app.ts
Normal file
181
services/our/src/app.ts
Normal file
@ -0,0 +1,181 @@
|
||||
import Fastify from 'fastify'
|
||||
import prismaPlugin from './plugins/prisma'
|
||||
import vodsRoutes from './plugins/vods'
|
||||
import uploadsRoutes from './plugins/uploads'
|
||||
import vtubersRoutes from './plugins/vtubers'
|
||||
import usersRoutes from './plugins/users'
|
||||
import indexRoutes from './plugins/index'
|
||||
import streamsRoutes from './plugins/streams'
|
||||
import hls from './plugins/hls.ts'
|
||||
import fastifyStatic from '@fastify/static'
|
||||
import fastifySecureSession from '@fastify/secure-session'
|
||||
import path, { basename } from 'node:path'
|
||||
// import fastifyMultipart from '@fastify/multipart'
|
||||
import fastifyFormbody from '@fastify/formbody'
|
||||
import fastifyView from "@fastify/view"
|
||||
import { env } from './config/env'
|
||||
import { constants } from './config/constants'
|
||||
import authRoutes from './plugins/auth'
|
||||
import Handlebars from 'handlebars'
|
||||
import graphileWorker from './plugins/graphileWorker'
|
||||
import fastifySwagger from "@fastify/swagger";
|
||||
import fastifySwaggerUi from "@fastify/swagger-ui";
|
||||
import { join } from 'node:path'
|
||||
import { format } from 'date-fns'
|
||||
import * as jdenticon from 'jdenticon'
|
||||
import { Role } from '../generated/prisma'
|
||||
import fastifyFlash from '@fastify/flash'
|
||||
import { isModerator, hasRole } from './utils/privs'
|
||||
import { signUrl } from './utils/cdn'
|
||||
import { extractBasePath } from './utils/filesystem'
|
||||
|
||||
export function buildApp() {
|
||||
const app = Fastify()
|
||||
|
||||
Handlebars.registerHelper('formatDate', function (dateString) {
|
||||
if (!dateString) return ''
|
||||
return format(new Date(dateString), 'yyyy-MM-dd')
|
||||
})
|
||||
Handlebars.registerHelper('identicon', function (str, size = 48) {
|
||||
return jdenticon.toSvg(str, size)
|
||||
})
|
||||
Handlebars.registerHelper('safeJson', function (context) {
|
||||
return new Handlebars.SafeString(JSON.stringify(context));
|
||||
});
|
||||
Handlebars.registerHelper('json', function (context) {
|
||||
return JSON.stringify(context)
|
||||
})
|
||||
Handlebars.registerHelper('patron', function (user) {
|
||||
if (!user.roles) {
|
||||
throw new Error(
|
||||
'patron hbs helper was called without roles. This usually means you forgot to include roles relationship in the query.'
|
||||
);
|
||||
}
|
||||
return user.roles.some((r: Role) => r.name.startsWith('supporter'));
|
||||
});
|
||||
Handlebars.registerHelper('notEqual', function (a, b) {
|
||||
return a !== b;
|
||||
});
|
||||
Handlebars.registerHelper('isEqual', function (a, b) {
|
||||
// console.log(`isEqual a=${a} b=${b}`)
|
||||
return a == b
|
||||
});
|
||||
Handlebars.registerHelper('isModerator', function (user) {
|
||||
return isModerator(user)
|
||||
})
|
||||
Handlebars.registerHelper('hasRole', hasRole)
|
||||
Handlebars.registerHelper('breaklines', function (text) {
|
||||
text = Handlebars.Utils.escapeExpression(text);
|
||||
text = text.replace(/(\r\n|\n|\r)/gm, '<br>');
|
||||
return new Handlebars.SafeString(text);
|
||||
});
|
||||
Handlebars.registerHelper('getCdnUrl', function (s3Key) {
|
||||
return signUrl(`${env.CDN_ORIGIN}/${s3Key}`, {
|
||||
securityKey: env.CDN_TOKEN_SECRET,
|
||||
expirationTime: constants.timeUnits.sevenDaysInSeconds,
|
||||
})
|
||||
})
|
||||
/**
|
||||
* @see https://github.com/video-dev/hls.js/issues/2152
|
||||
*/
|
||||
Handlebars.registerHelper('signedHlsUrl', function (s3Key) {
|
||||
const pathAllowed = extractBasePath(s3Key)
|
||||
const url = signUrl(`${env.CDN_ORIGIN}/${s3Key}`, {
|
||||
securityKey: env.CDN_TOKEN_SECRET,
|
||||
pathAllowed,
|
||||
isDirectory: true,
|
||||
expirationTime: constants.timeUnits.sevenDaysInSeconds,
|
||||
})
|
||||
console.log(`pathAllowed=${pathAllowed} url=${url}`)
|
||||
return url
|
||||
})
|
||||
Handlebars.registerHelper('basename', function (url: string) {
|
||||
return basename(url)
|
||||
})
|
||||
Handlebars.registerHelper('trunc', function (str, length = 6) {
|
||||
if (str && str.length > length) {
|
||||
return new Handlebars.SafeString(str.substring(0, length)) + '…';
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
});
|
||||
|
||||
const __dirname = import.meta.dirname;
|
||||
const swaggerOptions = {
|
||||
swagger: {
|
||||
info: {
|
||||
title: constants.site.title,
|
||||
description: constants.site.description,
|
||||
version: constants.site.version,
|
||||
},
|
||||
host: env.ORIGIN,
|
||||
schemes: ["http", "https"],
|
||||
consumes: ["application/json"],
|
||||
produces: ["application/json"],
|
||||
},
|
||||
};
|
||||
|
||||
const swaggerUiOptions = {
|
||||
routePrefix: "/api/docs",
|
||||
exposeRoute: true,
|
||||
};
|
||||
|
||||
app.register(fastifySwagger, swaggerOptions);
|
||||
app.register(fastifySwaggerUi, swaggerUiOptions);
|
||||
|
||||
app.register(fastifyStatic, {
|
||||
root: path.join(__dirname, 'assets'),
|
||||
prefix: '/', // optional: default '/'
|
||||
constraints: {} // optional: default {}
|
||||
})
|
||||
app.register(fastifyFormbody)
|
||||
// app.register(fastifyMultipart, {
|
||||
// limits: {
|
||||
// fileSize: 30 * 1024 * 1024 // 30MB
|
||||
// }
|
||||
// })
|
||||
app.register(fastifySecureSession, {
|
||||
// the name of the attribute decorated on the request-object, defaults to 'session'
|
||||
sessionName: 'session',
|
||||
cookieName: 'fp-session',
|
||||
// adapt this to point to the directory where secret-key is located
|
||||
key: Buffer.from(env.COOKIE_SECRET, 'hex'),
|
||||
// the amount of time the session is considered valid; this is different from the cookie options
|
||||
// and based on value within the session.
|
||||
expiry: 24 * 60 * 60, // Default 1 day
|
||||
cookie: {
|
||||
path: '/'
|
||||
// options for setCookie, see https://github.com/fastify/fastify-cookie
|
||||
}
|
||||
})
|
||||
app.register(fastifyFlash)
|
||||
app.register(fastifyView, {
|
||||
engine: {
|
||||
handlebars: Handlebars,
|
||||
},
|
||||
templates: join(__dirname, '..', 'src', 'views'),
|
||||
layout: 'layouts/main',
|
||||
viewExt: 'hbs',
|
||||
options: {
|
||||
partials: {
|
||||
navbar: 'partials/navbar.hbs',
|
||||
footer: 'partials/footer.hbs',
|
||||
commentForm: 'partials/commentForm.hbs'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
app.register(graphileWorker)
|
||||
app.register(prismaPlugin)
|
||||
app.register(hls)
|
||||
app.register(vodsRoutes)
|
||||
|
||||
app.register(streamsRoutes)
|
||||
app.register(vtubersRoutes)
|
||||
app.register(uploadsRoutes)
|
||||
app.register(usersRoutes)
|
||||
app.register(indexRoutes)
|
||||
app.register(authRoutes)
|
||||
|
||||
return app
|
||||
}
|
2835
services/our/src/assets/css/pico.amber.css
Normal file
2835
services/our/src/assets/css/pico.amber.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.amber.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.amber.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2835
services/our/src/assets/css/pico.blue.css
Normal file
2835
services/our/src/assets/css/pico.blue.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.blue.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.blue.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.amber.css
Normal file
2458
services/our/src/assets/css/pico.classless.amber.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.amber.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.amber.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.blue.css
Normal file
2458
services/our/src/assets/css/pico.classless.blue.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.blue.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.blue.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.conditional.amber.css
Normal file
2458
services/our/src/assets/css/pico.classless.conditional.amber.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.conditional.amber.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.conditional.amber.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.conditional.blue.css
Normal file
2458
services/our/src/assets/css/pico.classless.conditional.blue.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.conditional.blue.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.conditional.blue.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.conditional.cyan.css
Normal file
2458
services/our/src/assets/css/pico.classless.conditional.cyan.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.conditional.cyan.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.conditional.cyan.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.conditional.fuchsia.css
Normal file
2458
services/our/src/assets/css/pico.classless.conditional.fuchsia.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.conditional.fuchsia.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.conditional.fuchsia.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.conditional.green.css
Normal file
2458
services/our/src/assets/css/pico.classless.conditional.green.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.conditional.green.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.conditional.green.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.conditional.grey.css
Normal file
2458
services/our/src/assets/css/pico.classless.conditional.grey.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.conditional.grey.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.conditional.grey.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.conditional.indigo.css
Normal file
2458
services/our/src/assets/css/pico.classless.conditional.indigo.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.conditional.indigo.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.conditional.indigo.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.conditional.jade.css
Normal file
2458
services/our/src/assets/css/pico.classless.conditional.jade.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.conditional.jade.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.conditional.jade.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.conditional.lime.css
Normal file
2458
services/our/src/assets/css/pico.classless.conditional.lime.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.conditional.lime.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.conditional.lime.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.conditional.orange.css
Normal file
2458
services/our/src/assets/css/pico.classless.conditional.orange.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.conditional.orange.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.conditional.orange.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.conditional.pink.css
Normal file
2458
services/our/src/assets/css/pico.classless.conditional.pink.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.conditional.pink.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.conditional.pink.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.conditional.pumpkin.css
Normal file
2458
services/our/src/assets/css/pico.classless.conditional.pumpkin.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.conditional.pumpkin.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.conditional.pumpkin.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.conditional.purple.css
Normal file
2458
services/our/src/assets/css/pico.classless.conditional.purple.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.conditional.purple.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.conditional.purple.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.conditional.red.css
Normal file
2458
services/our/src/assets/css/pico.classless.conditional.red.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.conditional.red.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.conditional.red.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.conditional.sand.css
Normal file
2458
services/our/src/assets/css/pico.classless.conditional.sand.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.conditional.sand.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.conditional.sand.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.conditional.slate.css
Normal file
2458
services/our/src/assets/css/pico.classless.conditional.slate.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.conditional.slate.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.conditional.slate.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.conditional.violet.css
Normal file
2458
services/our/src/assets/css/pico.classless.conditional.violet.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.conditional.violet.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.conditional.violet.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.conditional.yellow.css
Normal file
2458
services/our/src/assets/css/pico.classless.conditional.yellow.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.conditional.yellow.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.conditional.yellow.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.conditional.zinc.css
Normal file
2458
services/our/src/assets/css/pico.classless.conditional.zinc.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.conditional.zinc.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.conditional.zinc.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.css
Normal file
2458
services/our/src/assets/css/pico.classless.css
Normal file
File diff suppressed because it is too large
Load Diff
2458
services/our/src/assets/css/pico.classless.cyan.css
Normal file
2458
services/our/src/assets/css/pico.classless.cyan.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.cyan.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.cyan.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.fuchsia.css
Normal file
2458
services/our/src/assets/css/pico.classless.fuchsia.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.fuchsia.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.fuchsia.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.green.css
Normal file
2458
services/our/src/assets/css/pico.classless.green.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.green.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.green.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.grey.css
Normal file
2458
services/our/src/assets/css/pico.classless.grey.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.grey.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.grey.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.indigo.css
Normal file
2458
services/our/src/assets/css/pico.classless.indigo.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.indigo.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.indigo.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.jade.css
Normal file
2458
services/our/src/assets/css/pico.classless.jade.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.jade.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.jade.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.lime.css
Normal file
2458
services/our/src/assets/css/pico.classless.lime.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.lime.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.lime.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
4
services/our/src/assets/css/pico.classless.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.orange.css
Normal file
2458
services/our/src/assets/css/pico.classless.orange.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.orange.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.orange.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2458
services/our/src/assets/css/pico.classless.pink.css
Normal file
2458
services/our/src/assets/css/pico.classless.pink.css
Normal file
File diff suppressed because it is too large
Load Diff
4
services/our/src/assets/css/pico.classless.pink.min.css
vendored
Normal file
4
services/our/src/assets/css/pico.classless.pink.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user