remove web project (diff repo), authentication for user and profile, config cleanup, new database keys, turnstile captcha support, user and profile classes, nameserver changes, user routes, web is now on fs
This commit is contained in:
21
README.md
21
README.md
@@ -1,7 +1,22 @@
|
||||
# Galvanic Corrosion
|
||||
delectable yum yum
|
||||
|
||||
Rec Room custom server for communities. Fast runtime and easy setup.
|
||||
Built for Rec Room build 526 (Timestamp: 637098805133024772, Version: 20191120)
|
||||
Rec Room custom server for communities. Fast runtime and easy setup.<br>Built for Rec Room build 526 (Timestamp: 637098805133024772, Version: 20191120)
|
||||
|
||||
<img src="galv4.jpg" alt="drawing" width="200"/>
|
||||
<img src="galv4.jpg" alt="drawing" width="200"/>
|
||||
|
||||
## Compiling Galvanic Corrosion
|
||||
* Install [Deno 2.x](https://docs.deno.com/runtime/getting_started/installation/) for the server
|
||||
* Install [Node.js 2x](https://nodejs.org/en/download) for the web project
|
||||
* Configure project
|
||||
- Clone this repo
|
||||
- Install dependencies with `deno i`
|
||||
- Compile server with `deno run cross-compile`
|
||||
* Configure web
|
||||
- Clone https://gitea.proxnet.dev/zombieb/galvanic-corrosion-web.git
|
||||
- Install deps with `npm i` (this may take some time)
|
||||
- Compile with Vite using `npx vite build`
|
||||
- Copy contents of `dist/*` to `res/web/`, next to `GalvanicCorrosion[.exe]` from above
|
||||
* Run the built executable in `build/`
|
||||
* ...
|
||||
* Profit
|
||||
24
data/galvanic-corrosion-web/.gitignore
vendored
24
data/galvanic-corrosion-web/.gitignore
vendored
@@ -1,24 +0,0 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
./dist
|
||||
./dist-ssr
|
||||
./*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
@@ -1,50 +0,0 @@
|
||||
# React + TypeScript + Vite
|
||||
|
||||
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||
|
||||
Currently, two official plugins are available:
|
||||
|
||||
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
||||
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
||||
|
||||
## Expanding the ESLint configuration
|
||||
|
||||
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
|
||||
|
||||
- Configure the top-level `parserOptions` property like this:
|
||||
|
||||
```js
|
||||
export default tseslint.config({
|
||||
languageOptions: {
|
||||
// other options...
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked`
|
||||
- Optionally add `...tseslint.configs.stylisticTypeChecked`
|
||||
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config:
|
||||
|
||||
```js
|
||||
// eslint.config.js
|
||||
import react from 'eslint-plugin-react'
|
||||
|
||||
export default tseslint.config({
|
||||
// Set the react version
|
||||
settings: { react: { version: '18.3' } },
|
||||
plugins: {
|
||||
// Add the react plugin
|
||||
react,
|
||||
},
|
||||
rules: {
|
||||
// other rules...
|
||||
// Enable its recommended rules
|
||||
...react.configs.recommended.rules,
|
||||
...react.configs['jsx-runtime'].rules,
|
||||
},
|
||||
})
|
||||
```
|
||||
@@ -1,28 +0,0 @@
|
||||
import js from '@eslint/js'
|
||||
import globals from 'globals'
|
||||
import reactHooks from 'eslint-plugin-react-hooks'
|
||||
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||
import tseslint from 'typescript-eslint'
|
||||
|
||||
export default tseslint.config(
|
||||
{ ignores: ['dist'] },
|
||||
{
|
||||
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
||||
files: ['**/*.{ts,tsx}'],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2020,
|
||||
globals: globals.browser,
|
||||
},
|
||||
plugins: {
|
||||
'react-hooks': reactHooks,
|
||||
'react-refresh': reactRefresh,
|
||||
},
|
||||
rules: {
|
||||
...reactHooks.configs.recommended.rules,
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
},
|
||||
},
|
||||
)
|
||||
@@ -1,13 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Skibidi</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
3226
data/galvanic-corrosion-web/package-lock.json
generated
3226
data/galvanic-corrosion-web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"name": "galvanic-corrosion-web",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.19.0",
|
||||
"@types/react": "^19.0.8",
|
||||
"@types/react-dom": "^19.0.3",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"eslint": "^9.19.0",
|
||||
"eslint-plugin-react-hooks": "^5.0.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.18",
|
||||
"globals": "^15.14.0",
|
||||
"typescript": "~5.7.2",
|
||||
"typescript-eslint": "^8.22.0",
|
||||
"vite": "^6.1.0"
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1,15 +0,0 @@
|
||||
import { useState } from 'react'
|
||||
import reactLogo from './assets/react.svg'
|
||||
import viteLogo from '/vite.svg'
|
||||
|
||||
function App() {
|
||||
const [count, setCount] = useState(0)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>Skibidi</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 4.0 KiB |
@@ -1,14 +0,0 @@
|
||||
:root {
|
||||
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
|
||||
color-scheme: light dark;
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
background-color: #242424;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import './index.css'
|
||||
import App from './App.tsx'
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>,
|
||||
)
|
||||
@@ -1 +0,0 @@
|
||||
/// <reference types="vite/client" />
|
||||
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
]
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2023"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
build: {
|
||||
rollupOptions: {
|
||||
input: {
|
||||
main: resolve(__dirname, 'index.html')
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
16
deno.json
16
deno.json
@@ -1,16 +1,24 @@
|
||||
{
|
||||
"tasks": {
|
||||
"compile-win": "deno compile --target x86_64-pc-windows-msvc --include data/ -o build/GalvanicCorrosion.exe -A src/main.ts",
|
||||
"compile-linux": "deno compile --target x86_64-unknown-linux-gnu --include data/ -o build/GalvanicCorrosion -A src/main.ts",
|
||||
"compile-win": "deno compile --target x86_64-pc-windows-msvc -o build/GalvanicCorrosion.exe -A src/main.ts",
|
||||
"compile-linux": "deno compile --target x86_64-unknown-linux-gnu -o build/GalvanicCorrosion -A src/main.ts",
|
||||
"cross-compile": "deno run compile-win && deno run compile-linux",
|
||||
"start": "deno run -A src/main.ts",
|
||||
"compile-win-run": "deno run compile-win && cd build/ && GalvanicCorrosion.exe"
|
||||
"web-compile-win": "scripts/web-compile.cmd && scripts/web-postcompile.cmd",
|
||||
"web-compile-win-dev": "scripts/web-compile.cmd && scripts/web-postcompile-dev.cmd",
|
||||
"full-compile": "deno run web-compile-win && deno run cross-compile",
|
||||
"full-compile-win": "deno run web-compile-win && deno run compile-win",
|
||||
"full-compile-win-run": "deno run full-compile-win && cd build/ && GalvanicCorrosion.exe",
|
||||
"dev": "deno run -A src/main.ts --dev",
|
||||
"web-dev": "cd web && npx vite dev"
|
||||
},
|
||||
"imports": {
|
||||
"@gz/jwt": "jsr:@gz/jwt@^0.1.0",
|
||||
"@proxnet/undead-logging": "jsr:@proxnet/undead-logging@^1.2.0",
|
||||
"@std/assert": "jsr:@std/assert@1",
|
||||
"@types/cookie-parser": "npm:@types/cookie-parser@^1.4.8",
|
||||
"@types/express": "npm:@types/express@^5.0.0",
|
||||
"@types/validator": "npm:@types/validator@^13.12.2",
|
||||
"cookie-parser": "npm:cookie-parser@^1.4.7",
|
||||
"discord.js": "npm:discord.js@^14.16.3",
|
||||
"express": "npm:express@^4.21.2",
|
||||
"ioredis": "npm:ioredis@^5.5.0",
|
||||
|
||||
132
deno.lock
generated
132
deno.lock
generated
@@ -8,10 +8,14 @@
|
||||
"jsr:@std/crypto@^1.0.3": "1.0.3",
|
||||
"jsr:@std/internal@^1.0.5": "1.0.5",
|
||||
"jsr:@std/uuid@*": "1.0.4",
|
||||
"npm:@types/cookie-parser@*": "1.4.8_@types+express@5.0.0",
|
||||
"npm:@types/cookie-parser@^1.4.8": "1.4.8_@types+express@5.0.0",
|
||||
"npm:@types/express@*": "5.0.0",
|
||||
"npm:@types/express@5": "5.0.0",
|
||||
"npm:@types/node@*": "22.5.4",
|
||||
"npm:@types/validator@^13.12.2": "13.12.2",
|
||||
"npm:chalk@^5.3.0": "5.3.0",
|
||||
"npm:cookie-parser@^1.4.7": "1.4.7",
|
||||
"npm:discord.js@^14.16.3": "14.16.3",
|
||||
"npm:express@^4.21.2": "4.21.2",
|
||||
"npm:ioredis@^5.5.0": "5.5.0",
|
||||
@@ -135,6 +139,12 @@
|
||||
"@types/node"
|
||||
]
|
||||
},
|
||||
"@types/cookie-parser@1.4.8_@types+express@5.0.0": {
|
||||
"integrity": "sha512-l37JqFrOJ9yQfRQkljb41l0xVphc7kg5JTjjr+pLRZ0IyZ49V4BQ8vbF4Ut2C2e+WH4al3xD3ZwYwIUfnbT4NQ==",
|
||||
"dependencies": [
|
||||
"@types/express"
|
||||
]
|
||||
},
|
||||
"@types/express-serve-static-core@5.0.1": {
|
||||
"integrity": "sha512-CRICJIl0N5cXDONAdlTv5ShATZ4HEwk6kDDIW2/w9qOWKg+NU/5F8wYRWCrONad0/UKkloNSmmyN/wX4rtpbVA==",
|
||||
"dependencies": [
|
||||
@@ -186,6 +196,9 @@
|
||||
"@types/send"
|
||||
]
|
||||
},
|
||||
"@types/validator@13.12.2": {
|
||||
"integrity": "sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA=="
|
||||
},
|
||||
"@types/ws@8.5.13": {
|
||||
"integrity": "sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==",
|
||||
"dependencies": [
|
||||
@@ -254,12 +267,22 @@
|
||||
"content-type@1.0.5": {
|
||||
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="
|
||||
},
|
||||
"cookie-parser@1.4.7": {
|
||||
"integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==",
|
||||
"dependencies": [
|
||||
"cookie@0.7.2",
|
||||
"cookie-signature"
|
||||
]
|
||||
},
|
||||
"cookie-signature@1.0.6": {
|
||||
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
|
||||
},
|
||||
"cookie@0.7.1": {
|
||||
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w=="
|
||||
},
|
||||
"cookie@0.7.2": {
|
||||
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="
|
||||
},
|
||||
"debug@2.6.9": {
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"dependencies": [
|
||||
@@ -350,7 +373,7 @@
|
||||
"body-parser",
|
||||
"content-disposition",
|
||||
"content-type",
|
||||
"cookie",
|
||||
"cookie@0.7.1",
|
||||
"cookie-signature",
|
||||
"debug@2.6.9",
|
||||
"depd",
|
||||
@@ -686,18 +709,123 @@
|
||||
}
|
||||
},
|
||||
"remote": {
|
||||
"https://deno.land/std@0.140.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74",
|
||||
"https://deno.land/std@0.140.0/_util/os.ts": "3b4c6e27febd119d36a416d7a97bd3b0251b77c88942c8f16ee5953ea13e2e49",
|
||||
"https://deno.land/std@0.140.0/bytes/bytes_list.ts": "67eb118e0b7891d2f389dad4add35856f4ad5faab46318ff99653456c23b025d",
|
||||
"https://deno.land/std@0.140.0/bytes/equals.ts": "fc16dff2090cced02497f16483de123dfa91e591029f985029193dfaa9d894c9",
|
||||
"https://deno.land/std@0.140.0/bytes/mod.ts": "763f97d33051cc3f28af1a688dfe2830841192a9fea0cbaa55f927b49d49d0bf",
|
||||
"https://deno.land/std@0.140.0/fmt/colors.ts": "30455035d6d728394781c10755351742dd731e3db6771b1843f9b9e490104d37",
|
||||
"https://deno.land/std@0.140.0/fs/_util.ts": "0fb24eb4bfebc2c194fb1afdb42b9c3dda12e368f43e8f2321f84fc77d42cb0f",
|
||||
"https://deno.land/std@0.140.0/fs/ensure_dir.ts": "9dc109c27df4098b9fc12d949612ae5c9c7169507660dcf9ad90631833209d9d",
|
||||
"https://deno.land/std@0.140.0/hash/sha256.ts": "803846c7a5a8a5a97f31defeb37d72f519086c880837129934f5d6f72102a8e8",
|
||||
"https://deno.land/std@0.140.0/io/buffer.ts": "bd0c4bf53db4b4be916ca5963e454bddfd3fcd45039041ea161dbf826817822b",
|
||||
"https://deno.land/std@0.140.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3",
|
||||
"https://deno.land/std@0.140.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09",
|
||||
"https://deno.land/std@0.140.0/path/_util.ts": "c1e9686d0164e29f7d880b2158971d805b6e0efc3110d0b3e24e4b8af2190d2b",
|
||||
"https://deno.land/std@0.140.0/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633",
|
||||
"https://deno.land/std@0.140.0/path/glob.ts": "cb5255638de1048973c3e69e420c77dc04f75755524cb3b2e160fe9277d939ee",
|
||||
"https://deno.land/std@0.140.0/path/mod.ts": "d3e68d0abb393fb0bf94a6d07c46ec31dc755b544b13144dee931d8d5f06a52d",
|
||||
"https://deno.land/std@0.140.0/path/posix.ts": "293cdaec3ecccec0a9cc2b534302dfe308adb6f10861fa183275d6695faace44",
|
||||
"https://deno.land/std@0.140.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9",
|
||||
"https://deno.land/std@0.140.0/path/win32.ts": "31811536855e19ba37a999cd8d1b62078235548d67902ece4aa6b814596dd757",
|
||||
"https://deno.land/std@0.140.0/streams/conversion.ts": "712585bfa0172a97fb68dd46e784ae8ad59d11b88079d6a4ab098ff42e697d21",
|
||||
"https://deno.land/std@0.186.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462",
|
||||
"https://deno.land/std@0.186.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3",
|
||||
"https://deno.land/std@0.186.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0",
|
||||
"https://deno.land/std@0.186.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b",
|
||||
"https://deno.land/std@0.186.0/path/_util.ts": "d7abb1e0dea065f427b89156e28cdeb32b045870acdf865833ba808a73b576d0",
|
||||
"https://deno.land/std@0.186.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000",
|
||||
"https://deno.land/std@0.186.0/path/glob.ts": "d479e0a695621c94d3fd7fe7abd4f9499caf32a8de13f25073451c6ef420a4e1",
|
||||
"https://deno.land/std@0.186.0/path/mod.ts": "ee161baec5ded6510ee1d1fb6a75a0f5e4b41f3f3301c92c716ecbdf7dae910d",
|
||||
"https://deno.land/std@0.186.0/path/posix.ts": "8b7c67ac338714b30c816079303d0285dd24af6b284f7ad63da5b27372a2c94d",
|
||||
"https://deno.land/std@0.186.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1",
|
||||
"https://deno.land/std@0.186.0/path/win32.ts": "d186344e5583bcbf8b18af416d13d82b35a317116e6460a5a3953508c3de5bba",
|
||||
"https://deno.land/std@0.197.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3",
|
||||
"https://deno.land/std@0.197.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee",
|
||||
"https://deno.land/std@0.197.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56",
|
||||
"https://deno.land/std@0.197.0/fs/_util.ts": "fbf57dcdc9f7bc8128d60301eece608246971a7836a3bb1e78da75314f08b978",
|
||||
"https://deno.land/std@0.197.0/fs/copy.ts": "b4f7fe87190d7b310c88a2d9ff845210c0a2b7b0a094ec509747359023beb7d6",
|
||||
"https://deno.land/std@0.197.0/fs/empty_dir.ts": "c3d2da4c7352fab1cf144a1ecfef58090769e8af633678e0f3fabaef98594688",
|
||||
"https://deno.land/std@0.197.0/fs/ensure_dir.ts": "dc64c4c75c64721d4e3fb681f1382f803ff3d2868f08563ff923fdd20d071c40",
|
||||
"https://deno.land/std@0.197.0/fs/ensure_file.ts": "c38602670bfaf259d86ca824a94e6cb9e5eb73757fefa4ebf43a90dd017d53d9",
|
||||
"https://deno.land/std@0.197.0/fs/ensure_link.ts": "c0f5b2f0ec094ed52b9128eccb1ee23362a617457aa0f699b145d4883f5b2fb4",
|
||||
"https://deno.land/std@0.197.0/fs/ensure_symlink.ts": "5006ab2f458159c56d689b53b1e48d57e05eeb1eaf64e677f7f76a30bc4fdba1",
|
||||
"https://deno.land/std@0.197.0/fs/eol.ts": "f1f2eb348a750c34500741987b21d65607f352cf7205f48f4319d417fff42842",
|
||||
"https://deno.land/std@0.197.0/fs/exists.ts": "29c26bca8584a22876be7cb8844f1b6c8fc35e9af514576b78f5c6884d7ed02d",
|
||||
"https://deno.land/std@0.197.0/fs/expand_glob.ts": "3e427436f4b3768727bd7de84169f10db75fe50b32e6dde567b8ae558a8d857a",
|
||||
"https://deno.land/std@0.197.0/fs/mod.ts": "bc3d0acd488cc7b42627044caf47d72019846d459279544e1934418955ba4898",
|
||||
"https://deno.land/std@0.197.0/fs/move.ts": "b4f8f46730b40c32ea3c0bc8eb0fd0e8139249a698883c7b3756424cf19785c9",
|
||||
"https://deno.land/std@0.197.0/fs/walk.ts": "21a3cc5ff39c38acc93575213f54d5f1d44c5c6614ed97603d171eb0bf56a565",
|
||||
"https://deno.land/std@0.197.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0",
|
||||
"https://deno.land/std@0.197.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b",
|
||||
"https://deno.land/std@0.197.0/path/_util.ts": "d7abb1e0dea065f427b89156e28cdeb32b045870acdf865833ba808a73b576d0",
|
||||
"https://deno.land/std@0.197.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000",
|
||||
"https://deno.land/std@0.197.0/path/glob.ts": "d479e0a695621c94d3fd7fe7abd4f9499caf32a8de13f25073451c6ef420a4e1",
|
||||
"https://deno.land/std@0.197.0/path/mod.ts": "f065032a7189404fdac3ad1a1551a9ac84751d2f25c431e101787846c86c79ef",
|
||||
"https://deno.land/std@0.197.0/path/posix.ts": "8b7c67ac338714b30c816079303d0285dd24af6b284f7ad63da5b27372a2c94d",
|
||||
"https://deno.land/std@0.197.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1",
|
||||
"https://deno.land/std@0.197.0/path/win32.ts": "4fca292f8d116fd6d62f243b8a61bd3d6835a9f0ede762ba5c01afe7c3c0aa12",
|
||||
"https://deno.land/std@0.85.0/_util/assert.ts": "2f868145a042a11d5ad0a3c748dcf580add8a0dbc0e876eaa0026303a5488f58",
|
||||
"https://deno.land/std@0.85.0/_util/os.ts": "e282950a0eaa96760c0cf11e7463e66babd15ec9157d4c9ed49cc0925686f6a7",
|
||||
"https://deno.land/std@0.85.0/fs/_util.ts": "f2ce811350236ea8c28450ed822a5f42a0892316515b1cd61321dec13569c56b",
|
||||
"https://deno.land/std@0.85.0/fs/copy.ts": "acc21e2569c92e715be48f40665a299cb995a4dce04145c3dd624791b885114c",
|
||||
"https://deno.land/std@0.85.0/fs/empty_dir.ts": "2edd70ff6405e1893e781a82aec8c574dfc748a7bb9d9ce8f0abdf002cdbba3f",
|
||||
"https://deno.land/std@0.85.0/fs/ensure_dir.ts": "f21262e788a707aaa2dd22064da7cd40e3b2f0f067e9b2aed1b288091170cc05",
|
||||
"https://deno.land/std@0.85.0/fs/ensure_file.ts": "84c7cff81ecedef3969e3fcd2d0c2aecd9bafea246cd18847deba7a54126134f",
|
||||
"https://deno.land/std@0.85.0/fs/ensure_link.ts": "e48abe5bf639389ee6f42bb8bdd8b7b2a4c93701cd618b12cdcad83ccea44f2e",
|
||||
"https://deno.land/std@0.85.0/fs/ensure_symlink.ts": "cbb2c908135808c0545c6304046b6ab5c024b0bb1832e69c819b58d9feee66ef",
|
||||
"https://deno.land/std@0.85.0/fs/eol.ts": "afaebaaac36f48c423b920c836551997715672b80a0fee9aa7667c181a94f2df",
|
||||
"https://deno.land/std@0.85.0/fs/exists.ts": "b0d2e31654819cc2a8d37df45d6b14686c0cc1d802e9ff09e902a63e98b85a00",
|
||||
"https://deno.land/std@0.85.0/fs/expand_glob.ts": "b5a8fcadf40eb7b034a1f807349cbace0ddb28c4e5a6b6aaf2d8ca925ba02f9f",
|
||||
"https://deno.land/std@0.85.0/fs/mod.ts": "26eee4b52a8c516e37d464094b080ff6822883e7f01ff0ba0a72b8dcd54b9927",
|
||||
"https://deno.land/std@0.85.0/fs/move.ts": "36697916a5cf2ebc7d298089a9a3ccc6b3af1eaecc173e57a9f5eb10f1f04221",
|
||||
"https://deno.land/std@0.85.0/fs/walk.ts": "8d37f2164a7397668842a7cb5d53b9e7bcd216462623b1b96abe519f76d7f8b9",
|
||||
"https://deno.land/std@0.85.0/path/_constants.ts": "1247fee4a79b70c89f23499691ef169b41b6ccf01887a0abd131009c5581b853",
|
||||
"https://deno.land/std@0.85.0/path/_interface.ts": "1fa73b02aaa24867e481a48492b44f2598cd9dfa513c7b34001437007d3642e4",
|
||||
"https://deno.land/std@0.85.0/path/_util.ts": "f4fa69aa3cbbd8568763bfc43c7236875015ba343602d8bafd332b4b4243681b",
|
||||
"https://deno.land/std@0.85.0/path/common.ts": "eaf03d08b569e8a87e674e4e265e099f237472b6fd135b3cbeae5827035ea14a",
|
||||
"https://deno.land/std@0.85.0/path/glob.ts": "4a524c1c9da3e79a9fdabdc6e850cd9e41bdf31e442856ffa19c5b123268ca95",
|
||||
"https://deno.land/std@0.85.0/path/mod.ts": "4465dc494f271b02569edbb4a18d727063b5dbd6ed84283ff906260970a15d12",
|
||||
"https://deno.land/std@0.85.0/path/posix.ts": "1408f8ba482a4dc5fc0a7cd7be28bbbff9608d2b3b5ffdcf288ae1228d959add",
|
||||
"https://deno.land/std@0.85.0/path/separator.ts": "8fdcf289b1b76fd726a508f57d3370ca029ae6976fcde5044007f062e643ff1c",
|
||||
"https://deno.land/std@0.85.0/path/win32.ts": "6ca052f54500f00cd7a5172fde62900626ab620dcd5bdcf4e6f5695d001ddef6",
|
||||
"https://deno.land/x/bcrypt@v0.3.0/mod.ts": "ff09bdae282583cf5f7d87efe37ddcecef7f14f6d12e8b8066a3058db8c6c2f7",
|
||||
"https://deno.land/x/bcrypt@v0.3.0/src/bcrypt/base64.ts": "b8266450a4f1eb6960f60f2f7986afc4dde6b45bd2d7ee7ba10789e67e17b9f7",
|
||||
"https://deno.land/x/bcrypt@v0.3.0/src/bcrypt/bcrypt.ts": "65819ce8e32d6e6a68f8753931237c58baa39b2573c1d7fac42f03d51499f242",
|
||||
"https://deno.land/x/bcrypt@v0.3.0/src/main.ts": "08d201b289c8d9c46f8839c69cd6625b213863db29775c7a200afc3b540e64f8",
|
||||
"https://deno.land/x/bcrypt@v0.3.0/src/worker.ts": "5a73bdfee9c9e622f47c9733d374b627dce52fb3ec1e74c8226698b3fc57ffac"
|
||||
"https://deno.land/x/bcrypt@v0.3.0/src/worker.ts": "5a73bdfee9c9e622f47c9733d374b627dce52fb3ec1e74c8226698b3fc57ffac",
|
||||
"https://deno.land/x/deno_cache@0.4.1/auth_tokens.ts": "5fee7e9155e78cedf3f6ff3efacffdb76ac1a76c86978658d9066d4fb0f7326e",
|
||||
"https://deno.land/x/deno_cache@0.4.1/cache.ts": "51f72f4299411193d780faac8c09d4e8cbee951f541121ef75fcc0e94e64c195",
|
||||
"https://deno.land/x/deno_cache@0.4.1/deno_dir.ts": "f2a9044ce8c7fe1109004cda6be96bf98b08f478ce77e7a07f866eff1bdd933f",
|
||||
"https://deno.land/x/deno_cache@0.4.1/deps.ts": "8974097d6c17e65d9a82d39377ae8af7d94d74c25c0cbb5855d2920e063f2343",
|
||||
"https://deno.land/x/deno_cache@0.4.1/dirs.ts": "d2fa473ef490a74f2dcb5abb4b9ab92a48d2b5b6320875df2dee64851fa64aa9",
|
||||
"https://deno.land/x/deno_cache@0.4.1/disk_cache.ts": "1f3f5232cba4c56412d93bdb324c624e95d5dd179d0578d2121e3ccdf55539f9",
|
||||
"https://deno.land/x/deno_cache@0.4.1/file_fetcher.ts": "07a6c5f8fd94bf50a116278cc6012b4921c70d2251d98ce1c9f3c352135c39f7",
|
||||
"https://deno.land/x/deno_cache@0.4.1/http_cache.ts": "f632e0d6ec4a5d61ae3987737a72caf5fcdb93670d21032ddb78df41131360cd",
|
||||
"https://deno.land/x/deno_cache@0.4.1/mod.ts": "ef1cda9235a93b89cb175fe648372fc0f785add2a43aa29126567a05e3e36195",
|
||||
"https://deno.land/x/deno_cache@0.4.1/util.ts": "8cb686526f4be5205b92c819ca2ce82220aa0a8dd3613ef0913f6dc269dbbcfe",
|
||||
"https://deno.land/x/dir@1.5.1/data_local_dir/mod.ts": "91eb1c4bfadfbeda30171007bac6d85aadacd43224a5ed721bbe56bc64e9eb66",
|
||||
"https://deno.land/x/emit@0.25.0/_utils.ts": "98412edc7aa29e77d592b54fbad00bdec1b05d0c25eb772a5f8edc9813e08d88",
|
||||
"https://deno.land/x/emit@0.25.0/emit.generated.js": "0728e0cd293b930db2532f8cb5087fdb77aee1f30a059207533780f40250fd6a",
|
||||
"https://deno.land/x/emit@0.25.0/mod.ts": "66ef8ddaedcfca033eeee851379af59ed3f0e0aa6e025e7cdd24e4e158d874f3",
|
||||
"https://deno.land/x/leaf@v1.0.4/constants.ts": "2b18c5be5a57cea4d3d6298d7c4c636e5db821c580c3197f9c9bcab65f8c3bf0",
|
||||
"https://deno.land/x/leaf@v1.0.4/functions/getFileInMem.ts": "cec6c3c6add22c0c3316d8301994ab583feac5c3052df3072ad12976ea2aeec4",
|
||||
"https://deno.land/x/leaf@v1.0.4/functions/getFilePath.ts": "80ce141c1bd9735d3b7961b6ec8736070475c296c342be6bb4e189483f020801",
|
||||
"https://deno.land/x/leaf@v1.0.4/functions/methods.ts": "b8beebdcc1c0fbae00cc61dc3fdca7209a5b5e08c1b955ccf0e6b04c85c6ee46",
|
||||
"https://deno.land/x/leaf@v1.0.4/leafCompiler.ts": "155ac29c04fe3a0f4d336a95058eebb1a14e291c31862356d11cd280e67563ce",
|
||||
"https://deno.land/x/leaf@v1.0.4/mod.ts": "2f7a4d2c804978c342a2092ff1d2ec05ffb0e52dfc57bdeac939d56df8254983",
|
||||
"https://deno.land/x/wasmbuild@0.14.1/cache.ts": "89eea5f3ce6035a1164b3e655c95f21300498920575ade23161421f5b01967f4",
|
||||
"https://deno.land/x/wasmbuild@0.14.1/loader.ts": "d98d195a715f823151cbc8baa3f32127337628379a02d9eb2a3c5902dbccfc02"
|
||||
},
|
||||
"workspace": {
|
||||
"dependencies": [
|
||||
"jsr:@gz/jwt@0.1",
|
||||
"jsr:@proxnet/undead-logging@^1.2.0",
|
||||
"jsr:@std/assert@1",
|
||||
"npm:@types/cookie-parser@^1.4.8",
|
||||
"npm:@types/express@5",
|
||||
"npm:@types/validator@^13.12.2",
|
||||
"npm:cookie-parser@^1.4.7",
|
||||
"npm:discord.js@^14.16.3",
|
||||
"npm:express@^4.21.2",
|
||||
"npm:ioredis@^5.5.0",
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
// @ts-types = "npm:@types/express"
|
||||
import express from "express";
|
||||
import Logging from "@proxnet/undead-logging";
|
||||
import { decode } from "@gz/jwt";
|
||||
import { Config } from "./config.ts";
|
||||
import { AuthType, User, UserTokenFormat } from "./data/users.ts";
|
||||
|
||||
const config = Config.getConfig();
|
||||
|
||||
const log = new Logging('APIUtils');
|
||||
|
||||
@@ -29,8 +34,6 @@ export function generateRandomString(length: number) {
|
||||
return randomString;
|
||||
}
|
||||
|
||||
const instanceId = generateRandomString(128);
|
||||
|
||||
export function checkQueryTypes<T>(typeDef: T) {
|
||||
return (rq: express.Request, rs: express.Response, nxt: express.NextFunction) => {
|
||||
for (const key in typeDef) {
|
||||
@@ -58,11 +61,11 @@ export function checkBodyTypes<T>(typeDef: T) {
|
||||
}
|
||||
|
||||
export function genericResponseFormat(failure: boolean, msg: string | null = null, data: object | null = null) {
|
||||
return { failed: failure, instance: instanceId, message: msg, data: data };
|
||||
return { failed: failure, message: msg, data: data };
|
||||
}
|
||||
export function genericResponse(failure: boolean, msg: string | null = null, data: object | null = null) {
|
||||
return (_rq: express.Request, rs: express.Response) => {
|
||||
rs.json({ failed: failure, instance: instanceId, message: msg, data: data });
|
||||
rs.json({ failed: failure, message: msg, data: data });
|
||||
};
|
||||
}
|
||||
type RecNetResponse = {
|
||||
@@ -165,4 +168,41 @@ export class RateLimiter {
|
||||
|
||||
}
|
||||
|
||||
export async function UserAuthentication(rq: express.Request, rs: express.Response, nxt: express.NextFunction) {
|
||||
|
||||
function returnUnauthorized() {
|
||||
rs.statusCode = 401;
|
||||
rs.json(genericResponseFormat(true, 'Authorization failed.'));
|
||||
}
|
||||
|
||||
const token: string | undefined = rq.cookies['token'];
|
||||
if (typeof token == 'undefined') {
|
||||
returnUnauthorized();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const decodedToken = await decode<UserTokenFormat>(token, config.auth.secret, { algorithm: "HS512" });
|
||||
|
||||
const invalid = [
|
||||
decodedToken.iss == config.web.publichost,
|
||||
decodedToken.nbf < Math.round(Date.now() / 1000),
|
||||
decodedToken.exp > Math.round(Date.now() / 1000),
|
||||
decodedToken.typ == AuthType.Web
|
||||
].includes(false);
|
||||
if (!invalid) {
|
||||
rs.locals.user = new User(decodedToken.sub);
|
||||
nxt();
|
||||
} else returnUnauthorized();
|
||||
|
||||
} catch (err) {
|
||||
returnUnauthorized();
|
||||
log.w(`User Authentication failed: ${err}`);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export type NoBody = Record<string | number | symbol, never>
|
||||
|
||||
export * as APIUtils from "./apiutils.ts"
|
||||
@@ -14,8 +14,8 @@ type RedisConfiguration = {
|
||||
type WebConfiguration = {
|
||||
port: number,
|
||||
host: string,
|
||||
nameserverHost: string,
|
||||
secureNameserverHost: boolean
|
||||
publichost: string,
|
||||
securepublichost: boolean
|
||||
}
|
||||
|
||||
type PublicConfiguration = {
|
||||
@@ -27,6 +27,7 @@ type PublicConfiguration = {
|
||||
}
|
||||
|
||||
type LoggingConfiguration = {
|
||||
notfound: boolean,
|
||||
debug: boolean,
|
||||
network: boolean
|
||||
}
|
||||
@@ -37,11 +38,15 @@ type DiscordConfiguration = {
|
||||
guildId: string
|
||||
}
|
||||
|
||||
type SecretConfiguration = {
|
||||
authSecret: string
|
||||
type AuthConfiguration = {
|
||||
secret: string,
|
||||
/**
|
||||
* In Hours
|
||||
*/
|
||||
timeout: number
|
||||
}
|
||||
|
||||
type RecaptchaConfiguration = {
|
||||
type TurnstileConfiguration = {
|
||||
sitekey: string,
|
||||
secret: string
|
||||
}
|
||||
@@ -52,8 +57,8 @@ export type GalvanicConfiguration = {
|
||||
public: PublicConfiguration,
|
||||
logging: LoggingConfiguration,
|
||||
discord: DiscordConfiguration | null,
|
||||
secrets: SecretConfiguration,
|
||||
recaptcha: RecaptchaConfiguration | null
|
||||
auth: AuthConfiguration,
|
||||
turnstile: TurnstileConfiguration | null
|
||||
}
|
||||
|
||||
export const defaultConfig: GalvanicConfiguration = {
|
||||
@@ -67,8 +72,8 @@ export const defaultConfig: GalvanicConfiguration = {
|
||||
web: {
|
||||
port: 3000,
|
||||
host: "127.0.0.1",
|
||||
nameserverHost: "127.0.0.1:3000",
|
||||
secureNameserverHost: false
|
||||
publichost: "127.0.0.1:3000",
|
||||
securepublichost: false,
|
||||
},
|
||||
public: {
|
||||
serverName: "Galvanic Corrosion",
|
||||
@@ -78,18 +83,20 @@ export const defaultConfig: GalvanicConfiguration = {
|
||||
maxLevels: 30
|
||||
},
|
||||
logging: {
|
||||
notfound: false,
|
||||
debug: false,
|
||||
network: false
|
||||
},
|
||||
discord: null,
|
||||
secrets: {
|
||||
authSecret: "CHANGE-ME-PLEASE"
|
||||
auth: {
|
||||
secret: "CHANGE-ME-PLEASE",
|
||||
timeout: 48
|
||||
},
|
||||
recaptcha: null
|
||||
turnstile: null
|
||||
}
|
||||
|
||||
/** The current configuration. Read and parsed only during startup. */
|
||||
let config: GalvanicConfiguration | undefined;
|
||||
let config: GalvanicConfiguration;
|
||||
try {
|
||||
if (!configurationExists()) generateDefaultConfig();
|
||||
config = JSON.parse(fs.readFileSync('./config.json').toString());
|
||||
@@ -113,4 +120,6 @@ export function getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
export const devMode = Deno.args.includes('--dev');
|
||||
|
||||
export * as Config from './config.ts';
|
||||
@@ -1,4 +1,4 @@
|
||||
import { encode, decode } from "@gz/jwt";
|
||||
import { decode } from "@gz/jwt";
|
||||
import { Config, GalvanicConfiguration } from "../config.ts";
|
||||
import Logging from "@proxnet/undead-logging";
|
||||
|
||||
@@ -6,7 +6,7 @@ const log = new Logging("Auth");
|
||||
|
||||
const config = Config.getConfig() as GalvanicConfiguration;
|
||||
|
||||
type TokenFormat = {
|
||||
export type ProfileTokenFormat = {
|
||||
iss: string;
|
||||
sub: number;
|
||||
nbf: number;
|
||||
@@ -30,7 +30,7 @@ export class GameAuthContext {
|
||||
|
||||
async decode() {
|
||||
try {
|
||||
const decoded = await decode(this.#rawToken, config.secrets.authSecret) as TokenFormat;
|
||||
const decoded = await decode(this.#rawToken, config.auth.secret) as ProfileTokenFormat;
|
||||
this.playerId = decoded.sub || null;
|
||||
const now = Math.round(Date.now() / 1000);
|
||||
|
||||
|
||||
58
src/data/captcha.ts
Normal file
58
src/data/captcha.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import Logging from "@proxnet/undead-logging";
|
||||
import { Config } from "../config.ts";
|
||||
|
||||
const log = new Logging("Turnstile");
|
||||
|
||||
type SiteVerifyParams = {
|
||||
secret: string,
|
||||
response: string,
|
||||
remoteip?: string
|
||||
}
|
||||
type SiteVerifyResponse = {
|
||||
success: boolean,
|
||||
challenge_ts: string,
|
||||
hostname: string,
|
||||
"error-codes": string[]
|
||||
}
|
||||
|
||||
class CAPTCHABase {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param response Turnstile response from client
|
||||
* @param remoteip Remote IP of client
|
||||
* @param local Override verification, return true for testing
|
||||
* @returns `null` if an error occurred, else boolean
|
||||
*/
|
||||
async siteVerify(response: string, remoteip?: string, local?: boolean) {
|
||||
if (local) return true;
|
||||
const config = Config.getConfig();
|
||||
if (typeof config == 'undefined') return null;
|
||||
if (config.turnstile == null) {
|
||||
log.e("Tried to verify Turnstile, but the config is null!");
|
||||
return null;
|
||||
}
|
||||
|
||||
const body: SiteVerifyParams = {
|
||||
secret: config.turnstile.secret,
|
||||
response: response,
|
||||
remoteip: remoteip
|
||||
}
|
||||
const res = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
|
||||
method: "POST",
|
||||
body: JSON.stringify(body),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
const resBody = await res.json() as SiteVerifyResponse;
|
||||
for (const error of resBody["error-codes"]) log.w(`Got Turnstile error: ${error}`);
|
||||
|
||||
return resBody.success;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const Turnstile = new CAPTCHABase();
|
||||
export default Turnstile;
|
||||
71
src/data/profiles.ts
Normal file
71
src/data/profiles.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { Redis } from "../db.ts";
|
||||
import { User } from "./users.ts";
|
||||
|
||||
interface ProfileInitOptions {
|
||||
username: string,
|
||||
user: User
|
||||
}
|
||||
|
||||
class Profile {
|
||||
|
||||
static async getUniqueId() {
|
||||
let id = Math.round(Math.random() * Math.pow(2, 31));
|
||||
while ((await Redis.Database.exists(Redis.buildKey(Redis.KeyGroups.Profiles.Root, String(id), Redis.KeyGroups.Profiles.Username))) >= 1) id = await this.getUniqueId();
|
||||
return id;
|
||||
}
|
||||
|
||||
static async byName(name: string) {
|
||||
const id = await Redis.Database.get(Redis.buildKey(Redis.KeyGroups.Profile_Usernames, name));
|
||||
if (id == null) return null;
|
||||
else return new Profile(parseInt(id, 10));
|
||||
}
|
||||
|
||||
static async init(options: ProfileInitOptions) {
|
||||
|
||||
const newId = await this.getUniqueId()
|
||||
|
||||
Redis.Database.set(Redis.buildKey(Redis.KeyGroups.Profiles.Root, newId, Redis.KeyGroups.Profiles.Username), options.username);
|
||||
Redis.Database.set(Redis.buildKey(Redis.KeyGroups.Profiles.Root, newId, Redis.KeyGroups.Profiles.User), options.user.getUuid());
|
||||
|
||||
return new Profile(newId);
|
||||
|
||||
}
|
||||
|
||||
#id: number;
|
||||
|
||||
#dbUserKey = `${Redis.KeyGroups.Profiles.Root}:${this.getId().toString()}`;
|
||||
|
||||
constructor(id: number) {
|
||||
|
||||
this.#id = id;
|
||||
|
||||
}
|
||||
|
||||
getId(): number {
|
||||
return this.#id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disassociate the profile with its parent user.
|
||||
*
|
||||
* This method is potentially dangerous if you don't have any logical references to this profile.
|
||||
*
|
||||
* Note that profiles cannot be completely deleted ("purged") [from the database].
|
||||
*/
|
||||
disassociate() {
|
||||
Redis.Database.del(Redis.buildKey(this.#dbUserKey, Redis.KeyGroups.Profiles.User));
|
||||
}
|
||||
|
||||
setAssociatedUser(user: User) {
|
||||
Redis.Database.set(Redis.buildKey(this.#dbUserKey, Redis.KeyGroups.Profiles.User), user.getUuid());
|
||||
}
|
||||
|
||||
async getAssociatedUser() {
|
||||
const data = await Redis.Database.get(Redis.buildKey(this.#dbUserKey, Redis.KeyGroups.Profiles.User));
|
||||
if (data == null) throw new Error(`Orphan profile '${this.getId()}' is not associated with a user.`);
|
||||
else return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Profile;
|
||||
@@ -1,45 +0,0 @@
|
||||
import Logging from "@proxnet/undead-logging";
|
||||
import { Config } from "../config.ts";
|
||||
|
||||
const log = new Logging("ReCAPTCHA");
|
||||
|
||||
type SiteVerifyParams = {
|
||||
secret: string,
|
||||
response: string,
|
||||
remoteip?: string
|
||||
}
|
||||
type SiteVerifyResponse = {
|
||||
success: boolean,
|
||||
challenge_ts: string,
|
||||
hostname: string,
|
||||
"error-codes": string[]
|
||||
}
|
||||
|
||||
class ReCAPTCHABase {
|
||||
|
||||
async siteVerify(response: string, remoteip?: string) {
|
||||
const config = Config.getConfig();
|
||||
if (typeof config == 'undefined') return null;
|
||||
if (config.recaptcha == null) {
|
||||
log.e("Tried to verify ReCAPTCHA, but the config is null!");
|
||||
return null;
|
||||
}
|
||||
|
||||
const body: SiteVerifyParams = {
|
||||
secret: config.recaptcha.secret,
|
||||
response: response,
|
||||
remoteip: remoteip
|
||||
}
|
||||
const res = await fetch('https://google.com/recaptcha/api/siteverify', {
|
||||
method: "POST",
|
||||
body: JSON.stringify(body)
|
||||
});
|
||||
|
||||
const resBody = await res.json() as SiteVerifyResponse;
|
||||
return resBody.success
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const Recaptcha = new ReCAPTCHABase();
|
||||
export default Recaptcha;
|
||||
@@ -1,53 +1,64 @@
|
||||
import * as bcrypt from "bcrypt";
|
||||
import { Redis } from "../db.ts";
|
||||
import Logging from "@proxnet/undead-logging";
|
||||
|
||||
const log = new Logging("UserConstruct");
|
||||
import * as JsonWebToken from "@gz/jwt";
|
||||
import { Config } from "../config.ts";
|
||||
import Profile from "./profiles.ts";
|
||||
|
||||
type UserInitOptions = {
|
||||
username: string,
|
||||
password: string,
|
||||
}
|
||||
|
||||
type UserCreatedObj = {
|
||||
user: User,
|
||||
backupcode: string
|
||||
export enum AuthType {
|
||||
Game,
|
||||
Web
|
||||
}
|
||||
|
||||
function randomASCII() {
|
||||
export type UserTokenFormat = {
|
||||
iss: string;
|
||||
sub: string;
|
||||
nbf: number;
|
||||
iat: number;
|
||||
exp: number;
|
||||
typ: AuthType;
|
||||
}
|
||||
|
||||
const config = Config.getConfig();
|
||||
|
||||
function randomASCII(length: number) {
|
||||
const codes = crypto.getRandomValues(new Uint8Array(512));
|
||||
const filteredCodes = codes.filter(val => (val >= 48 && val <= 57) || (val >= 65 && val <= 90) || (val >= 97 && val <= 122) );
|
||||
let str = String.fromCharCode(...filteredCodes);
|
||||
if (str.length < 32) str = randomASCII();
|
||||
return str.substring(0, 32);
|
||||
if (str.length < length) str = randomASCII(length);
|
||||
return str.substring(0, length);
|
||||
}
|
||||
|
||||
export class User {
|
||||
|
||||
static async exists(username: string) {
|
||||
static async existsByName(username: string) {
|
||||
return (await Redis.Database.exists(Redis.buildKey(Redis.KeyGroups.Usernames, username))) == 1;
|
||||
}
|
||||
|
||||
static async exists(uuid: string) {
|
||||
return (await Redis.Database.exists(Redis.buildKey(Redis.KeyGroups.Users.Root, uuid, Redis.KeyGroups.Profiles.Exists))) == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a user
|
||||
* @returns A `UserCreatedObj` with a reference to the new user if one was created, else `null` if the username already exists.
|
||||
* @returns A `User` if one was created, else `null` if the username already exists.
|
||||
*/
|
||||
static async init(options: UserInitOptions) {
|
||||
if (await User.exists(options.username)) return null;
|
||||
if (await User.existsByName(options.username)) return null;
|
||||
|
||||
const uuid = crypto.randomUUID();
|
||||
const backup = randomASCII();
|
||||
const backup = randomASCII(32);
|
||||
Redis.Database.set(Redis.buildKey(Redis.KeyGroups.Usernames, options.username), uuid);
|
||||
Redis.Database.set(Redis.buildKey(Redis.KeyGroups.Users.Root, uuid, Redis.KeyGroups.Users.Exists), 'i think so'); // there's probably a better way of knowing if a user really exists; if it works, it works
|
||||
Redis.Database.set(Redis.buildKey(Redis.KeyGroups.Users.Root, uuid, Redis.KeyGroups.Users.Username), options.username);
|
||||
Redis.Database.set(Redis.buildKey(Redis.KeyGroups.Users.Root, uuid, Redis.KeyGroups.Users.BackupCode), backup);
|
||||
Redis.Database.set(Redis.buildKey(Redis.KeyGroups.Users.Root, uuid, Redis.KeyGroups.Users.Password), await bcrypt.hash(options.password));
|
||||
|
||||
const user = new User(uuid);
|
||||
const res: UserCreatedObj = {
|
||||
user: user,
|
||||
backupcode: backup
|
||||
}
|
||||
return res;
|
||||
|
||||
return new User(uuid);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,6 +81,18 @@ export class User {
|
||||
return this.#uuid;
|
||||
}
|
||||
|
||||
async getToken() {
|
||||
const payload: UserTokenFormat = {
|
||||
iss: config.web.publichost,
|
||||
sub: this.#uuid,
|
||||
nbf: Math.round(Date.now() / 1000) - 200,
|
||||
iat: Math.round(Date.now() / 1000),
|
||||
exp: Math.round(Date.now() / 1000) + (config.auth.timeout * 60 * 60),
|
||||
typ: AuthType.Web
|
||||
};
|
||||
return await JsonWebToken.encode(payload, config.auth.secret, {algorithm: "HS512"});
|
||||
}
|
||||
|
||||
async validatePassword(password: string) {
|
||||
const hash = await Redis.Database.get(Redis.buildKey(Redis.KeyGroups.Users.Root, this.#uuid, Redis.KeyGroups.Users.Password));
|
||||
if (hash == null) throw new Error(`Hash for user ${this.#uuid} was not found`);
|
||||
@@ -85,17 +108,30 @@ export class User {
|
||||
return await Redis.Database.get(Redis.buildKey(Redis.KeyGroups.Users.Root, this.#uuid, Redis.KeyGroups.Users.BackupCode));
|
||||
}
|
||||
|
||||
async getAssociatedProfiles() {
|
||||
/**
|
||||
* Get all profiles associated with the user.
|
||||
*
|
||||
* Can return as `Set<Profile>` if `parse` is true, else `Set<number>`
|
||||
*/
|
||||
async getAssociatedProfiles(): Promise<Set<Profile>>;
|
||||
async getAssociatedProfiles(parse: true): Promise<Set<Profile>>;
|
||||
async getAssociatedProfiles(parse: false): Promise<Set<number>>;
|
||||
async getAssociatedProfiles(parse: boolean = true) {
|
||||
const list = await Redis.Database.smembers(Redis.buildKey(Redis.KeyGroups.Users.Root, this.#uuid, Redis.KeyGroups.Users.Profiles));
|
||||
return new Set<number>(list.filter(val => !Number.isNaN(parseInt(val, 10))).map(val => parseInt(val, 10)));
|
||||
if (parse) return new Set<Profile>(list.filter(val => !Number.isNaN(parseInt(val, 10))).map(val => new Profile(parseInt(val, 10))));
|
||||
else return new Set<number>(list.filter(val => !Number.isNaN(parseInt(val, 10))).map(val => parseInt(val, 10)));
|
||||
}
|
||||
|
||||
async removeAssociatedProfile(id: number) {
|
||||
await Redis.Database.srem(Redis.buildKey(Redis.KeyGroups.Users.Root, this.#uuid, Redis.KeyGroups.Users.Profiles), id);
|
||||
async removeAssociatedProfile(user: Profile) {
|
||||
await Redis.Database.srem(Redis.buildKey(Redis.KeyGroups.Users.Root, this.#uuid, Redis.KeyGroups.Users.Profiles), user.getId());
|
||||
}
|
||||
|
||||
async addAssociatedProfile(id: number) {
|
||||
await Redis.Database.sadd(Redis.buildKey(Redis.KeyGroups.Users.Root, this.#uuid, Redis.KeyGroups.Users.Profiles), id);
|
||||
async addAssociatedProfile(user: Profile) {
|
||||
await Redis.Database.sadd(Redis.buildKey(Redis.KeyGroups.Users.Root, this.#uuid, Redis.KeyGroups.Users.Profiles), user.getId());
|
||||
}
|
||||
|
||||
async getUsername() {
|
||||
return await Redis.Database.get(Redis.buildKey(Redis.KeyGroups.Users.Root, this.#uuid, Redis.KeyGroups.Users.Username));
|
||||
}
|
||||
|
||||
}
|
||||
25
src/db.ts
25
src/db.ts
@@ -20,11 +20,11 @@ Deno.addSignalListener('SIGINT', () => {
|
||||
});
|
||||
|
||||
export const Database = new Redis({
|
||||
port: config?.redis.port,
|
||||
host: config?.redis.host,
|
||||
username: config?.redis.username == "" ? undefined : config?.redis.username,
|
||||
password: config?.redis.password == "" ? undefined : config?.redis.password,
|
||||
db: config?.redis.db,
|
||||
port: config.redis.port,
|
||||
host: config.redis.host,
|
||||
username: config.redis.username == "" ? undefined : config.redis.username,
|
||||
password: config.redis.password == "" ? undefined : config.redis.password,
|
||||
db: config.redis.db,
|
||||
lazyConnect: true
|
||||
});
|
||||
Database.on('connect', async () => {
|
||||
@@ -35,13 +35,13 @@ Database.on('connect', async () => {
|
||||
});
|
||||
});
|
||||
Database.on('connecting', () => {
|
||||
log.i('Connecting to Redis..');
|
||||
log.n('Connecting to Redis..');
|
||||
});
|
||||
Database.on('error', (err) => {
|
||||
log.e(`Redis error: ${err.stack}`);
|
||||
});
|
||||
|
||||
export function buildKey(...args: string[]) {
|
||||
export function buildKey(...args: (string | number)[]) {
|
||||
return args.join(':');
|
||||
}
|
||||
export const KeyGroups = {
|
||||
@@ -50,18 +50,21 @@ export const KeyGroups = {
|
||||
Dynamic: "dynamic",
|
||||
Game: "game"
|
||||
},
|
||||
Ids: "profile-ids",
|
||||
Profile_Usernames: "profile-usernames",
|
||||
Profiles: {
|
||||
Root: "profiles"
|
||||
Root: "profiles",
|
||||
Exists: "exists",
|
||||
Username: "username",
|
||||
User: "user-association"
|
||||
},
|
||||
Usernames: "usernames",
|
||||
Users: {
|
||||
Root: "users",
|
||||
Exists: "exists",
|
||||
Username: "username",
|
||||
Password: "password",
|
||||
BackupCode: "backupcode",
|
||||
Profiles: "profiles",
|
||||
Meta: "meta"
|
||||
Profiles: "profiles"
|
||||
}
|
||||
}
|
||||
export * as Redis from "./db.ts";
|
||||
@@ -14,7 +14,7 @@ export const client = new discord.Client({ intents: [discord.GatewayIntentBits.G
|
||||
|
||||
client.once(discord.Events.ClientReady, client => {
|
||||
log.i(`Logged in to Discord as "${client.user.tag}"`);
|
||||
client.user?.setActivity(config?.public.motd, { type: discord.ActivityType.Custom });
|
||||
client.user?.setActivity(config.public.motd, { type: discord.ActivityType.Custom });
|
||||
});
|
||||
|
||||
let shuttingDown = false;
|
||||
@@ -27,12 +27,12 @@ Deno.addSignalListener('SIGINT', () => {
|
||||
});
|
||||
|
||||
export function login() {
|
||||
if (config?.discord?.token == Config.defaultConfig.discord?.token) {
|
||||
if (config.discord?.token == Config.defaultConfig.discord?.token) {
|
||||
log.i('Discord not configured, ignoring');
|
||||
return;
|
||||
}
|
||||
log.i(`Creating Discord connection..`);
|
||||
client.login(config?.discord?.token);
|
||||
client.login(config.discord?.token);
|
||||
}
|
||||
|
||||
export * as Discord from "./discord.ts";
|
||||
@@ -14,7 +14,7 @@ type ConfigMResult = {
|
||||
Data: (string | null)[] | null
|
||||
}
|
||||
|
||||
/** Get a dyamic config. */
|
||||
/** Get a dynamic config. */
|
||||
export async function getConfig(key: string) {
|
||||
const res: ConfigResult = {
|
||||
Status: ResultType.Found,
|
||||
|
||||
30
src/main.ts
30
src/main.ts
@@ -1,11 +1,13 @@
|
||||
import * as Log from "@proxnet/undead-logging";
|
||||
import * as Config from "./config.ts";
|
||||
// @ts-types = 'npm:@types/express'
|
||||
import express from "express";
|
||||
import { Database } from "./db.ts";
|
||||
import { APIUtils } from "./apiutils.ts";
|
||||
import { Discord } from "./discord.ts";
|
||||
import { User } from "./data/users.ts";
|
||||
import { generateRandomString } from "./apiutils.ts";
|
||||
// @ts-types = "npm:@types/express"
|
||||
import express from "express";
|
||||
|
||||
const instanceId = generateRandomString(64);
|
||||
|
||||
const log = new Log.default("Main");
|
||||
|
||||
@@ -17,7 +19,7 @@ if (typeof config == 'undefined') {
|
||||
log.e('Cannot start: Configuration is undefined');
|
||||
Deno.exit(1);
|
||||
}
|
||||
if (config.secrets.authSecret == Config.defaultConfig.secrets.authSecret) {
|
||||
if (config.auth.secret == Config.defaultConfig.auth.secret) {
|
||||
log.e(`Cannot start: Auth secret is default. Please change 'secrets.authSecret' in 'config.json'`);
|
||||
Deno.exit(1);
|
||||
}
|
||||
@@ -28,7 +30,7 @@ Log.MessageTypeVisibility.Debug = config.logging.debug;
|
||||
const port = config.web.port;
|
||||
const host = config.web.host;
|
||||
|
||||
log.i(`Starting HTTP server on http://${host}:${port}`);
|
||||
log.n(`Starting HTTP server on http://${host}:${port}`);
|
||||
|
||||
const app = express();
|
||||
|
||||
@@ -36,21 +38,22 @@ app.disable('etag');
|
||||
app.disable('x-powered-by');
|
||||
|
||||
app.use((rq: express.Request, rs: express.Response, nxt: express.NextFunction) => {
|
||||
rs.locals.auth = null;
|
||||
rs.setHeader('Instance', instanceId)
|
||||
log.n(`${APIUtils.getSrcIpDefault(rq)} ${rq.method} ${rq.originalUrl}`);
|
||||
nxt();
|
||||
});
|
||||
|
||||
app.get('/', APIUtils.genericResponse(false, `${config?.public.serverName} - ${config?.public.motd}`));
|
||||
|
||||
app.get('/debug', async (_rq, rs) => {
|
||||
const user = await User.init({ username: "testuser123", password: "foopass123" });
|
||||
log.i(String(user == null));
|
||||
|
||||
rs.sendStatus(200);
|
||||
app.get('/', (_rq, rs) => {
|
||||
rs.redirect('/web/settings');
|
||||
});
|
||||
app.get('/info', APIUtils.genericResponse(false, null, {
|
||||
name: config.public.serverName,
|
||||
motd: config.public.motd,
|
||||
sitekey: config.turnstile == null ? null : config.turnstile.sitekey
|
||||
}));
|
||||
|
||||
// content routes
|
||||
const webRouter = await import('./routes/web.ts');
|
||||
const nameserverRouter = await import('./routes/nameserver.ts');
|
||||
const apiRouter = await import('./routes/api.ts');
|
||||
const userRouter = await import('./routes/user.ts');
|
||||
@@ -58,6 +61,7 @@ const userRouter = await import('./routes/user.ts');
|
||||
app.use(nameserverRouter.route.path, nameserverRouter.route.router);
|
||||
app.use(apiRouter.route.path, apiRouter.route.router);
|
||||
app.use(userRouter.route.path, userRouter.route.router);
|
||||
app.use(webRouter.route.path, webRouter.route.router);
|
||||
|
||||
app.use((rq: express.Request, rs: express.Response) => {
|
||||
log.e(`${APIUtils.getSrcIpDefault(rq)} 404 ${rq.method} ${rq.url.toString()}`);
|
||||
|
||||
@@ -2,7 +2,7 @@ import { APIUtils } from "../apiutils.ts";
|
||||
import { Config } from "../config.ts";
|
||||
|
||||
const config = Config.getConfig() as Config.GalvanicConfiguration;
|
||||
const protocol = config.web.secureNameserverHost ? 'https' : 'http';
|
||||
const protocol = config.web.securepublichost ? 'https' : 'http';
|
||||
|
||||
export const route = APIUtils.createRouter('/ns');
|
||||
|
||||
@@ -21,17 +21,17 @@ type NameserverHosts = {
|
||||
}
|
||||
|
||||
const nameserver: NameserverHosts = {
|
||||
Auth: `${protocol}://${config.web.nameserverHost}/auth`,
|
||||
API: `${protocol}://${config.web.nameserverHost}`,
|
||||
WWW: `${protocol}://${config.web.nameserverHost}`,
|
||||
Notifications: `${protocol}://${config.web.nameserverHost}/notify`,
|
||||
Images: `${protocol}://${config.web.nameserverHost}/img`,
|
||||
CDN: `${protocol}://${config.web.nameserverHost}/cdn`,
|
||||
Commerce: `${protocol}://${config.web.nameserverHost}/commerce`,
|
||||
Matchmaking: `${protocol}://${config.web.nameserverHost}/match`,
|
||||
Storage: `${protocol}://${config.web.nameserverHost}/storage`,
|
||||
Chat: `${protocol}://${config.web.nameserverHost}/chat`,
|
||||
Leaderboard: `${protocol}://${config.web.nameserverHost}/leaderboard`
|
||||
Auth: `${protocol}://${config.web.publichost}/auth`,
|
||||
API: `${protocol}://${config.web.publichost}`,
|
||||
WWW: `${protocol}://${config.web.publichost}`,
|
||||
Notifications: `${protocol}://${config.web.publichost}/notify`,
|
||||
Images: `${protocol}://${config.web.publichost}/img`,
|
||||
CDN: `${protocol}://${config.web.publichost}/cdn`,
|
||||
Commerce: `${protocol}://${config.web.publichost}/commerce`,
|
||||
Matchmaking: `${protocol}://${config.web.publichost}/match`,
|
||||
Storage: `${protocol}://${config.web.publichost}/storage`,
|
||||
Chat: `${protocol}://${config.web.publichost}/chat`,
|
||||
Leaderboard: `${protocol}://${config.web.publichost}/leaderboard`
|
||||
}
|
||||
|
||||
route.router.get('*', (_rq, rs) => {
|
||||
|
||||
@@ -1,20 +1,29 @@
|
||||
import { APIUtils, getSrcIpDefault } from "../apiutils.ts";
|
||||
import { APIUtils, getSrcIpDefault, NoBody } from "../apiutils.ts";
|
||||
// @ts-types = "npm:@types/express"
|
||||
import express from "express";
|
||||
import { User } from "../data/users.ts";
|
||||
import Recaptcha from "../data/recaptcha.ts";
|
||||
import Turnstile from "../data/captcha.ts";
|
||||
import { Config } from "../config.ts";
|
||||
// @ts-types = "npm:@types/cookie-parser"
|
||||
import cookie_parser from "cookie-parser";
|
||||
|
||||
const config = Config.getConfig();
|
||||
|
||||
export const route = APIUtils.createRouter('/user');
|
||||
|
||||
type CreateUserBody = {
|
||||
username: string,
|
||||
password: string,
|
||||
recaptcha: string
|
||||
turnstile: string
|
||||
}
|
||||
type LoginBody = {
|
||||
username: string,
|
||||
password: string
|
||||
}
|
||||
|
||||
type CreatedUserResponse = {
|
||||
uuid: string,
|
||||
backupcode: string
|
||||
type MyDetailsResponse = {
|
||||
username: string,
|
||||
profiles: number[]
|
||||
}
|
||||
|
||||
const rateLimit = new APIUtils.RateLimiter(10, 1);
|
||||
@@ -23,31 +32,75 @@ route.router.post('/create',
|
||||
|
||||
rateLimit.middle(),
|
||||
express.json(),
|
||||
APIUtils.checkBodyTypes<CreateUserBody>({ username: "test", password: "test", recaptcha: "test" }),
|
||||
APIUtils.checkBodyTypes<CreateUserBody>({ username: "test", password: "test", turnstile: "test" }),
|
||||
|
||||
async (rq, rs) => {
|
||||
const body = rq.body as CreateUserBody;
|
||||
async (rq: express.Request<NoBody, NoBody, CreateUserBody>, rs: express.Response) => {
|
||||
|
||||
const recaptchaStatus = await Recaptcha.siteVerify(body.recaptcha, getSrcIpDefault(rq));
|
||||
if (recaptchaStatus) {
|
||||
const userinit = await User.init({ username: body.username, password: body.password });
|
||||
const turnstileStatus = await Turnstile.siteVerify(rq.body.turnstile, getSrcIpDefault(rq));
|
||||
if (turnstileStatus) {
|
||||
const userinit = await User.init({ username: rq.body.username, password: rq.body.password });
|
||||
if (userinit == null) {
|
||||
rs.statusCode = 400;
|
||||
rs.json(APIUtils.genericResponseFormat(true, "Username is already taken"));
|
||||
} else {
|
||||
|
||||
const res: CreatedUserResponse = {
|
||||
uuid: userinit.user.getUuid(),
|
||||
backupcode: userinit.backupcode
|
||||
}
|
||||
rs.json(APIUtils.genericResponseFormat(false, "User created successfully", res));
|
||||
|
||||
rs.cookie('token', await userinit.user.getToken());
|
||||
rs.json(APIUtils.genericResponseFormat(false, "User created successfully", { backupCode: userinit.backupcode }));
|
||||
}
|
||||
}
|
||||
else {
|
||||
rs.statusCode = 400;
|
||||
rs.json(APIUtils.genericResponseFormat(true, "ReCAPTCHA error"));
|
||||
rs.json(APIUtils.genericResponseFormat(true, "Turnstile error"));
|
||||
}
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
route.router.post('/login',
|
||||
|
||||
rateLimit.middle(),
|
||||
express.json(),
|
||||
APIUtils.checkBodyTypes<LoginBody>({ username: "asdf", password: "adsf" }),
|
||||
|
||||
async (rq: express.Request<NoBody, NoBody, LoginBody>, rs: express.Response) => {
|
||||
|
||||
const user = await User.byName(rq.body.username);
|
||||
if (user == null) {
|
||||
rs.statusCode = 400;
|
||||
rs.json(APIUtils.genericResponseFormat(true, "Unknown User"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (await user.validatePassword(rq.body.password)) {
|
||||
rs.cookie('token', await user.getToken(), { sameSite: true, maxAge: config.auth.timeout * 60 * 60 * 1000, secure: config.web.securepublichost && rq.secure ? true : undefined });
|
||||
rs.json(APIUtils.genericResponseFormat(false, "Logged In"));
|
||||
} else {
|
||||
rs.statusCode = 400;
|
||||
rs.json(APIUtils.genericResponseFormat(true, "Invalid Password"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
route.router.get('/me',
|
||||
|
||||
APIUtils.UserAuthentication,
|
||||
cookie_parser(),
|
||||
|
||||
async (_rq, rs) => {
|
||||
|
||||
const user = rs.locals.user;
|
||||
|
||||
const username = await user.getUsername();
|
||||
const profiles = await user.getAssociatedProfiles();
|
||||
|
||||
const res: MyDetailsResponse = {
|
||||
username: username == null ? "UnknownUser" : username,
|
||||
profiles: Array.from(profiles.values())
|
||||
}
|
||||
|
||||
rs.json(APIUtils.genericResponseFormat(false, null, res));
|
||||
|
||||
}
|
||||
|
||||
);
|
||||
12
src/routes/web.ts
Normal file
12
src/routes/web.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { APIUtils } from "../apiutils.ts";
|
||||
// @ts-types = "npm:@types/express"
|
||||
import express from "express";
|
||||
import { devMode } from "../config.ts";
|
||||
import path from "node:path";
|
||||
|
||||
export const route = APIUtils.createRouter('/web');
|
||||
|
||||
route.router.get('/', (_rq, rs) => {
|
||||
rs.redirect('/web/settings');
|
||||
});
|
||||
route.router.use(express.static(path.resolve(Deno.cwd() + '\\res\\web'), {etag: false, cacheControl: devMode ? false : true, extensions: [ 'html' ] }));
|
||||
@@ -1,9 +1,11 @@
|
||||
import { Authentication } from "../data/auth.ts";
|
||||
import { User } from "../data/users.ts";
|
||||
|
||||
declare global {
|
||||
namespace Express {
|
||||
interface Locals {
|
||||
auth: Authentication.GameAuthContext | null
|
||||
gameAuth: Authentication.GameAuthContext,
|
||||
user: User
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user