Compare commits

...

74 Commits

Author SHA1 Message Date
88abf11a14 Update dependency framer-motion to v11.11.2 2024-10-08 11:03:11 +00:00
Lee
f7e5ab8937 Merge pull request 'Update dependency nodemon to v3' (#58) from renovate/nodemon-3.x into master
All checks were successful
Deploy Backend / deploy (push) Successful in 2m9s
Reviewed-on: #58
2024-10-04 23:26:24 +00:00
04ce91b459 7
All checks were successful
Deploy Backend / deploy (push) Successful in 2m2s
Deploy Website / deploy (push) Successful in 4m42s
2024-10-05 00:12:22 +01:00
fd8e832581 7
Some checks failed
Deploy Backend / deploy (push) Failing after 32s
Deploy Website / deploy (push) Has been cancelled
2024-10-05 00:09:22 +01:00
fa2091069a Update dependency nodemon to v3 2024-10-04 23:08:46 +00:00
dddd5c3aab 7
Some checks failed
Deploy Backend / deploy (push) Failing after 33s
Deploy Website / deploy (push) Has been cancelled
2024-10-05 00:07:48 +01:00
0fc54e699f 7
Some checks failed
Deploy Backend / deploy (push) Failing after 37s
2024-10-05 00:06:38 +01:00
a46afff48a use prod mode
Some checks failed
Deploy Backend / deploy (push) Failing after 39s
2024-10-05 00:01:39 +01:00
e202d72331 messing around with the backend
Some checks failed
Deploy Backend / deploy (push) Successful in 1m36s
Deploy Website / deploy (push) Has been cancelled
2024-10-04 23:59:29 +01:00
296179a10b switch backend to fastify
All checks were successful
Deploy Backend / deploy (push) Successful in 1m24s
2024-10-04 23:26:05 +01:00
e5bfa8afa0 strip api prefix middleware
All checks were successful
Deploy Backend / deploy (push) Successful in 1m40s
2024-10-04 23:14:26 +01:00
338f955c78 fix port
All checks were successful
Deploy Backend / deploy (push) Successful in 1m14s
2024-10-04 23:09:34 +01:00
0203c9aa20 yes?
All checks were successful
Deploy Backend / deploy (push) Successful in 1m12s
2024-10-04 23:05:45 +01:00
0480481f00 yes?
All checks were successful
Deploy Backend / deploy (push) Successful in 1m23s
2024-10-04 23:04:02 +01:00
4d2f291e8f yes?
Some checks failed
Deploy Backend / deploy (push) Failing after 41s
2024-10-04 23:02:10 +01:00
77819e873d yes?
Some checks failed
Deploy Backend / deploy (push) Failing after 40s
2024-10-04 22:59:52 +01:00
522c09c32c yes?
Some checks failed
Deploy Backend / deploy (push) Failing after 29s
2024-10-04 22:57:10 +01:00
5865d55392 yes?
Some checks failed
Deploy Backend / deploy (push) Failing after 29s
Deploy Website / deploy (push) Has been cancelled
2024-10-04 22:55:24 +01:00
1164b4358b add common to workflow paths
Some checks failed
Deploy Backend / deploy (push) Failing after 26s
Deploy Website / deploy (push) Successful in 3m42s
2024-10-04 22:27:53 +01:00
c04e47b10e add common to workflow paths
Some checks failed
Deploy Backend / deploy (push) Failing after 26s
Deploy Website / deploy (push) Has been cancelled
2024-10-04 22:26:09 +01:00
0ad7f1662e meow 2024-10-04 22:25:22 +01:00
e105a76bf2 impl simple backend
Some checks failed
Deploy Backend / deploy (push) Failing after 29s
2024-10-04 22:21:37 +01:00
cd7cc29afd oops 2
All checks were successful
Deploy Website / deploy (push) Successful in 4m27s
2024-10-04 21:51:51 +01:00
779177d39b oops
All checks were successful
Deploy Backend / deploy (push) Successful in 43s
2024-10-04 21:51:21 +01:00
498492bb91 deploy backend
Some checks failed
Deploy Backend / deploy (push) Failing after 27s
Deploy Website / deploy (push) Failing after 2m34s
2024-10-04 21:46:49 +01:00
c1fe5f2884 temp
All checks were successful
Deploy Website / deploy (push) Successful in 4m7s
2024-10-04 21:39:39 +01:00
88e0e95e9f 7
All checks were successful
Deploy Website / deploy (push) Successful in 2m0s
2024-10-04 21:37:00 +01:00
60ceb4f4ab 7
Some checks failed
Deploy Website / deploy (push) Failing after 1m15s
2024-10-04 21:23:47 +01:00
899909a316 7
Some checks failed
Deploy Website / deploy (push) Failing after 42s
2024-10-04 21:21:57 +01:00
400a230ded 7
Some checks failed
Deploy Website / deploy (push) Failing after 34s
2024-10-04 21:21:08 +01:00
97d917ae27 7
Some checks failed
Deploy Website / deploy (push) Failing after 1m13s
2024-10-04 21:18:19 +01:00
04156ae37e 7
Some checks failed
Deploy Website / deploy (push) Failing after 17s
2024-10-04 21:17:10 +01:00
8952ec63d7 7
Some checks failed
Deploy Website / deploy (push) Failing after 24s
2024-10-04 21:13:15 +01:00
e4d00c890a 7
Some checks failed
Deploy Website / deploy (push) Failing after 34s
2024-10-04 21:11:55 +01:00
78584c0ce8 add workflow dispatch 2024-10-04 21:10:55 +01:00
8938bd2924 start fixing website 2024-10-04 21:09:04 +01:00
287912bd81 start fixing website
Some checks failed
Deploy Backend / deploy (push) Successful in 35s
Deploy Frontend / deploy (push) Failing after 10s
2024-10-04 21:07:09 +01:00
7e6c706369 fix backend?
Some checks failed
Deploy Backend / deploy (push) Successful in 29s
Deploy Frontend / deploy (push) Failing after 11s
2024-10-04 21:05:21 +01:00
8850516693 smaller image
Some checks failed
Deploy Backend / deploy (push) Failing after 57s
Deploy Frontend / deploy (push) Failing after 10s
2024-10-04 20:59:47 +01:00
c09f27b16f smaller image
Some checks failed
Deploy Backend / deploy (push) Failing after 12s
Deploy Frontend / deploy (push) Failing after 12s
2024-10-04 20:57:05 +01:00
beeed6ed7c smaller image
Some checks failed
Deploy Backend / deploy (push) Failing after 14s
Deploy Frontend / deploy (push) Failing after 9s
2024-10-04 20:55:02 +01:00
8671f4e036 smaller image
Some checks failed
Deploy Backend / deploy (push) Failing after 16s
Deploy Frontend / deploy (push) Has been cancelled
2024-10-04 20:54:27 +01:00
9d83a95912 oopsie
Some checks failed
Deploy Backend / deploy (push) Failing after 11s
Deploy Frontend / deploy (push) Failing after 11s
2024-10-04 20:53:35 +01:00
58f1c651b4 oopsie
Some checks failed
Deploy Backend / deploy (push) Successful in 1m26s
Deploy Frontend / deploy (push) Failing after 10s
2024-10-04 20:49:45 +01:00
98c25afd9f oopsie
Some checks failed
Deploy Backend / deploy (push) Failing after 24s
Deploy Frontend / deploy (push) Failing after 10s
2024-10-04 20:48:35 +01:00
a53f15a491 7
Some checks failed
Deploy Backend / deploy (push) Failing after 25s
Deploy Frontend / deploy (push) Failing after 13s
2024-10-04 20:47:38 +01:00
81092225cf 7
Some checks failed
Deploy Backend / deploy (push) Failing after 33s
Deploy Frontend / deploy (push) Failing after 13s
2024-10-04 20:44:15 +01:00
5d439c3ad6 7
Some checks failed
Deploy Backend / deploy (push) Failing after 28s
Deploy Frontend / deploy (push) Failing after 10s
2024-10-04 20:42:29 +01:00
aff4ef4209 7
Some checks failed
Deploy Backend / deploy (push) Failing after 24s
Deploy Frontend / deploy (push) Failing after 10s
2024-10-04 20:39:58 +01:00
4a2c6a83e6 7
Some checks failed
Deploy Backend / deploy (push) Failing after 33s
Deploy Frontend / deploy (push) Failing after 10s
2024-10-04 20:38:36 +01:00
98c52e5525 let's try this again
Some checks failed
Deploy Backend / deploy (push) Failing after 25s
Deploy Frontend / deploy (push) Failing after 10s
2024-10-04 20:35:44 +01:00
00462d9ed6 bob
All checks were successful
Deploy / deploy (push) Successful in 5m10s
2024-10-04 18:25:37 +01:00
4d35ee4050 7
All checks were successful
Deploy Frontend / deploy (push) Successful in 3m58s
2024-10-04 18:14:59 +01:00
53607c791e 7
Some checks failed
Deploy Frontend / deploy (push) Failing after 17s
2024-10-04 18:12:32 +01:00
1493295654 7
All checks were successful
Deploy Frontend / deploy (push) Successful in 2m18s
2024-10-04 18:09:36 +01:00
29a05e72a2 7
Some checks failed
Deploy Frontend / deploy (push) Failing after 28s
2024-10-04 18:08:31 +01:00
7e4978a9e0 7
Some checks failed
Deploy Frontend / deploy (push) Failing after 1m46s
2024-10-04 18:06:08 +01:00
a6a92b64e9 7
Some checks failed
Deploy Frontend / deploy (push) Failing after 1m15s
2024-10-04 18:04:00 +01:00
7545ee2066 7
Some checks failed
Deploy Frontend / deploy (push) Failing after 53s
2024-10-04 18:00:46 +01:00
1f7652863b 7
All checks were successful
Deploy Frontend / deploy (push) Successful in 1m31s
2024-10-04 17:56:09 +01:00
dc76cf61d9 7
All checks were successful
Deploy Frontend / deploy (push) Successful in 4m51s
2024-10-04 17:51:05 +01:00
f8a6bccf14 7
All checks were successful
Deploy Frontend / deploy (push) Successful in 1m34s
2024-10-04 17:46:38 +01:00
b1656b2858 7
Some checks failed
Deploy Frontend / deploy (push) Has been cancelled
2024-10-04 17:42:32 +01:00
498e0cd9d6 7
Some checks failed
Deploy Frontend / deploy (push) Has been cancelled
2024-10-04 17:38:52 +01:00
0f9c1c85aa 7
Some checks failed
Deploy Frontend / deploy (push) Failing after 11m21s
2024-10-04 17:25:00 +01:00
cf550906a8 7 2024-10-04 17:24:52 +01:00
3a476167a4 7
Some checks failed
Deploy Frontend / deploy (push) Has been cancelled
2024-10-04 17:22:08 +01:00
6841494806 7
Some checks failed
Deploy Frontend / deploy (push) Failing after 12s
2024-10-04 17:21:44 +01:00
748ed6a34b bob
All checks were successful
Deploy Frontend / deploy (push) Successful in 3m18s
2024-10-04 17:15:17 +01:00
5cc9e2c1c2 bob
All checks were successful
Deploy Frontend / deploy (push) Successful in 3m45s
2024-10-04 17:11:11 +01:00
ac9ce6fc62 now?
All checks were successful
Deploy Frontend / deploy (push) Successful in 3m22s
2024-10-04 17:01:15 +01:00
0daa7b200a now?
Some checks failed
Deploy Frontend / deploy (push) Has been cancelled
2024-10-04 16:56:58 +01:00
d4d7a60d1b ehuqiweh
All checks were successful
Deploy Frontend / deploy (push) Successful in 3m23s
2024-10-04 16:51:17 +01:00
e0833d17f1 j
All checks were successful
Deploy Frontend / deploy (push) Successful in 4m3s
2024-10-04 16:43:12 +01:00
444 changed files with 4596 additions and 1012 deletions

2
.dockerignore Normal file

@ -0,0 +1,2 @@
node_modules
dist

@ -1,3 +0,0 @@
{
"extends": ["next/core-web-vitals", "next/typescript"]
}

@ -0,0 +1,28 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: scoresaber-reloaded-backend
namespace: public-services
spec:
replicas: 1
selector:
matchLabels:
app: scoresaber-reloaded-backend
template:
metadata:
labels:
app: scoresaber-reloaded-backend
spec:
containers:
- name: scoresaber-reloaded-backend-container
image: git.fascinated.cc/fascinated/scoresaber-reloaded-backend:latest
imagePullPolicy: Always
ports:
- containerPort: 8080
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
cpu: 1000m # 1 vCPU
memory: 512Mi

@ -0,0 +1,26 @@
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: scoresaber-reloaded-backend-ingress
namespace: public-services
annotations:
kubernetes.io/ingress.class: traefik-external
spec:
entryPoints:
- websecure
routes:
- match: Host(`ssr.fascinated.cc`) && PathPrefix(`/api`)
kind: Rule
middlewares:
- name: default-headers
namespace: traefik
- name: compress
namespace: traefik
- name: scoresaber-reloaded-backend-strip-api-prefix
namespace: public-services
services:
- name: scoresaber-reloaded-backend-service
port: 8080
tls:
secretName: fascinated-cc

@ -0,0 +1,13 @@
---
apiVersion: v1
kind: Service
metadata:
name: scoresaber-reloaded-backend-service
namespace: public-services
spec:
type: ClusterIP
ports:
- port: 8080
targetPort: 8080
selector:
app: scoresaber-reloaded-backend

@ -0,0 +1,10 @@
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: scoresaber-reloaded-backend-strip-api-prefix
namespace: public-services
spec:
stripPrefix:
prefixes:
- "/api"

@ -1,21 +1,21 @@
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: scoresaber-reloaded name: scoresaber-reloaded-website
namespace: public-services namespace: public-services
spec: spec:
replicas: 1 replicas: 1
selector: selector:
matchLabels: matchLabels:
app: scoresaber-reloaded app: scoresaber-reloaded-website
template: template:
metadata: metadata:
labels: labels:
app: scoresaber-reloaded app: scoresaber-reloaded-website
spec: spec:
containers: containers:
- name: scoresaber-reloaded-container - name: scoresaber-reloaded-website-container
image: git.fascinated.cc/fascinated/scoresaber-reloaded:latest image: git.fascinated.cc/fascinated/scoresaber-reloaded-website:latest
imagePullPolicy: Always imagePullPolicy: Always
ports: ports:
- containerPort: 3000 - containerPort: 3000

@ -2,7 +2,7 @@
apiVersion: traefik.io/v1alpha1 apiVersion: traefik.io/v1alpha1
kind: IngressRoute kind: IngressRoute
metadata: metadata:
name: scoresaber-reloaded-ingress name: scoresaber-reloaded-website-ingress
namespace: public-services namespace: public-services
annotations: annotations:
kubernetes.io/ingress.class: traefik-external kubernetes.io/ingress.class: traefik-external
@ -18,7 +18,7 @@ spec:
- name: compress - name: compress
namespace: traefik namespace: traefik
services: services:
- name: scoresaber-reloaded-service - name: scoresaber-reloaded-website-service
port: 3000 port: 3000
tls: tls:
secretName: fascinated-cc secretName: fascinated-cc

@ -2,7 +2,7 @@
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: scoresaber-reloaded-service name: scoresaber-reloaded-website-service
namespace: public-services namespace: public-services
spec: spec:
type: ClusterIP type: ClusterIP
@ -10,4 +10,4 @@ spec:
- port: 3000 - port: 3000
targetPort: 3000 targetPort: 3000
selector: selector:
app: scoresaber-reloaded app: scoresaber-reloaded-website

@ -0,0 +1,63 @@
name: "Deploy Backend"
on:
workflow_dispatch:
push:
branches:
- master
paths:
- backend/**
- common/**
- .gitea/kubernetes/backend/**
- .gitea/workflows/deploy-backend.yml
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_TOKEN }}
registry: git.fascinated.cc
- name: Build Image
uses: docker/build-push-action@v6
with:
context: .
file: ./backend/Dockerfile
push: true
tags: |
git.fascinated.cc/fascinated/scoresaber-reloaded-backend:${{ github.sha }}
git.fascinated.cc/fascinated/scoresaber-reloaded-backend:latest
build-args: |
GIT_REV=${{ gitea.sha }}
- name: Install kubectl
uses: azure/setup-kubectl@v4
id: install
- name: Setup Kubernetes Context
uses: azure/k8s-set-context@v4
with:
kubeconfig: ${{ secrets.KUBECONFIG }}
- name: Deploy to Kubernetes
uses: Azure/k8s-deploy@v5
with:
action: deploy
namespace: public-services
manifests: |
.gitea/kubernetes/backend/deployment.yaml
.gitea/kubernetes/backend/service.yaml
.gitea/kubernetes/backend/strip-api-prefix-middleware.yaml
.gitea/kubernetes/backend/ingress.yaml
images: |
git.fascinated.cc/fascinated/scoresaber-reloaded-backend:${{ github.sha }}

@ -1,9 +1,15 @@
name: "Deploy" name: "Deploy Website"
on: on:
workflow_dispatch:
push: push:
branches: branches:
- master - master
paths:
- website/**
- common/**
- .gitea/kubernetes/website/**
- .gitea/workflows/deploy-website.yml
jobs: jobs:
deploy: deploy:
@ -26,10 +32,11 @@ jobs:
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
with: with:
context: . context: .
file: ./website/Dockerfile
push: true push: true
tags: | tags: |
git.fascinated.cc/fascinated/scoresaber-reloaded:${{ github.sha }} git.fascinated.cc/fascinated/scoresaber-reloaded-website:${{ github.sha }}
git.fascinated.cc/fascinated/scoresaber-reloaded:latest git.fascinated.cc/fascinated/scoresaber-reloaded-website:latest
build-args: | build-args: |
GIT_REV=${{ gitea.sha }} GIT_REV=${{ gitea.sha }}
SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}
@ -49,9 +56,9 @@ jobs:
action: deploy action: deploy
namespace: public-services namespace: public-services
manifests: | manifests: |
.gitea/kubernetes/sealed-secrets.yaml .gitea/kubernetes/website/sealed-secrets.yaml
.gitea/kubernetes/deployment.yaml .gitea/kubernetes/website/deployment.yaml
.gitea/kubernetes/service.yaml .gitea/kubernetes/website/service.yaml
.gitea/kubernetes/ingress.yaml .gitea/kubernetes/website/ingress.yaml
images: | images: |
git.fascinated.cc/fascinated/scoresaber-reloaded:${{ github.sha }} git.fascinated.cc/fascinated/scoresaber-reloaded-website:${{ github.sha }}

110
.gitignore vendored

@ -1,41 +1,79 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. logs
*.log
# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
lerna-debug.log*
# local env files .pnpm-debug.log*
.env*.local report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
pids
# vercel *.pid
.vercel *.seed
*.pid.lock
# typescript lib-cov
coverage
*.lcov
.nyc_output
.grunt
bower_components
.lock-wscript
build/Release
node_modules/
jspm_packages/
web_modules/
*.tsbuildinfo *.tsbuildinfo
.npm
.eslintcache
.stylelintcache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
.node_repl_history
*.tgz
.yarn-integrity
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
.cache
.parcel-cache
.next
out
.nuxt
dist
.cache/
.vuepress/dist
.temp
.docusaurus
.serverless/
.fusebox/
.dynamodb/
.tern-port
.vscode-test
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
.DS_*
**/*.backup.*
**/*.back.*
node_modules
*.sublime*
psd
thumb
sketch
/node_modules
/.pnp
.pnp.js
/coverage
/.next/
/out/
/build
.DS_Store
*.pem
.env*.local
.vercel
next-env.d.ts next-env.d.ts
.idea
# Sentry Config File
.env.sentry-build-plugin

@ -1,65 +0,0 @@
FROM fascinated/docker-images:nodejs_20_with_pnpm AS base
# Install dependencies and build tools for canvas
FROM base AS deps
RUN apk add --no-cache python3 make g++ gcc pkgconfig pixman cairo-dev libjpeg-turbo-dev pango-dev giflib-dev
WORKDIR /app
COPY package.json* pnpm-lock.yaml* ./
RUN pnpm install --frozen-lockfile --quiet
# Build from source
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Install runtime dependencies
RUN apk add --no-cache cairo pango libjpeg-turbo giflib
ENV NEXT_TELEMETRY_DISABLED=1
# Add the commit hash
ARG GIT_REV
ENV GIT_REV=${GIT_REV}
# Add the sentry auth token
ARG SENTRY_AUTH_TOKEN
ENV SENTRY_AUTH_TOKEN=${SENTRY_AUTH_TOKEN}
# Build the app
RUN pnpm run build
# Final stage to run the app
FROM base AS runner
WORKDIR /app
# Install runtime dependencies
RUN apk add --no-cache cairo pango libjpeg-turbo giflib
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
RUN mkdir .next
RUN chown nextjs:nodejs .next
# Add the commit hash
ARG GIT_REV
ENV GIT_REV=${GIT_REV}
# Copy the built app from the builder stage
COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/package.json ./package.json
COPY --from=builder --chown=nextjs:nodejs /app/next.config.mjs ./next.config.mjs
USER nextjs
EXPOSE 3000
ENV HOSTNAME="0.0.0.0"
ENV PORT=3000
CMD ["pnpm", "start"]

2
backend/.dockerignore Normal file

@ -0,0 +1,2 @@
node_modules
dist

25
backend/.eslintrc.js Normal file

@ -0,0 +1,25 @@
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};

31
backend/Dockerfile Normal file

@ -0,0 +1,31 @@
FROM node:20-alpine3.17
# Install pnpm globally
RUN npm install -g pnpm
ENV PNPM_HOME=/usr/local/bin
WORKDIR /app
ARG GIT_REV
ENV GIT_REV=${GIT_REV}
# Copy necessary files for installation
COPY package.json* pnpm-lock.yaml* pnpm-workspace.yaml* ./
COPY common ./common
COPY backend ./backend
# Install all dependencies (for common and backend)
RUN pnpm install
# Run in production mode
ENV NODE_ENV=production
# Build the common workspace first, then the backend
RUN pnpm --filter ...common build
RUN pnpm --filter ...backend build
# Expose the port your application runs on
EXPOSE 8080
# Command to run your app
CMD ["node", "backend/dist/main.js"]

8
backend/nest-cli.json Normal file

@ -0,0 +1,8 @@
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}

44
backend/package.json Normal file

@ -0,0 +1,44 @@
{
"name": "backend",
"version": "1.0.0",
"author": "fascinated7",
"license": "MIT",
"private": true,
"scripts": {
"dev": "nest start --watch --webpack webpack-hmr.config.js",
"build": "nest build",
"start": "nest start"
},
"dependencies": {
"@fastify/one-line-logger": "^2.0.0",
"@nestjs/common": "^10.0.0",
"@nestjs/core": "^10.0.0",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/platform-fastify": "^10.4.4",
"@ssr/common": "workspace:*",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.1"
},
"devDependencies": {
"@nestjs/cli": "^10.0.0",
"@nestjs/schematics": "^10.0.0",
"@nestjs/testing": "^10.0.0",
"@types/express": "^4.17.17",
"@types/node": "^20.3.1",
"@types/supertest": "^6.0.0",
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"concurrently": "^9.0.1",
"eslint": "^8.42.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"nodemon": "^3.0.0",
"prettier": "^3.0.0",
"source-map-support": "^0.5.21",
"supertest": "^7.0.0",
"ts-loader": "^9.4.3",
"ts-node": "^10.9.1",
"tsup": "^8.3.0",
"typescript": "^5"
}
}

12
backend/src/app.module.ts Normal file

@ -0,0 +1,12 @@
import { Module } from "@nestjs/common";
import { AppController } from "./controller/app.controller";
import { PlayerService } from "./service/player.service";
import { PlayerController } from "./controller/player.controller";
import { AppService } from "./service/app.service";
@Module({
imports: [],
controllers: [AppController, PlayerController],
providers: [AppService, PlayerService],
})
export class AppModule {}

@ -0,0 +1,15 @@
import { Controller, Get } from "@nestjs/common";
import { AppService } from "../service/app.service";
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get("/")
getHome() {
return {
message: "ScoreSaber Reloaded API",
version: this.appService.getVersion(),
};
}
}

@ -0,0 +1,12 @@
import { Controller, Get, Param } from "@nestjs/common";
import { PlayerService } from "../service/player.service";
@Controller("/player")
export class PlayerController {
constructor(private readonly playerService: PlayerService) {}
@Get("/history/:id")
getHistory(@Param("id") id: string) {
return this.playerService.getHistory(id);
}
}

21
backend/src/main.ts Normal file

@ -0,0 +1,21 @@
import { NestFactory } from "@nestjs/core";
import { FastifyAdapter, NestFastifyApplication } from "@nestjs/platform-fastify";
import { AppModule } from "./app.module";
async function bootstrap() {
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter({
logger: {
transport: {
target: "@fastify/one-line-logger",
},
},
}),
{
logger: ["error", "warn", "log"],
}
);
await app.listen(8080, "0.0.0.0");
}
bootstrap();

@ -0,0 +1,14 @@
import { Injectable } from "@nestjs/common";
import { isProduction } from "@ssr/common/dist";
@Injectable()
export class AppService {
/**
* Gets the app version.
*
* @returns the app version
*/
getVersion(): string {
return `1.0.0-${isProduction() ? process.env.GIT_REV.substring(0, 7) : "dev"}`;
}
}

@ -0,0 +1,16 @@
import { Injectable } from "@nestjs/common";
@Injectable()
export class PlayerService {
/**
* Gets the statistic history for the given player
*
* @param id the id of the player
* @returns the players statistic history
*/
getHistory(id: string) {
return {
id: id,
};
}
}

@ -0,0 +1,4 @@
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
}

21
backend/tsconfig.json Normal file

@ -0,0 +1,21 @@
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "ES2021",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false
}
}

@ -0,0 +1,6 @@
module.exports = function (options) {
return {
...options,
stats: "minimal", // This disables the full-screen mode and simplifies the output
};
};

2
common/.dockerignore Normal file

@ -0,0 +1,2 @@
node_modules
dist

13
common/package.json Normal file

@ -0,0 +1,13 @@
{
"name": "@ssr/common",
"version": "1.0.0",
"scripts": {
"dev": "tsup src/index.ts --watch",
"build": "tsup src/index.ts"
},
"devDependencies": {
"@types/node": "^22.7.4",
"tsup": "^6.5.0",
"typescript": "^5"
}
}

1
common/src/index.ts Normal file

@ -0,0 +1 @@
export * from "src/utils";

6
common/src/utils.ts Normal file

@ -0,0 +1,6 @@
/**
* Checks if we're in production
*/
export function isProduction() {
return process.env.NODE_ENV === "production";
}

21
common/tsconfig.json Normal file

@ -0,0 +1,21 @@
{
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "ES2021",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false
}
}

9
common/tsup.config.ts Normal file

@ -0,0 +1,9 @@
import { defineConfig } from "tsup";
export default defineConfig({
entry: ["src/index.ts"],
splitting: false,
sourcemap: true,
clean: true,
dts: true, // This line enables type declaration file generation
});

@ -1,66 +1,15 @@
{ {
"name": "scoresaber-reloadedv3", "name": "scoresaber-reloadedv3",
"version": "0.1.0", "version": "1.0.0",
"private": true,
"scripts": { "scripts": {
"dev": "next dev --turbo", "dev": "pnpm --parallel --workspace-concurrency=4 run -r dev",
"build": "next build",
"start": "next start", "build:website": "pnpm --filter website build",
"lint": "next lint" "build:backend": "pnpm --filter backend build",
"start:website": "pnpm --filter website start",
"start:backend": "pnpm --filter backend start"
}, },
"dependencies": { "author": "fascinated7",
"@formkit/tempo": "^0.1.2", "license": "MIT"
"@heroicons/react": "^2.1.5", }
"@hookform/resolvers": "^3.9.0",
"@radix-ui/react-avatar": "^1.1.0",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-scroll-area": "^1.1.0",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-toast": "^1.2.1",
"@radix-ui/react-tooltip": "^1.1.2",
"@sentry/nextjs": "8",
"@tanstack/react-query": "^5.55.4",
"@trigger.dev/nextjs": "^3.0.8",
"@trigger.dev/react": "^3.0.8",
"@trigger.dev/sdk": "^3.0.8",
"@uidotdev/usehooks": "^2.4.1",
"canvas": "3.0.0-rc2",
"chart.js": "^4.4.4",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"comlink": "^4.4.1",
"dexie": "^4.0.8",
"dexie-react-hooks": "^1.1.7",
"extract-colors": "^4.0.8",
"framer-motion": "^11.5.4",
"js-cookie": "^3.0.5",
"ky": "^1.7.2",
"lucide-react": "^0.447.0",
"mongoose": "^8.7.0",
"next": "15.0.0-rc.0",
"next-build-id": "^3.0.0",
"next-themes": "^0.3.0",
"react": "19.0.0-rc-3edc000d-20240926",
"react-chartjs-2": "^5.2.0",
"react-dom": "19.0.0-rc-3edc000d-20240926",
"react-hook-form": "^7.53.0",
"tailwind-merge": "^2.5.2",
"tailwindcss-animate": "^1.0.7",
"zod": "^3.23.8"
},
"devDependencies": {
"@types/js-cookie": "^3.0.6",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"eslint": "^8",
"eslint-config-next": "14.2.14",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"typescript": "^5"
},
"trigger.dev": {
"endpointId": "scoresaber-reloaded-KB0Z"
}
}

4599
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

4
pnpm-workspace.yaml Normal file

@ -0,0 +1,4 @@
packages:
- "common"
- "website"
- "backend"

@ -1,55 +0,0 @@
import { createCanvas, loadImage } from "canvas";
import { config } from "../../config";
import ky from "ky";
import { extractColors } from "extract-colors";
import { cache } from "react";
/**
* Proxies all non-localhost images to make them load faster.
*
* @param originalUrl the original image url
* @returns the new image url
*/
export function getImageUrl(originalUrl: string) {
return `${!config.siteUrl.includes("localhost") ? "https://img.fascinated.cc/upload/q_70/" : ""}${originalUrl}`;
}
/**
* Gets the average color of an image
*
* @param src the image url
* @returns the average color
*/
export const getAverageColor = cache(async (src: string) => {
const before = performance.now();
console.log(`Getting average color of "${src}"...`);
try {
const response = await ky.get(`https://img.fascinated.cc/upload/w_64,h_64,o_jpg/${src}`);
if (response.status !== 200) {
return undefined;
}
const imageBuffer = await response.arrayBuffer();
// Create an image from the buffer using canvas
const img = await loadImage(Buffer.from(imageBuffer));
const canvas = createCanvas(img.width, img.height);
const ctx = canvas.getContext("2d");
// Draw the image onto the canvas
ctx.drawImage(img, 0, 0);
// Get the pixel data from the canvas
const imageData = ctx.getImageData(0, 0, img.width, img.height);
const { data, width, height } = imageData;
// Use your extractColors function to calculate the average color
const color = await extractColors({ data, width, height });
console.log(`Found average color of "${src}" in ${(performance.now() - before).toFixed(0)}ms`);
return color[2];
} catch (error) {
console.error("Error while getting average color:", error);
return undefined;
}
});

2
website/.dockerignore Normal file

@ -0,0 +1,2 @@
node_modules
dist

8
website/.eslintrc.json Normal file

@ -0,0 +1,8 @@
{
"extends": ["next/core-web-vitals", "next/typescript"],
"rules": {
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/no-unused-expressions": "off",
"@typescript-eslint/no-empty-object-type": "off"
}
}

41
website/.gitignore vendored Normal file

@ -0,0 +1,41 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
.idea
# Sentry Config File
.env.sentry-build-plugin

25
website/Dockerfile Normal file

@ -0,0 +1,25 @@
FROM node:20-alpine3.17 AS base
# Install pnpm
RUN npm install -g pnpm
ENV PNPM_HOME=/usr/local/bin
FROM base AS runner
WORKDIR /app
# Copy website package and lock files only
COPY package.json* pnpm-lock.yaml* pnpm-workspace.yaml* ./
COPY website ./website
ARG GIT_REV
ENV GIT_REV=${GIT_REV}
RUN pnpm install --filter website
RUN pnpm run build:website
# Expose the app port and start it
EXPOSE 3000
ENV HOSTNAME="0.0.0.0"
ENV PORT=3000
CMD ["pnpm", "start:website"]

@ -1,17 +1,8 @@
import { withSentryConfig } from "@sentry/nextjs"; import { withSentryConfig } from "@sentry/nextjs";
import { format } from "@formkit/tempo"; import { format } from "@formkit/tempo";
import nextBuildId from "next-build-id";
import path from "path";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url); // get the resolved path to the file
const __dirname = path.dirname(__filename); // get the name of the directory
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const nextConfig = { const nextConfig = {
experimental: {
webpackMemoryOptimizations: true,
},
images: { images: {
remotePatterns: [ remotePatterns: [
{ {
@ -23,8 +14,7 @@ const nextConfig = {
], ],
}, },
env: { env: {
NEXT_PUBLIC_BUILD_ID: NEXT_PUBLIC_BUILD_ID: process.env.GIT_REV || "dev",
process.env.GIT_REV || nextBuildId.sync({ dir: __dirname }),
NEXT_PUBLIC_BUILD_TIME: new Date().toLocaleDateString("en-US", { NEXT_PUBLIC_BUILD_TIME: new Date().toLocaleDateString("en-US", {
year: "numeric", year: "numeric",
month: "long", month: "long",

64
website/package.json Normal file

@ -0,0 +1,64 @@
{
"name": "website",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev --turbo",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@formkit/tempo": "^0.1.2",
"@heroicons/react": "^2.1.5",
"@hookform/resolvers": "^3.9.0",
"@radix-ui/react-avatar": "^1.1.0",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-scroll-area": "^1.1.0",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-toast": "^1.2.1",
"@radix-ui/react-tooltip": "^1.1.2",
"@sentry/nextjs": "8",
"@tanstack/react-query": "^5.55.4",
"@trigger.dev/nextjs": "^3.0.8",
"@trigger.dev/react": "^3.0.8",
"@trigger.dev/sdk": "^3.0.8",
"@uidotdev/usehooks": "^2.4.1",
"chart.js": "^4.4.4",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"comlink": "^4.4.1",
"dexie": "^4.0.8",
"dexie-react-hooks": "^1.1.7",
"framer-motion": "^11.5.4",
"js-cookie": "^3.0.5",
"ky": "^1.7.2",
"lucide-react": "^0.447.0",
"mongoose": "^8.7.0",
"next": "15.0.0-rc.0",
"next-build-id": "^3.0.0",
"next-themes": "^0.3.0",
"react": "19.0.0-rc-3edc000d-20240926",
"react-chartjs-2": "^5.2.0",
"react-dom": "19.0.0-rc-3edc000d-20240926",
"react-hook-form": "^7.53.0",
"tailwind-merge": "^2.5.2",
"tailwindcss-animate": "^1.0.7",
"zod": "^3.23.8"
},
"devDependencies": {
"@types/js-cookie": "^3.0.6",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"eslint": "^8",
"eslint-config-next": "14.2.14",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"typescript": "^5"
},
"trigger.dev": {
"endpointId": "scoresaber-reloaded-KB0Z"
}
}

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Before

Width:  |  Height:  |  Size: 841 B

After

Width:  |  Height:  |  Size: 841 B

Before

Width:  |  Height:  |  Size: 132 B

After

Width:  |  Height:  |  Size: 132 B

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Before

Width:  |  Height:  |  Size: 766 B

After

Width:  |  Height:  |  Size: 766 B

Before

Width:  |  Height:  |  Size: 659 B

After

Width:  |  Height:  |  Size: 659 B

Before

Width:  |  Height:  |  Size: 604 B

After

Width:  |  Height:  |  Size: 604 B

Before

Width:  |  Height:  |  Size: 121 B

After

Width:  |  Height:  |  Size: 121 B

Before

Width:  |  Height:  |  Size: 522 B

After

Width:  |  Height:  |  Size: 522 B

Before

Width:  |  Height:  |  Size: 445 B

After

Width:  |  Height:  |  Size: 445 B

Before

Width:  |  Height:  |  Size: 320 B

After

Width:  |  Height:  |  Size: 320 B

Before

Width:  |  Height:  |  Size: 909 B

After

Width:  |  Height:  |  Size: 909 B

Before

Width:  |  Height:  |  Size: 109 B

After

Width:  |  Height:  |  Size: 109 B

Before

Width:  |  Height:  |  Size: 554 B

After

Width:  |  Height:  |  Size: 554 B

Before

Width:  |  Height:  |  Size: 311 B

After

Width:  |  Height:  |  Size: 311 B

Before

Width:  |  Height:  |  Size: 179 B

After

Width:  |  Height:  |  Size: 179 B

Before

Width:  |  Height:  |  Size: 214 B

After

Width:  |  Height:  |  Size: 214 B

Before

Width:  |  Height:  |  Size: 339 B

After

Width:  |  Height:  |  Size: 339 B

Before

Width:  |  Height:  |  Size: 324 B

After

Width:  |  Height:  |  Size: 324 B

Before

Width:  |  Height:  |  Size: 282 B

After

Width:  |  Height:  |  Size: 282 B

Before

Width:  |  Height:  |  Size: 127 B

After

Width:  |  Height:  |  Size: 127 B

Before

Width:  |  Height:  |  Size: 254 B

After

Width:  |  Height:  |  Size: 254 B

Before

Width:  |  Height:  |  Size: 105 B

After

Width:  |  Height:  |  Size: 105 B

Before

Width:  |  Height:  |  Size: 326 B

After

Width:  |  Height:  |  Size: 326 B

Before

Width:  |  Height:  |  Size: 651 B

After

Width:  |  Height:  |  Size: 651 B

Before

Width:  |  Height:  |  Size: 127 B

After

Width:  |  Height:  |  Size: 127 B

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Before

Width:  |  Height:  |  Size: 132 B

After

Width:  |  Height:  |  Size: 132 B

Before

Width:  |  Height:  |  Size: 810 B

After

Width:  |  Height:  |  Size: 810 B

Before

Width:  |  Height:  |  Size: 792 B

After

Width:  |  Height:  |  Size: 792 B

Before

Width:  |  Height:  |  Size: 287 B

After

Width:  |  Height:  |  Size: 287 B

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before

Width:  |  Height:  |  Size: 206 B

After

Width:  |  Height:  |  Size: 206 B

Before

Width:  |  Height:  |  Size: 139 B

After

Width:  |  Height:  |  Size: 139 B

Before

Width:  |  Height:  |  Size: 377 B

After

Width:  |  Height:  |  Size: 377 B

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before

Width:  |  Height:  |  Size: 359 B

After

Width:  |  Height:  |  Size: 359 B

Before

Width:  |  Height:  |  Size: 561 B

After

Width:  |  Height:  |  Size: 561 B

Before

Width:  |  Height:  |  Size: 523 B

After

Width:  |  Height:  |  Size: 523 B

Before

Width:  |  Height:  |  Size: 286 B

After

Width:  |  Height:  |  Size: 286 B

Before

Width:  |  Height:  |  Size: 344 B

After

Width:  |  Height:  |  Size: 344 B

Before

Width:  |  Height:  |  Size: 135 B

After

Width:  |  Height:  |  Size: 135 B

Before

Width:  |  Height:  |  Size: 123 B

After

Width:  |  Height:  |  Size: 123 B

Before

Width:  |  Height:  |  Size: 717 B

After

Width:  |  Height:  |  Size: 717 B

Before

Width:  |  Height:  |  Size: 263 B

After

Width:  |  Height:  |  Size: 263 B

Before

Width:  |  Height:  |  Size: 215 B

After

Width:  |  Height:  |  Size: 215 B

Before

Width:  |  Height:  |  Size: 286 B

After

Width:  |  Height:  |  Size: 286 B

Before

Width:  |  Height:  |  Size: 135 B

After

Width:  |  Height:  |  Size: 135 B

Before

Width:  |  Height:  |  Size: 419 B

After

Width:  |  Height:  |  Size: 419 B

Before

Width:  |  Height:  |  Size: 362 B

After

Width:  |  Height:  |  Size: 362 B

Some files were not shown because too many files have changed in this diff Show More