From e18d06036c4f4724e5194acdabf465e1b5173ae8 Mon Sep 17 00:00:00 2001 From: Ethan Wu Date: Sat, 8 Aug 2020 02:36:32 -0400 Subject: [PATCH 001/155] refactor(server): move most server code to be self-contained Move configuration for TypeScript, configuration for NYC, and tests to inside of the server/ directory. --- .lintstagedrc.js | 2 +- ava.config.cjs | 3 ++- package.json | 14 ++++++-------- server/.eslintrc.js | 4 +--- .nycrc.json => server/.nycrc.json | 0 server/{ => src}/api/admin/challs/delete.js | 0 server/{ => src}/api/admin/challs/get.js | 0 server/{ => src}/api/admin/challs/index.js | 0 server/{ => src}/api/admin/challs/list.js | 0 server/{ => src}/api/admin/challs/put.js | 0 server/{ => src}/api/admin/index.js | 0 server/{ => src}/api/admin/upload/index.js | 0 server/{ => src}/api/admin/upload/post.js | 0 server/{ => src}/api/admin/upload/query.js | 0 server/{ => src}/api/auth/index.js | 0 server/{ => src}/api/auth/login.js | 0 server/{ => src}/api/auth/recover.js | 0 server/{ => src}/api/auth/register.js | 0 server/{ => src}/api/auth/test.js | 0 server/{ => src}/api/auth/verify.js | 0 server/{ => src}/api/challs/get.js | 0 server/{ => src}/api/challs/index.js | 0 server/{ => src}/api/challs/solves.js | 0 server/{ => src}/api/challs/submit.js | 0 server/{ => src}/api/index.js | 0 server/{ => src}/api/integrations/client/config.js | 0 server/{ => src}/api/integrations/client/index.js | 0 .../{ => src}/api/integrations/ctftime/callback.js | 0 server/{ => src}/api/integrations/ctftime/index.js | 0 .../api/integrations/ctftime/leaderboard.js | 0 server/{ => src}/api/leaderboard/graph.js | 0 server/{ => src}/api/leaderboard/index.js | 0 server/{ => src}/api/leaderboard/now.js | 0 server/{ => src}/api/users/id.js | 0 server/{ => src}/api/users/index.js | 0 .../{ => src}/api/users/me-auth/ctftime/delete.js | 0 .../{ => src}/api/users/me-auth/ctftime/index.js | 0 server/{ => src}/api/users/me-auth/ctftime/put.js | 0 server/{ => src}/api/users/me-auth/email/delete.js | 0 server/{ => src}/api/users/me-auth/email/index.js | 0 server/{ => src}/api/users/me-auth/email/put.js | 0 server/{ => src}/api/users/me.js | 0 server/{ => src}/api/users/members/delete.js | 0 server/{ => src}/api/users/members/index.js | 0 server/{ => src}/api/users/members/list.js | 0 server/{ => src}/api/users/members/new.js | 0 server/{ => src}/api/users/update.js | 0 server/{ => src}/api/users/util.js | 0 server/{ => src}/app.js | 0 server/{ => src}/auth/index.ts | 0 server/{ => src}/auth/register.ts | 0 server/{ => src}/auth/token.ts | 0 server/{ => src}/cache/challs.js | 0 server/{ => src}/cache/client.js | 0 server/{ => src}/cache/index.js | 0 server/{ => src}/cache/leaderboard.js | 0 server/{ => src}/cache/login.js | 0 server/{ => src}/cache/timeouts.js | 0 server/{ => src}/challenges/Provider.ts | 0 server/{ => src}/challenges/index.ts | 0 server/{ => src}/challenges/types.ts | 0 server/{ => src}/challenges/util.ts | 0 server/{ => src}/config/client.ts | 0 server/{ => src}/config/server.ts | 0 server/{ => src}/config/util.ts | 0 server/{ => src}/database/challenges.ts | 0 server/{ => src}/database/db.ts | 0 server/{ => src}/database/index.ts | 0 server/{ => src}/database/members.ts | 0 server/{ => src}/database/migrate.ts | 0 server/{ => src}/database/solves.ts | 0 server/{ => src}/database/users.ts | 0 server/{ => src}/database/util.ts | 0 server/{ => src}/email/emails/verify.html | 0 server/{ => src}/email/emails/verify.txt | 0 server/{ => src}/email/index.ts | 0 server/{ => src}/email/provider.ts | 0 server/{ => src}/errors/index.js | 0 server/{ => src}/index.js | 0 server/{ => src}/leaderboard/calculate.js | 0 server/{ => src}/leaderboard/index.js | 0 server/{ => src}/leaderboard/samples.js | 0 .../providers/challenges/database/index.ts | 7 ++----- server/{ => src}/providers/emails/mailgun/index.ts | 0 server/{ => src}/providers/emails/ses/index.ts | 0 server/{ => src}/providers/emails/smtp/index.ts | 0 server/{ => src}/providers/uploads/dummy/index.ts | 0 server/{ => src}/providers/uploads/gcs/index.ts | 0 server/{ => src}/providers/uploads/local/index.ts | 0 server/{ => src}/responses.ts | 0 server/{ => src}/uploads/index.ts | 0 server/{ => src}/uploads/provider.ts | 0 server/{ => src}/util/index.ts | 0 server/{ => src}/util/normalize.ts | 0 server/{ => src}/util/perms.ts | 0 server/{ => src}/util/restrict.ts | 0 server/{ => src}/util/scores.ts | 0 server/{ => src}/util/validate.ts | 0 {test => server/test}/.eslintrc.js | 3 ++- {test => server/test}/_util.js | 6 +++--- {test => server/test}/conf-test.yaml | 0 {test => server/test}/integration/app.js | 2 +- {test => server/test}/integration/auth.js | 12 ++++++------ {test => server/test}/integration/challenges.js | 8 ++++---- {test => server/test}/integration/leaderboard.js | 4 ++-- {test => server/test}/integration/profile.js | 8 ++++---- {test => server/test}/integration/submit.js | 8 ++++---- {test => server/test}/integration/submitTiming.js | 8 ++++---- {test => server/test}/unit/challenges/index.js | 4 ++-- {test => server/test}/unit/config/util.js | 2 +- {test => server/test}/unit/rateLimit.js | 2 +- {test => server/test}/unit/restrict.js | 4 ++-- {test => server/test}/unit/util.js | 2 +- tsconfig.json => server/tsconfig.json | 6 +++--- tsconfig.strict.json | 8 -------- 115 files changed, 52 insertions(+), 65 deletions(-) rename .nycrc.json => server/.nycrc.json (100%) rename server/{ => src}/api/admin/challs/delete.js (100%) rename server/{ => src}/api/admin/challs/get.js (100%) rename server/{ => src}/api/admin/challs/index.js (100%) rename server/{ => src}/api/admin/challs/list.js (100%) rename server/{ => src}/api/admin/challs/put.js (100%) rename server/{ => src}/api/admin/index.js (100%) rename server/{ => src}/api/admin/upload/index.js (100%) rename server/{ => src}/api/admin/upload/post.js (100%) rename server/{ => src}/api/admin/upload/query.js (100%) rename server/{ => src}/api/auth/index.js (100%) rename server/{ => src}/api/auth/login.js (100%) rename server/{ => src}/api/auth/recover.js (100%) rename server/{ => src}/api/auth/register.js (100%) rename server/{ => src}/api/auth/test.js (100%) rename server/{ => src}/api/auth/verify.js (100%) rename server/{ => src}/api/challs/get.js (100%) rename server/{ => src}/api/challs/index.js (100%) rename server/{ => src}/api/challs/solves.js (100%) rename server/{ => src}/api/challs/submit.js (100%) rename server/{ => src}/api/index.js (100%) rename server/{ => src}/api/integrations/client/config.js (100%) rename server/{ => src}/api/integrations/client/index.js (100%) rename server/{ => src}/api/integrations/ctftime/callback.js (100%) rename server/{ => src}/api/integrations/ctftime/index.js (100%) rename server/{ => src}/api/integrations/ctftime/leaderboard.js (100%) rename server/{ => src}/api/leaderboard/graph.js (100%) rename server/{ => src}/api/leaderboard/index.js (100%) rename server/{ => src}/api/leaderboard/now.js (100%) rename server/{ => src}/api/users/id.js (100%) rename server/{ => src}/api/users/index.js (100%) rename server/{ => src}/api/users/me-auth/ctftime/delete.js (100%) rename server/{ => src}/api/users/me-auth/ctftime/index.js (100%) rename server/{ => src}/api/users/me-auth/ctftime/put.js (100%) rename server/{ => src}/api/users/me-auth/email/delete.js (100%) rename server/{ => src}/api/users/me-auth/email/index.js (100%) rename server/{ => src}/api/users/me-auth/email/put.js (100%) rename server/{ => src}/api/users/me.js (100%) rename server/{ => src}/api/users/members/delete.js (100%) rename server/{ => src}/api/users/members/index.js (100%) rename server/{ => src}/api/users/members/list.js (100%) rename server/{ => src}/api/users/members/new.js (100%) rename server/{ => src}/api/users/update.js (100%) rename server/{ => src}/api/users/util.js (100%) rename server/{ => src}/app.js (100%) rename server/{ => src}/auth/index.ts (100%) rename server/{ => src}/auth/register.ts (100%) rename server/{ => src}/auth/token.ts (100%) rename server/{ => src}/cache/challs.js (100%) rename server/{ => src}/cache/client.js (100%) rename server/{ => src}/cache/index.js (100%) rename server/{ => src}/cache/leaderboard.js (100%) rename server/{ => src}/cache/login.js (100%) rename server/{ => src}/cache/timeouts.js (100%) rename server/{ => src}/challenges/Provider.ts (100%) rename server/{ => src}/challenges/index.ts (100%) rename server/{ => src}/challenges/types.ts (100%) rename server/{ => src}/challenges/util.ts (100%) rename server/{ => src}/config/client.ts (100%) rename server/{ => src}/config/server.ts (100%) rename server/{ => src}/config/util.ts (100%) rename server/{ => src}/database/challenges.ts (100%) rename server/{ => src}/database/db.ts (100%) rename server/{ => src}/database/index.ts (100%) rename server/{ => src}/database/members.ts (100%) rename server/{ => src}/database/migrate.ts (100%) rename server/{ => src}/database/solves.ts (100%) rename server/{ => src}/database/users.ts (100%) rename server/{ => src}/database/util.ts (100%) rename server/{ => src}/email/emails/verify.html (100%) rename server/{ => src}/email/emails/verify.txt (100%) rename server/{ => src}/email/index.ts (100%) rename server/{ => src}/email/provider.ts (100%) rename server/{ => src}/errors/index.js (100%) rename server/{ => src}/index.js (100%) rename server/{ => src}/leaderboard/calculate.js (100%) rename server/{ => src}/leaderboard/index.js (100%) rename server/{ => src}/leaderboard/samples.js (100%) rename server/{ => src}/providers/challenges/database/index.ts (95%) rename server/{ => src}/providers/emails/mailgun/index.ts (100%) rename server/{ => src}/providers/emails/ses/index.ts (100%) rename server/{ => src}/providers/emails/smtp/index.ts (100%) rename server/{ => src}/providers/uploads/dummy/index.ts (100%) rename server/{ => src}/providers/uploads/gcs/index.ts (100%) rename server/{ => src}/providers/uploads/local/index.ts (100%) rename server/{ => src}/responses.ts (100%) rename server/{ => src}/uploads/index.ts (100%) rename server/{ => src}/uploads/provider.ts (100%) rename server/{ => src}/util/index.ts (100%) rename server/{ => src}/util/normalize.ts (100%) rename server/{ => src}/util/perms.ts (100%) rename server/{ => src}/util/restrict.ts (100%) rename server/{ => src}/util/scores.ts (100%) rename server/{ => src}/util/validate.ts (100%) rename {test => server/test}/.eslintrc.js (80%) rename {test => server/test}/_util.js (86%) rename {test => server/test}/conf-test.yaml (100%) rename {test => server/test}/integration/app.js (85%) rename {test => server/test}/integration/auth.js (89%) rename {test => server/test}/integration/challenges.js (86%) rename {test => server/test}/integration/leaderboard.js (80%) rename {test => server/test}/integration/profile.js (79%) rename {test => server/test}/integration/submit.js (93%) rename {test => server/test}/integration/submitTiming.js (86%) rename {test => server/test}/unit/challenges/index.js (59%) rename {test => server/test}/unit/config/util.js (92%) rename {test => server/test}/unit/rateLimit.js (92%) rename {test => server/test}/unit/restrict.js (95%) rename {test => server/test}/unit/util.js (71%) rename tsconfig.json => server/tsconfig.json (82%) delete mode 100644 tsconfig.strict.json diff --git a/.lintstagedrc.js b/.lintstagedrc.js index d05720ee5..332e4066c 100644 --- a/.lintstagedrc.js +++ b/.lintstagedrc.js @@ -5,5 +5,5 @@ const cli = new CLIEngine({}) module.exports = { '*.{j,t}s?(x)': files => 'eslint --max-warnings=0 --fix ' +files.filter(file => !cli.isPathIgnored(file)).join(' '), - '*.ts?(x)': () => 'tsc --noEmit' + 'server/**/*.ts?(x)': () => 'tsc --noEmit -p server' } diff --git a/ava.config.cjs b/ava.config.cjs index aed17ecaa..106656554 100644 --- a/ava.config.cjs +++ b/ava.config.cjs @@ -1,3 +1,4 @@ +// Server tests - placed at root because AVA requires it require('dotenv').config() module.exports = { @@ -5,7 +6,7 @@ module.exports = { API_ENDPOINT: '/api/v1' }, files: [ - 'test/**/*.js' + 'server/test/**/*.js' ], timeout: '30000' } diff --git a/package.json b/package.json index 4dbb5b21a..b723b373d 100644 --- a/package.json +++ b/package.json @@ -4,22 +4,20 @@ "license": "MIT", "private": true, "scripts": { - "lint": "sh -c \"tsc --noEmit ; eslint .\"", - "lint:strict": "sh -c \"tsc --noEmit -p tsconfig.strict.json ; eslint .\"", + "lint": "sh -c \"tsc --noEmit -p server ; eslint .\"", "start": "node --enable-source-maps --unhandled-rejections=strict dist/server/index.js", "migrate": "yarn build:ts && cross-env RCTF_DATABASE_MIGRATE=only yarn start", "build:client": "preact build --src client/src --template client/index.html --dest dist/build --no-prerender --no-inline-css", - "build:ts": "tsc && yarn copy-static", + "build:ts": "tsc -p server && yarn copy-static", "build": "yarn build:ts && yarn build:client", "watch:client": "cross-env NODE_ENV=development preact watch --src client/src --template client/index.html", "watch:server": "nodemon --enable-source-maps dist/server/index.js | pino-pretty -c -t", - "watch:ts": "tsc -w", + "watch:ts": "tsc -w -p server", "dev": "yarn build:ts && concurrently -k -t \"HH:mm:ss.SSS\" -p \"[{time}]\" -c \"cyan,green,red\" \"yarn:watch:*\"", - "test": "yarn build && nyc --skip-full ava", + "test": "yarn build && nyc --nycrc-path server/.nycrc.json --skip-full ava", "test:slim": "ava", - "test:report": "nyc --skip-full --reporter=lcov ava", - "commit": "git cz", - "copy-static": "cpy \"server/**/*\" \"!**/*.{j,t}s\" \"migrations\" dist/ --parents" + "test:report": "nyc --nycrc-path server/.nycrc.json --skip-full --reporter=lcov ava", + "copy-static": "cpy --cwd=server/src \"**/*\" \"!**/*.{j,t}s\" ../../dist/server --parents && cpy migrations dist/ --parents" }, "nodemonConfig": { "watch": [ diff --git a/server/.eslintrc.js b/server/.eslintrc.js index 3432b5145..a96ca65b9 100644 --- a/server/.eslintrc.js +++ b/server/.eslintrc.js @@ -1,5 +1,3 @@ -const path = require('path') - module.exports = { env: { node: true @@ -10,7 +8,7 @@ module.exports = { }, parser: '@typescript-eslint/parser', parserOptions: { - tsconfigRootDir: path.dirname(__dirname), + tsconfigRootDir: __dirname, project: ['./tsconfig.json'] }, overrides: [{ diff --git a/.nycrc.json b/server/.nycrc.json similarity index 100% rename from .nycrc.json rename to server/.nycrc.json diff --git a/server/api/admin/challs/delete.js b/server/src/api/admin/challs/delete.js similarity index 100% rename from server/api/admin/challs/delete.js rename to server/src/api/admin/challs/delete.js diff --git a/server/api/admin/challs/get.js b/server/src/api/admin/challs/get.js similarity index 100% rename from server/api/admin/challs/get.js rename to server/src/api/admin/challs/get.js diff --git a/server/api/admin/challs/index.js b/server/src/api/admin/challs/index.js similarity index 100% rename from server/api/admin/challs/index.js rename to server/src/api/admin/challs/index.js diff --git a/server/api/admin/challs/list.js b/server/src/api/admin/challs/list.js similarity index 100% rename from server/api/admin/challs/list.js rename to server/src/api/admin/challs/list.js diff --git a/server/api/admin/challs/put.js b/server/src/api/admin/challs/put.js similarity index 100% rename from server/api/admin/challs/put.js rename to server/src/api/admin/challs/put.js diff --git a/server/api/admin/index.js b/server/src/api/admin/index.js similarity index 100% rename from server/api/admin/index.js rename to server/src/api/admin/index.js diff --git a/server/api/admin/upload/index.js b/server/src/api/admin/upload/index.js similarity index 100% rename from server/api/admin/upload/index.js rename to server/src/api/admin/upload/index.js diff --git a/server/api/admin/upload/post.js b/server/src/api/admin/upload/post.js similarity index 100% rename from server/api/admin/upload/post.js rename to server/src/api/admin/upload/post.js diff --git a/server/api/admin/upload/query.js b/server/src/api/admin/upload/query.js similarity index 100% rename from server/api/admin/upload/query.js rename to server/src/api/admin/upload/query.js diff --git a/server/api/auth/index.js b/server/src/api/auth/index.js similarity index 100% rename from server/api/auth/index.js rename to server/src/api/auth/index.js diff --git a/server/api/auth/login.js b/server/src/api/auth/login.js similarity index 100% rename from server/api/auth/login.js rename to server/src/api/auth/login.js diff --git a/server/api/auth/recover.js b/server/src/api/auth/recover.js similarity index 100% rename from server/api/auth/recover.js rename to server/src/api/auth/recover.js diff --git a/server/api/auth/register.js b/server/src/api/auth/register.js similarity index 100% rename from server/api/auth/register.js rename to server/src/api/auth/register.js diff --git a/server/api/auth/test.js b/server/src/api/auth/test.js similarity index 100% rename from server/api/auth/test.js rename to server/src/api/auth/test.js diff --git a/server/api/auth/verify.js b/server/src/api/auth/verify.js similarity index 100% rename from server/api/auth/verify.js rename to server/src/api/auth/verify.js diff --git a/server/api/challs/get.js b/server/src/api/challs/get.js similarity index 100% rename from server/api/challs/get.js rename to server/src/api/challs/get.js diff --git a/server/api/challs/index.js b/server/src/api/challs/index.js similarity index 100% rename from server/api/challs/index.js rename to server/src/api/challs/index.js diff --git a/server/api/challs/solves.js b/server/src/api/challs/solves.js similarity index 100% rename from server/api/challs/solves.js rename to server/src/api/challs/solves.js diff --git a/server/api/challs/submit.js b/server/src/api/challs/submit.js similarity index 100% rename from server/api/challs/submit.js rename to server/src/api/challs/submit.js diff --git a/server/api/index.js b/server/src/api/index.js similarity index 100% rename from server/api/index.js rename to server/src/api/index.js diff --git a/server/api/integrations/client/config.js b/server/src/api/integrations/client/config.js similarity index 100% rename from server/api/integrations/client/config.js rename to server/src/api/integrations/client/config.js diff --git a/server/api/integrations/client/index.js b/server/src/api/integrations/client/index.js similarity index 100% rename from server/api/integrations/client/index.js rename to server/src/api/integrations/client/index.js diff --git a/server/api/integrations/ctftime/callback.js b/server/src/api/integrations/ctftime/callback.js similarity index 100% rename from server/api/integrations/ctftime/callback.js rename to server/src/api/integrations/ctftime/callback.js diff --git a/server/api/integrations/ctftime/index.js b/server/src/api/integrations/ctftime/index.js similarity index 100% rename from server/api/integrations/ctftime/index.js rename to server/src/api/integrations/ctftime/index.js diff --git a/server/api/integrations/ctftime/leaderboard.js b/server/src/api/integrations/ctftime/leaderboard.js similarity index 100% rename from server/api/integrations/ctftime/leaderboard.js rename to server/src/api/integrations/ctftime/leaderboard.js diff --git a/server/api/leaderboard/graph.js b/server/src/api/leaderboard/graph.js similarity index 100% rename from server/api/leaderboard/graph.js rename to server/src/api/leaderboard/graph.js diff --git a/server/api/leaderboard/index.js b/server/src/api/leaderboard/index.js similarity index 100% rename from server/api/leaderboard/index.js rename to server/src/api/leaderboard/index.js diff --git a/server/api/leaderboard/now.js b/server/src/api/leaderboard/now.js similarity index 100% rename from server/api/leaderboard/now.js rename to server/src/api/leaderboard/now.js diff --git a/server/api/users/id.js b/server/src/api/users/id.js similarity index 100% rename from server/api/users/id.js rename to server/src/api/users/id.js diff --git a/server/api/users/index.js b/server/src/api/users/index.js similarity index 100% rename from server/api/users/index.js rename to server/src/api/users/index.js diff --git a/server/api/users/me-auth/ctftime/delete.js b/server/src/api/users/me-auth/ctftime/delete.js similarity index 100% rename from server/api/users/me-auth/ctftime/delete.js rename to server/src/api/users/me-auth/ctftime/delete.js diff --git a/server/api/users/me-auth/ctftime/index.js b/server/src/api/users/me-auth/ctftime/index.js similarity index 100% rename from server/api/users/me-auth/ctftime/index.js rename to server/src/api/users/me-auth/ctftime/index.js diff --git a/server/api/users/me-auth/ctftime/put.js b/server/src/api/users/me-auth/ctftime/put.js similarity index 100% rename from server/api/users/me-auth/ctftime/put.js rename to server/src/api/users/me-auth/ctftime/put.js diff --git a/server/api/users/me-auth/email/delete.js b/server/src/api/users/me-auth/email/delete.js similarity index 100% rename from server/api/users/me-auth/email/delete.js rename to server/src/api/users/me-auth/email/delete.js diff --git a/server/api/users/me-auth/email/index.js b/server/src/api/users/me-auth/email/index.js similarity index 100% rename from server/api/users/me-auth/email/index.js rename to server/src/api/users/me-auth/email/index.js diff --git a/server/api/users/me-auth/email/put.js b/server/src/api/users/me-auth/email/put.js similarity index 100% rename from server/api/users/me-auth/email/put.js rename to server/src/api/users/me-auth/email/put.js diff --git a/server/api/users/me.js b/server/src/api/users/me.js similarity index 100% rename from server/api/users/me.js rename to server/src/api/users/me.js diff --git a/server/api/users/members/delete.js b/server/src/api/users/members/delete.js similarity index 100% rename from server/api/users/members/delete.js rename to server/src/api/users/members/delete.js diff --git a/server/api/users/members/index.js b/server/src/api/users/members/index.js similarity index 100% rename from server/api/users/members/index.js rename to server/src/api/users/members/index.js diff --git a/server/api/users/members/list.js b/server/src/api/users/members/list.js similarity index 100% rename from server/api/users/members/list.js rename to server/src/api/users/members/list.js diff --git a/server/api/users/members/new.js b/server/src/api/users/members/new.js similarity index 100% rename from server/api/users/members/new.js rename to server/src/api/users/members/new.js diff --git a/server/api/users/update.js b/server/src/api/users/update.js similarity index 100% rename from server/api/users/update.js rename to server/src/api/users/update.js diff --git a/server/api/users/util.js b/server/src/api/users/util.js similarity index 100% rename from server/api/users/util.js rename to server/src/api/users/util.js diff --git a/server/app.js b/server/src/app.js similarity index 100% rename from server/app.js rename to server/src/app.js diff --git a/server/auth/index.ts b/server/src/auth/index.ts similarity index 100% rename from server/auth/index.ts rename to server/src/auth/index.ts diff --git a/server/auth/register.ts b/server/src/auth/register.ts similarity index 100% rename from server/auth/register.ts rename to server/src/auth/register.ts diff --git a/server/auth/token.ts b/server/src/auth/token.ts similarity index 100% rename from server/auth/token.ts rename to server/src/auth/token.ts diff --git a/server/cache/challs.js b/server/src/cache/challs.js similarity index 100% rename from server/cache/challs.js rename to server/src/cache/challs.js diff --git a/server/cache/client.js b/server/src/cache/client.js similarity index 100% rename from server/cache/client.js rename to server/src/cache/client.js diff --git a/server/cache/index.js b/server/src/cache/index.js similarity index 100% rename from server/cache/index.js rename to server/src/cache/index.js diff --git a/server/cache/leaderboard.js b/server/src/cache/leaderboard.js similarity index 100% rename from server/cache/leaderboard.js rename to server/src/cache/leaderboard.js diff --git a/server/cache/login.js b/server/src/cache/login.js similarity index 100% rename from server/cache/login.js rename to server/src/cache/login.js diff --git a/server/cache/timeouts.js b/server/src/cache/timeouts.js similarity index 100% rename from server/cache/timeouts.js rename to server/src/cache/timeouts.js diff --git a/server/challenges/Provider.ts b/server/src/challenges/Provider.ts similarity index 100% rename from server/challenges/Provider.ts rename to server/src/challenges/Provider.ts diff --git a/server/challenges/index.ts b/server/src/challenges/index.ts similarity index 100% rename from server/challenges/index.ts rename to server/src/challenges/index.ts diff --git a/server/challenges/types.ts b/server/src/challenges/types.ts similarity index 100% rename from server/challenges/types.ts rename to server/src/challenges/types.ts diff --git a/server/challenges/util.ts b/server/src/challenges/util.ts similarity index 100% rename from server/challenges/util.ts rename to server/src/challenges/util.ts diff --git a/server/config/client.ts b/server/src/config/client.ts similarity index 100% rename from server/config/client.ts rename to server/src/config/client.ts diff --git a/server/config/server.ts b/server/src/config/server.ts similarity index 100% rename from server/config/server.ts rename to server/src/config/server.ts diff --git a/server/config/util.ts b/server/src/config/util.ts similarity index 100% rename from server/config/util.ts rename to server/src/config/util.ts diff --git a/server/database/challenges.ts b/server/src/database/challenges.ts similarity index 100% rename from server/database/challenges.ts rename to server/src/database/challenges.ts diff --git a/server/database/db.ts b/server/src/database/db.ts similarity index 100% rename from server/database/db.ts rename to server/src/database/db.ts diff --git a/server/database/index.ts b/server/src/database/index.ts similarity index 100% rename from server/database/index.ts rename to server/src/database/index.ts diff --git a/server/database/members.ts b/server/src/database/members.ts similarity index 100% rename from server/database/members.ts rename to server/src/database/members.ts diff --git a/server/database/migrate.ts b/server/src/database/migrate.ts similarity index 100% rename from server/database/migrate.ts rename to server/src/database/migrate.ts diff --git a/server/database/solves.ts b/server/src/database/solves.ts similarity index 100% rename from server/database/solves.ts rename to server/src/database/solves.ts diff --git a/server/database/users.ts b/server/src/database/users.ts similarity index 100% rename from server/database/users.ts rename to server/src/database/users.ts diff --git a/server/database/util.ts b/server/src/database/util.ts similarity index 100% rename from server/database/util.ts rename to server/src/database/util.ts diff --git a/server/email/emails/verify.html b/server/src/email/emails/verify.html similarity index 100% rename from server/email/emails/verify.html rename to server/src/email/emails/verify.html diff --git a/server/email/emails/verify.txt b/server/src/email/emails/verify.txt similarity index 100% rename from server/email/emails/verify.txt rename to server/src/email/emails/verify.txt diff --git a/server/email/index.ts b/server/src/email/index.ts similarity index 100% rename from server/email/index.ts rename to server/src/email/index.ts diff --git a/server/email/provider.ts b/server/src/email/provider.ts similarity index 100% rename from server/email/provider.ts rename to server/src/email/provider.ts diff --git a/server/errors/index.js b/server/src/errors/index.js similarity index 100% rename from server/errors/index.js rename to server/src/errors/index.js diff --git a/server/index.js b/server/src/index.js similarity index 100% rename from server/index.js rename to server/src/index.js diff --git a/server/leaderboard/calculate.js b/server/src/leaderboard/calculate.js similarity index 100% rename from server/leaderboard/calculate.js rename to server/src/leaderboard/calculate.js diff --git a/server/leaderboard/index.js b/server/src/leaderboard/index.js similarity index 100% rename from server/leaderboard/index.js rename to server/src/leaderboard/index.js diff --git a/server/leaderboard/samples.js b/server/src/leaderboard/samples.js similarity index 100% rename from server/leaderboard/samples.js rename to server/src/leaderboard/samples.js diff --git a/server/providers/challenges/database/index.ts b/server/src/providers/challenges/database/index.ts similarity index 95% rename from server/providers/challenges/database/index.ts rename to server/src/providers/challenges/database/index.ts index 8acffdce7..70b6ca8d8 100644 --- a/server/providers/challenges/database/index.ts +++ b/server/src/providers/challenges/database/index.ts @@ -38,14 +38,11 @@ class DatabaseProvider extends EventEmitter implements Provider { } challengeToRow (chall: Challenge): DatabaseChallenge { - chall = deepCopy(chall) - - const id = chall.id - delete chall.id + const { id, ...data } = deepCopy(chall) return { id, - data: chall + data } } diff --git a/server/providers/emails/mailgun/index.ts b/server/src/providers/emails/mailgun/index.ts similarity index 100% rename from server/providers/emails/mailgun/index.ts rename to server/src/providers/emails/mailgun/index.ts diff --git a/server/providers/emails/ses/index.ts b/server/src/providers/emails/ses/index.ts similarity index 100% rename from server/providers/emails/ses/index.ts rename to server/src/providers/emails/ses/index.ts diff --git a/server/providers/emails/smtp/index.ts b/server/src/providers/emails/smtp/index.ts similarity index 100% rename from server/providers/emails/smtp/index.ts rename to server/src/providers/emails/smtp/index.ts diff --git a/server/providers/uploads/dummy/index.ts b/server/src/providers/uploads/dummy/index.ts similarity index 100% rename from server/providers/uploads/dummy/index.ts rename to server/src/providers/uploads/dummy/index.ts diff --git a/server/providers/uploads/gcs/index.ts b/server/src/providers/uploads/gcs/index.ts similarity index 100% rename from server/providers/uploads/gcs/index.ts rename to server/src/providers/uploads/gcs/index.ts diff --git a/server/providers/uploads/local/index.ts b/server/src/providers/uploads/local/index.ts similarity index 100% rename from server/providers/uploads/local/index.ts rename to server/src/providers/uploads/local/index.ts diff --git a/server/responses.ts b/server/src/responses.ts similarity index 100% rename from server/responses.ts rename to server/src/responses.ts diff --git a/server/uploads/index.ts b/server/src/uploads/index.ts similarity index 100% rename from server/uploads/index.ts rename to server/src/uploads/index.ts diff --git a/server/uploads/provider.ts b/server/src/uploads/provider.ts similarity index 100% rename from server/uploads/provider.ts rename to server/src/uploads/provider.ts diff --git a/server/util/index.ts b/server/src/util/index.ts similarity index 100% rename from server/util/index.ts rename to server/src/util/index.ts diff --git a/server/util/normalize.ts b/server/src/util/normalize.ts similarity index 100% rename from server/util/normalize.ts rename to server/src/util/normalize.ts diff --git a/server/util/perms.ts b/server/src/util/perms.ts similarity index 100% rename from server/util/perms.ts rename to server/src/util/perms.ts diff --git a/server/util/restrict.ts b/server/src/util/restrict.ts similarity index 100% rename from server/util/restrict.ts rename to server/src/util/restrict.ts diff --git a/server/util/scores.ts b/server/src/util/scores.ts similarity index 100% rename from server/util/scores.ts rename to server/src/util/scores.ts diff --git a/server/util/validate.ts b/server/src/util/validate.ts similarity index 100% rename from server/util/validate.ts rename to server/src/util/validate.ts diff --git a/test/.eslintrc.js b/server/test/.eslintrc.js similarity index 80% rename from test/.eslintrc.js rename to server/test/.eslintrc.js index 96660d914..733cfedd0 100644 --- a/test/.eslintrc.js +++ b/server/test/.eslintrc.js @@ -5,5 +5,6 @@ module.exports = { plugins: [ ], rules: { - } + }, + parser: 'espree' } diff --git a/test/_util.js b/server/test/_util.js similarity index 86% rename from test/_util.js rename to server/test/_util.js index 617e8dc2e..b11326972 100644 --- a/test/_util.js +++ b/server/test/_util.js @@ -1,8 +1,8 @@ require('ava') const { v4: uuidv4 } = require('uuid') -const { default: config } = require('../dist/server/config/server') -const db = require('../dist/server/database') +const { default: config } = require('../../dist/server/config/server') +const db = require('../../dist/server/database') const ret = { // Generate only valid parameters @@ -31,7 +31,7 @@ const ret = { }, getFirstLoadedChallenge: () => { // Load on-demand - const challenges = require('../dist/server/challenges') + const challenges = require('../../dist/server/challenges') return new Promise((resolve, reject) => { const check = () => { try { diff --git a/test/conf-test.yaml b/server/test/conf-test.yaml similarity index 100% rename from test/conf-test.yaml rename to server/test/conf-test.yaml diff --git a/test/integration/app.js b/server/test/integration/app.js similarity index 85% rename from test/integration/app.js rename to server/test/integration/app.js index 82d8bf60c..0aac08dcc 100644 --- a/test/integration/app.js +++ b/server/test/integration/app.js @@ -9,7 +9,7 @@ test.serial('PORT env flag', async t => { const old = process.env.PORT process.env.PORT = PORT - require(path.join(__dirname, '/../../dist/server/index')) + require(path.join(__dirname, '/../../../dist/server/index')) const resp = await got(`http://localhost:${PORT}`) t.true(resp.body !== undefined) diff --git a/test/integration/auth.js b/server/test/integration/auth.js similarity index 89% rename from test/integration/auth.js rename to server/test/integration/auth.js index 376f69433..3731d0409 100644 --- a/test/integration/auth.js +++ b/server/test/integration/auth.js @@ -1,12 +1,12 @@ const test = require('ava') const request = require('supertest') -const app = require('../../dist/server/app').default -const { removeUserByEmail } = require('../../dist/server/database').users +const app = require('../../../dist/server/app').default +const { removeUserByEmail } = require('../../../dist/server/database').users -const { default: config } = require('../../dist/server/config/server') -const { responseList } = require('../../dist/server/responses') -const database = require('../../dist/server/database') -const auth = require('../../dist/server/auth') +const { default: config } = require('../../../dist/server/config/server') +const { responseList } = require('../../../dist/server/responses') +const database = require('../../../dist/server/database') +const auth = require('../../../dist/server/auth') const util = require('../_util') const testUser = util.generateTestUser() diff --git a/test/integration/challenges.js b/server/test/integration/challenges.js similarity index 86% rename from test/integration/challenges.js rename to server/test/integration/challenges.js index 75d35f5b5..43265aa6b 100644 --- a/test/integration/challenges.js +++ b/server/test/integration/challenges.js @@ -1,11 +1,11 @@ const test = require('ava') const request = require('supertest') -const app = require('../../dist/server/app').default +const app = require('../../../dist/server/app').default const util = require('../_util') -const auth = require('../../dist/server/auth') -const { default: config } = require('../../dist/server/config/server') +const auth = require('../../../dist/server/auth') +const { default: config } = require('../../../dist/server/config/server') -const { responseList } = require('../../dist/server/responses') +const { responseList } = require('../../../dist/server/responses') let uuid, testUserData diff --git a/test/integration/leaderboard.js b/server/test/integration/leaderboard.js similarity index 80% rename from test/integration/leaderboard.js rename to server/test/integration/leaderboard.js index 4fb3288c9..9312d914b 100644 --- a/test/integration/leaderboard.js +++ b/server/test/integration/leaderboard.js @@ -1,8 +1,8 @@ const test = require('ava') const request = require('supertest') -const app = require('../../dist/server/app').default +const app = require('../../../dist/server/app').default -const { responseList } = require('../../dist/server/responses') +const { responseList } = require('../../../dist/server/responses') test.before('start server', async t => { await app.ready() diff --git a/test/integration/profile.js b/server/test/integration/profile.js similarity index 79% rename from test/integration/profile.js rename to server/test/integration/profile.js index d26a699bf..b6188f873 100644 --- a/test/integration/profile.js +++ b/server/test/integration/profile.js @@ -1,11 +1,11 @@ const test = require('ava') const request = require('supertest') -const app = require('../../dist/server/app').default +const app = require('../../../dist/server/app').default const { v4: uuidv4 } = require('uuid') -const { default: config } = require('../../dist/server/config/server') -const { removeUserByEmail } = require('../../dist/server/database').users -const { responseList } = require('../../dist/server/responses') +const { default: config } = require('../../../dist/server/config/server') +const { removeUserByEmail } = require('../../../dist/server/database').users +const { responseList } = require('../../../dist/server/responses') const testUser = { email: uuidv4() + '@test.com', diff --git a/test/integration/submit.js b/server/test/integration/submit.js similarity index 93% rename from test/integration/submit.js rename to server/test/integration/submit.js index ebd0dec24..92c3c861a 100644 --- a/test/integration/submit.js +++ b/server/test/integration/submit.js @@ -1,11 +1,11 @@ const test = require('ava') const request = require('supertest') -const app = require('../../dist/server/app').default +const app = require('../../../dist/server/app').default const { v4: uuidv4 } = require('uuid') -const db = require('../../dist/server/database') -const { responseList } = require('../../dist/server/responses') -const auth = require('../../dist/server/auth') +const db = require('../../../dist/server/database') +const { responseList } = require('../../../dist/server/responses') +const auth = require('../../../dist/server/auth') const util = require('../_util') let chall, uuid, testUserData diff --git a/test/integration/submitTiming.js b/server/test/integration/submitTiming.js similarity index 86% rename from test/integration/submitTiming.js rename to server/test/integration/submitTiming.js index 08a4c8ac1..b0d82cc1a 100644 --- a/test/integration/submitTiming.js +++ b/server/test/integration/submitTiming.js @@ -1,11 +1,11 @@ const test = require('ava') const request = require('supertest') -const app = require('../../dist/server/app').default -const { default: config } = require('../../dist/server/config/server') +const app = require('../../../dist/server/app').default +const { default: config } = require('../../../dist/server/config/server') const util = require('../_util') -const { responseList } = require('../../dist/server/responses') -const auth = require('../../dist/server/auth') +const { responseList } = require('../../../dist/server/responses') +const auth = require('../../../dist/server/auth') const { getFirstLoadedChallenge } = require('../_util.js') let chall, uuid, testUserData diff --git a/test/unit/challenges/index.js b/server/test/unit/challenges/index.js similarity index 59% rename from test/unit/challenges/index.js rename to server/test/unit/challenges/index.js index 26cc43730..c6deef066 100644 --- a/test/unit/challenges/index.js +++ b/server/test/unit/challenges/index.js @@ -1,10 +1,10 @@ const test = require('ava') const app = require('fastify')() -const { init } = require('../../../dist/server/uploads') +const { init } = require('../../../../dist/server/uploads') init(app) -const challenges = require('../../../dist/server/challenges') +const challenges = require('../../../../dist/server/challenges') test('get all challenges', t => { const data = challenges.getAllChallenges() diff --git a/test/unit/config/util.js b/server/test/unit/config/util.js similarity index 92% rename from test/unit/config/util.js rename to server/test/unit/config/util.js index 577c6674b..e6c7fec23 100644 --- a/test/unit/config/util.js +++ b/server/test/unit/config/util.js @@ -1,6 +1,6 @@ const test = require('ava') -const util = require('../../../dist/server/config/util') +const util = require('../../../../dist/server/config/util') test('removeUndefined passes through properties', t => { const obj = { diff --git a/test/unit/rateLimit.js b/server/test/unit/rateLimit.js similarity index 92% rename from test/unit/rateLimit.js rename to server/test/unit/rateLimit.js index c2fed114c..91599a62e 100644 --- a/test/unit/rateLimit.js +++ b/server/test/unit/rateLimit.js @@ -1,6 +1,6 @@ const test = require('ava') const { v4: uuid } = require('uuid') -const timeouts = require('../../dist/server/cache/timeouts') +const timeouts = require('../../../dist/server/cache/timeouts') test('allows request if under ratelimit', async t => { const result = await timeouts.checkRateLimit({ diff --git a/test/unit/restrict.js b/server/test/unit/restrict.js similarity index 95% rename from test/unit/restrict.js rename to server/test/unit/restrict.js index c6d33b6c0..f09b41a83 100644 --- a/test/unit/restrict.js +++ b/server/test/unit/restrict.js @@ -1,7 +1,7 @@ const test = require('ava') -const { default: config } = require('../../dist/server/config/server') -const restrict = require('../../dist/server/util/restrict') +const { default: config } = require('../../../dist/server/config/server') +const restrict = require('../../../dist/server/util/restrict') let old diff --git a/test/unit/util.js b/server/test/unit/util.js similarity index 71% rename from test/unit/util.js rename to server/test/unit/util.js index ee69c03a4..e767b0fad 100644 --- a/test/unit/util.js +++ b/server/test/unit/util.js @@ -1,6 +1,6 @@ const test = require('ava') -const util = require('../../dist/server/util') +const util = require('../../../dist/server/util') test('get score dynamic', t => { t.is(typeof util.scores.getScore(100, 500, 0), 'number') diff --git a/tsconfig.json b/server/tsconfig.json similarity index 82% rename from tsconfig.json rename to server/tsconfig.json index 1ed9310e4..4ab6ee657 100644 --- a/tsconfig.json +++ b/server/tsconfig.json @@ -10,10 +10,10 @@ "allowSyntheticDefaultImports": true, "inlineSourceMap": true, "inlineSources": true, - "outDir": "dist/server", - "baseUrl": "." + "outDir": "../dist/server", + "rootDir": "src" }, "include": [ - "server/**/*" + "src/**/*" ] } diff --git a/tsconfig.strict.json b/tsconfig.strict.json deleted file mode 100644 index 28391ea06..000000000 --- a/tsconfig.strict.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "strict": true, - "noImplicitAny": false, - "checkJs": true, - } -} From f45e55f5bdbc2762494e90cd4a18db970c1af667 Mon Sep 17 00:00:00 2001 From: Philip Papurt Date: Fri, 7 Aug 2020 06:45:49 -0400 Subject: [PATCH 002/155] feat(client): add initial client webpack setup Hoist shared TS ESLint config options to project root Import from react / use react types to make typescript happy. Add prefresh config: Webpack 5 support for prefresh has not yet landed, but set up the configuration anyways. --- .eslintignore | 3 - .eslintrc.js | 20 +- .gitignore | 3 - artillery/.rdeploy.example/config.json | 1502 ---- .../files/example-sha1hash.txt | 1 - client/.eslintrc.js | 28 +- client/index.html | 11 +- client/lib/loaders/mustache-config-loader.js | 10 - client/lib/svg-icon-component-generator.js | 51 - client/src/api/admin/challs.js | 21 - client/src/api/auth.js | 132 - client/src/api/challenges.js | 46 - client/src/api/members.js | 28 - client/src/api/profile.js | 64 - client/src/api/scoreboard.js | 16 - client/src/api/util.js | 58 - client/src/app.tsx | 26 + client/src/components/action-button.js | 31 - client/src/components/admin/problem.js | 347 - client/src/components/ctftime-additional.js | 85 - client/src/components/ctftime-button.js | 68 - client/src/components/footer.js | 27 - client/src/components/form.js | 62 - client/src/components/graph.js | 236 - client/src/components/header.js | 49 - client/src/components/icon.js | 7 - client/src/components/jss.js | 31 - client/src/components/logout-button.js | 60 - client/src/components/markdown.js | 29 - client/src/components/modal.js | 86 - client/src/components/not-started.js | 25 - client/src/components/or.js | 32 - client/src/components/pagination.js | 104 - client/src/components/pending-token.js | 35 - client/src/components/problem.js | 207 - client/src/components/profile/ctftime-card.js | 59 - client/src/components/profile/members-card.js | 112 - client/src/components/profile/solves-card.js | 87 - client/src/components/solves-dialog.js | 140 - client/src/components/sponsors.js | 58 - client/src/components/timer.js | 82 - client/src/components/toast.js | 86 - client/src/components/token-preview.js | 18 - client/src/config.js | 3 - client/src/config.ts | 21 + client/src/icons/address-book.svg | 1 - client/src/icons/clock.svg | 1 - client/src/icons/ctftime.svg | 1 - client/src/icons/envelope-open.svg | 1 - client/src/icons/id-card.svg | 1 - client/src/icons/rank.svg | 1 - client/src/icons/trophy.svg | 1 - client/src/icons/user-circle.svg | 1 - client/src/index.js | 133 - client/src/index.tsx | 8 + client/src/routes/admin/challs.js | 83 - client/src/routes/challs.js | 231 - client/src/routes/ctftime-callback.js | 16 - client/src/routes/error.js | 22 - client/src/routes/home.js | 31 - client/src/routes/home.tsx | 30 + client/src/routes/login.js | 158 - client/src/routes/profile.js | 415 -- client/src/routes/recover.js | 70 - client/src/routes/register.js | 142 - client/src/routes/scoreboard.js | 288 - client/src/routes/verify.js | 43 - client/src/static/analytics/autotrack.js | 62 - client/src/static/analytics/index.js | 12 - client/src/sw.js | 13 - client/src/util/auth.tsx | 25 + client/src/util/ctftime.js | 34 - client/src/util/index.js | 8 - client/src/util/strings.js | 21 - client/src/util/theme.ts | 17 + client/src/util/time.js | 34 - client/tsconfig.json | 19 + client/webpack.config.js | 111 + package.json | 57 +- preact.config.js | 93 - server/.eslintrc.js | 20 +- server/src/app.js | 2 +- yarn.lock | 6240 +++++------------ 83 files changed, 2024 insertions(+), 10629 deletions(-) delete mode 100644 artillery/.rdeploy.example/config.json delete mode 100644 artillery/.rdeploy.example/files/example-sha1hash.txt delete mode 100644 client/lib/loaders/mustache-config-loader.js delete mode 100644 client/lib/svg-icon-component-generator.js delete mode 100644 client/src/api/admin/challs.js delete mode 100644 client/src/api/auth.js delete mode 100644 client/src/api/challenges.js delete mode 100644 client/src/api/members.js delete mode 100644 client/src/api/profile.js delete mode 100644 client/src/api/scoreboard.js delete mode 100644 client/src/api/util.js create mode 100644 client/src/app.tsx delete mode 100644 client/src/components/action-button.js delete mode 100644 client/src/components/admin/problem.js delete mode 100644 client/src/components/ctftime-additional.js delete mode 100644 client/src/components/ctftime-button.js delete mode 100644 client/src/components/footer.js delete mode 100644 client/src/components/form.js delete mode 100644 client/src/components/graph.js delete mode 100644 client/src/components/header.js delete mode 100644 client/src/components/icon.js delete mode 100644 client/src/components/jss.js delete mode 100644 client/src/components/logout-button.js delete mode 100644 client/src/components/markdown.js delete mode 100644 client/src/components/modal.js delete mode 100644 client/src/components/not-started.js delete mode 100644 client/src/components/or.js delete mode 100644 client/src/components/pagination.js delete mode 100644 client/src/components/pending-token.js delete mode 100644 client/src/components/problem.js delete mode 100644 client/src/components/profile/ctftime-card.js delete mode 100644 client/src/components/profile/members-card.js delete mode 100644 client/src/components/profile/solves-card.js delete mode 100644 client/src/components/solves-dialog.js delete mode 100644 client/src/components/sponsors.js delete mode 100644 client/src/components/timer.js delete mode 100644 client/src/components/toast.js delete mode 100644 client/src/components/token-preview.js delete mode 100644 client/src/config.js create mode 100644 client/src/config.ts delete mode 100644 client/src/icons/address-book.svg delete mode 100644 client/src/icons/clock.svg delete mode 100644 client/src/icons/ctftime.svg delete mode 100644 client/src/icons/envelope-open.svg delete mode 100644 client/src/icons/id-card.svg delete mode 100644 client/src/icons/rank.svg delete mode 100644 client/src/icons/trophy.svg delete mode 100644 client/src/icons/user-circle.svg delete mode 100644 client/src/index.js create mode 100644 client/src/index.tsx delete mode 100644 client/src/routes/admin/challs.js delete mode 100644 client/src/routes/challs.js delete mode 100644 client/src/routes/ctftime-callback.js delete mode 100644 client/src/routes/error.js delete mode 100644 client/src/routes/home.js create mode 100644 client/src/routes/home.tsx delete mode 100644 client/src/routes/login.js delete mode 100644 client/src/routes/profile.js delete mode 100644 client/src/routes/recover.js delete mode 100644 client/src/routes/register.js delete mode 100644 client/src/routes/scoreboard.js delete mode 100644 client/src/routes/verify.js delete mode 100644 client/src/static/analytics/autotrack.js delete mode 100644 client/src/static/analytics/index.js delete mode 100644 client/src/sw.js create mode 100644 client/src/util/auth.tsx delete mode 100644 client/src/util/ctftime.js delete mode 100644 client/src/util/index.js delete mode 100644 client/src/util/strings.js create mode 100644 client/src/util/theme.ts delete mode 100644 client/src/util/time.js create mode 100644 client/tsconfig.json create mode 100644 client/webpack.config.js delete mode 100644 preact.config.js diff --git a/.eslintignore b/.eslintignore index 4033a5266..a93361448 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,5 +1,2 @@ # Ignore build folders /dist - -# Ignore bundled analytics -/client/src/static/analytics/* \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js index 6c4f4bd26..2e48c4192 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,6 +5,7 @@ module.exports = { extends: [ 'standard' ], + parser: '@typescript-eslint/parser', parserOptions: { ecmaFeatures: { }, @@ -25,5 +26,22 @@ module.exports = { 'no-void': ['error', { allowAsStatement: true }] - } + }, + overrides: [{ + files: ['*.ts', '*.tsx'], + extends: [ + 'plugin:@typescript-eslint/recommended', + 'plugin:@typescript-eslint/recommended-requiring-type-checking' + ], + plugins: [ + '@typescript-eslint' + ], + rules: { + '@typescript-eslint/no-unnecessary-condition': 'error', + '@typescript-eslint/require-await': 'off' + } + }, { + files: ['**/.eslintrc.js'], + parser: 'espree' + }] } diff --git a/.gitignore b/.gitignore index 2b6ab3559..17b3569cc 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,3 @@ $RECYCLE.BIN # nyc test runner /.nyc_output /coverage - -# preact-cli build size plugin -size-plugin.json diff --git a/artillery/.rdeploy.example/config.json b/artillery/.rdeploy.example/config.json deleted file mode 100644 index 12fd57c57..000000000 --- a/artillery/.rdeploy.example/config.json +++ /dev/null @@ -1,1502 +0,0 @@ -[ - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-0", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-1", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-2", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-3", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-4", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-5", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-6", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-7", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-8", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-9", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-10", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-11", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-12", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-13", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-14", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-15", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-16", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-17", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-18", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-19", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-20", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-21", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-22", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-23", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-24", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-25", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-26", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-27", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-28", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-29", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-30", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-31", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-32", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-33", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-34", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-35", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-36", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-37", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-38", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-39", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-40", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-41", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-42", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-43", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-44", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-45", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-46", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-47", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-48", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-49", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-50", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-51", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-52", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-53", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-54", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-55", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-56", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-57", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-58", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-59", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-60", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-61", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-62", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-63", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-64", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-65", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-66", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-67", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-68", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-69", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-70", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-71", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-72", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-73", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-74", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-75", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-76", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-77", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-78", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-79", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-80", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-81", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-82", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-83", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-84", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-85", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-86", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-87", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-88", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-89", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-90", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-91", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-92", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-93", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-94", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-95", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-96", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-97", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-98", - "name": "Test Challenge" - }, - { - "files": [ - "files/example-sha1hash.txt" - ], - "description": "A test challenge", - "author": "Test", - "port": 1234, - "flag": "flag{test_flag_here}", - "points": { - "max": 500, - "min": 50 - }, - "id": "pwn/test-challenge-99", - "name": "Test Challenge" - } -] \ No newline at end of file diff --git a/artillery/.rdeploy.example/files/example-sha1hash.txt b/artillery/.rdeploy.example/files/example-sha1hash.txt deleted file mode 100644 index 9fe2bfd80..000000000 --- a/artillery/.rdeploy.example/files/example-sha1hash.txt +++ /dev/null @@ -1 +0,0 @@ -This should be served \ No newline at end of file diff --git a/client/.eslintrc.js b/client/.eslintrc.js index 8ecabf5fa..5805bed00 100644 --- a/client/.eslintrc.js +++ b/client/.eslintrc.js @@ -3,21 +3,29 @@ module.exports = { browser: true }, extends: [ - 'preact', - - // include standard again since preact sets style options which override - // those of standard (see preactjs/eslint-config-preact#6) - 'standard' + 'plugin:react/recommended', + 'plugin:react-hooks/recommended' ], plugins: [ ], rules: { radix: 'off', 'jsx-quotes': ['error', 'prefer-single'], - 'no-multiple-empty-lines': ['error', { // override again because of reincluding standard - max: 1, - maxEOF: 0, - maxBOF: 0 - }] + 'react/react-in-jsx-scope': 'off', // We use ProvidePlugin + 'react/prop-types': 'off' // We have TypeScript and don't care about JS interop + }, + parserOptions: { + tsconfigRootDir: __dirname, + project: ['./tsconfig.json'] + }, + overrides: [{ + files: ['lib/**/*', 'webpack.config*.js'], + parser: 'espree' + }], + settings: { + react: { + // Set a recent React version to satisfy the plugin + version: '16.13.1' + } } } diff --git a/client/index.html b/client/index.html index 99ac8b00c..d1d95ca33 100644 --- a/client/index.html +++ b/client/index.html @@ -22,16 +22,11 @@ - + - {{ #config.renderGoogleAnalytics }} - - - - - {{ /config.renderGoogleAnalytics }} + - <% preact.bodyEnd %> +
diff --git a/client/lib/loaders/mustache-config-loader.js b/client/lib/loaders/mustache-config-loader.js deleted file mode 100644 index 3174ed2cf..000000000 --- a/client/lib/loaders/mustache-config-loader.js +++ /dev/null @@ -1,10 +0,0 @@ -const mustache = require('mustache') -const { default: config } = require('../../../dist/server/config/client') - -module.exports = function (source) { - // FIXME: refactor this (copy-pasted from server) - return mustache.render(source, { - jsonConfig: JSON.stringify(config), - config - }) -} diff --git a/client/lib/svg-icon-component-generator.js b/client/lib/svg-icon-component-generator.js deleted file mode 100644 index 31838704f..000000000 --- a/client/lib/svg-icon-component-generator.js +++ /dev/null @@ -1,51 +0,0 @@ -const path = require('path') -const { pascalCase } = require('pascal-case') -const { stringifyRequest } = require('loader-utils') - -// From JetBrains/svg-sprite-loader/lib/utils/stringify-symbol.js -function stringifySymbol (symbol) { - return JSON.stringify({ - id: symbol.id, - use: symbol.useId, - viewBox: symbol.viewBox, - content: symbol.render() - }) -} - -function stringifySymbolRequest (symbol, request) { - return stringifyRequest({ - context: path.dirname(symbol.request.file) - }, request) -} - -// From JetBrains/svg-sprite-loader/examples/custom-runtime-generator/ -module.exports = function runtimeGenerator ({ symbol, config, loaderContext }) { - const { spriteModule, symbolModule, runtimeOptions } = config - const compilerContext = loaderContext._compiler.context - - const iconModulePath = path.resolve(compilerContext, runtimeOptions.iconModule) - const iconModuleRequest = stringifySymbolRequest(symbol, iconModulePath) - - const spriteRequest = stringifySymbolRequest(symbol, spriteModule) - const symbolRequest = stringifySymbolRequest(symbol, symbolModule) - const parentComponentDisplayName = 'SpriteSymbolComponent' - const displayName = `${pascalCase(symbol.id)}${parentComponentDisplayName}` - - // Use h syntax to avoid needing a separate Babel transpile - return ` - import { h } from 'preact' - import SpriteSymbol from ${symbolRequest} - import sprite from ${spriteRequest} - import ${parentComponentDisplayName} from ${iconModuleRequest} - - const symbol = new SpriteSymbol(${stringifySymbol(symbol)}) - sprite.add(symbol) - export default function ${displayName} (props) { - return h(${parentComponentDisplayName}, { - ...props, - glyph: "${symbol.id}", - viewBox: "${symbol.viewBox}" - }); - } - ` -} diff --git a/client/src/api/admin/challs.js b/client/src/api/admin/challs.js deleted file mode 100644 index 0fa48590b..000000000 --- a/client/src/api/admin/challs.js +++ /dev/null @@ -1,21 +0,0 @@ -import { request, handleResponse } from '../util' - -export const getChallenges = async () => { - return (await request('GET', '/admin/challs')).data -} - -export const updateChallenge = async ({ id, data }) => { - return (await request('PUT', `/admin/challs/${encodeURIComponent(id)}`, { data })).data -} - -export const deleteChallenge = async ({ id }) => { - return (await request('DELETE', `/admin/challs/${encodeURIComponent(id)}`)).data -} - -export const uploadFiles = async ({ files }) => { - const resp = await request('POST', '/admin/upload', { - files - }) - - return handleResponse({ resp, valid: ['goodFilesUpload'] }) -} diff --git a/client/src/api/auth.js b/client/src/api/auth.js deleted file mode 100644 index dd6829064..000000000 --- a/client/src/api/auth.js +++ /dev/null @@ -1,132 +0,0 @@ -import { request } from './util' -import { route } from 'preact-router' - -export const setAuthToken = ({ authToken }) => { - localStorage.token = authToken - route('/profile') -} - -export const login = async ({ teamToken, ctftimeToken }) => { - const resp = await request('POST', '/auth/login', { - teamToken, - ctftimeToken - }) - switch (resp.kind) { - case 'goodLogin': - return { - authToken: resp.data.authToken - } - case 'badTokenVerification': - return { - teamToken: resp.message - } - case 'badUnknownUser': - return { - badUnknownUser: true - } - default: - return { - teamToken: 'Unknown response from server, please contact ctf administrator' - } - } -} - -export const logout = () => { - localStorage.removeItem('token') - - return route('/') -} - -export const verify = async ({ verifyToken }) => { - const resp = await request('POST', '/auth/verify', { - verifyToken - }) - switch (resp.kind) { - case 'goodRegister': - case 'goodVerify': - return { - authToken: resp.data.authToken - } - case 'goodEmailSet': - return { - emailSet: true - } - default: - return { - verifyToken: resp.message - } - } -} - -export const register = async ({ email, name, ctftimeToken }) => { - const resp = await request('POST', '/auth/register', { - email, - name, - ctftimeToken - }) - switch (resp.kind) { - case 'goodRegister': - localStorage.setItem('token', resp.data.authToken) - - return route('/profile') - case 'goodVerifySent': - return { - verifySent: true - } - case 'badEmail': - case 'badKnownEmail': - case 'badCompetitionNotAllowed': - return { - errors: { - email: resp.message - } - } - case 'badKnownName': - return { - errors: { - name: resp.message - }, - data: resp.data - } - case 'badName': - return { - errors: { - name: resp.message - } - } - } -} - -export const ctftimeCallback = ({ ctftimeCode }) => { - return request('POST', '/integrations/ctftime/callback', { - ctftimeCode - }) -} - -export const putCtftime = ({ ctftimeToken }) => { - return request('PUT', '/users/me/auth/ctftime', { - ctftimeToken - }) -} - -export const deleteCtftime = () => { - return request('DELETE', '/users/me/auth/ctftime') -} - -export const recover = async ({ email }) => { - const resp = await request('POST', '/auth/recover', { - email - }) - switch (resp.kind) { - case 'goodVerifySent': - return { - verifySent: true - } - default: - return { - errors: { - email: resp.message - } - } - } -} diff --git a/client/src/api/challenges.js b/client/src/api/challenges.js deleted file mode 100644 index f77c3c7bd..000000000 --- a/client/src/api/challenges.js +++ /dev/null @@ -1,46 +0,0 @@ -import { request, handleResponse } from './util' -import { privateProfile } from './profile' - -export const getChallenges = async () => { - const resp = await request('GET', '/challs') - - if (resp.kind === 'badNotStarted') { - return { - notStarted: true - } - } - - return handleResponse({ resp, valid: ['goodChallenges'] }) -} - -export const getPrivateSolves = async () => { - const { data, error } = await privateProfile() - - if (error) { - return { error } - } - return { - data: data.solves - } -} - -export const getSolves = ({ challId, limit, offset }) => { - return request('GET', `/challs/${encodeURIComponent(challId)}/solves`, { - limit, - offset - }) -} - -export const submitFlag = async (id, flag) => { - if (flag === undefined || flag.length === 0) { - return Promise.resolve({ - error: "Flag can't be empty" - }) - } - - const resp = await request('POST', `/challs/${encodeURIComponent(id)}/submit`, { - flag - }) - - return handleResponse({ resp, valid: ['goodFlag'] }) -} diff --git a/client/src/api/members.js b/client/src/api/members.js deleted file mode 100644 index 4000084ef..000000000 --- a/client/src/api/members.js +++ /dev/null @@ -1,28 +0,0 @@ -import { request } from './util' - -export const getMembers = async () => { - return (await request('GET', '/users/me/members')).data -} - -export const addMember = async ({ email }) => { - const resp = await request('POST', '/users/me/members', { email }) - switch (resp.kind) { - case 'badEmail': - case 'badKnownEmail': - return { - error: resp.message - } - case 'goodMemberCreate': - return { - data: resp.data - } - default: - return { - error: 'Unknown error' - } - } -} - -export const removeMember = async ({ id }) => { - return (await request('DELETE', `/users/me/members/${encodeURIComponent(id)}`)).data -} diff --git a/client/src/api/profile.js b/client/src/api/profile.js deleted file mode 100644 index bc603cd9e..000000000 --- a/client/src/api/profile.js +++ /dev/null @@ -1,64 +0,0 @@ -import { request, handleResponse } from './util' - -export const privateProfile = async () => { - const resp = await request('GET', '/users/me') - - return handleResponse({ resp, valid: ['goodUserData'] }) -} - -export const pendingPrivateProfile = async ({ authToken }) => { - const { data } = await (await fetch('/api/v1/users/me', { - headers: { - authorization: `Bearer ${authToken}` - } - })).json() - - return data -} - -export const publicProfile = async (uuid) => { - const resp = await request('GET', `/users/${encodeURIComponent(uuid)}`) - - return handleResponse({ resp, valid: ['goodUserData'] }) -} - -export const updateAccount = async ({ name, division }) => { - const resp = await request('PATCH', '/users/me', { - name, - division - }) - - if (resp.kind === 'badRateLimit') { - const ms = resp.data.timeLeft - const sec = Math.floor(ms / 1000) - const min = Math.floor(sec / 60) - - let msg - if (min === 0) { - msg = `${sec} seconds` - } else { - msg = `${min} minutes` - } - - return { - error: `Please wait ${msg} before trying this again` - } - } - - return handleResponse({ resp, valid: ['goodUserUpdate'] }) -} - -export const updateEmail = async ({ email }) => { - const resp = await request('PUT', '/users/me/auth/email', { - email - }) - - return handleResponse({ resp, valid: ['goodVerifySent', 'goodEmailSet'], resolveDataMessage: true }) -} - -export const deleteEmail = async () => { - const resp = await request('DELETE', '/users/me/auth/email') - - // If the email did not exist, still a "success" in that no more email - return handleResponse({ resp, valid: ['goodEmailRemoved', 'badEmailNoExists'], resolveDataMessage: true }) -} diff --git a/client/src/api/scoreboard.js b/client/src/api/scoreboard.js deleted file mode 100644 index 91724681d..000000000 --- a/client/src/api/scoreboard.js +++ /dev/null @@ -1,16 +0,0 @@ -import { request } from './util' - -export const getScoreboard = ({ division, limit = 100, offset = 0 }) => { - return request('GET', '/leaderboard/now', { - division, - limit, - offset - }) -} - -export const getGraph = async ({ division }) => { - return request('GET', '/leaderboard/graph', { - division, - limit: 10 - }) -} diff --git a/client/src/api/util.js b/client/src/api/util.js deleted file mode 100644 index bdcab8509..000000000 --- a/client/src/api/util.js +++ /dev/null @@ -1,58 +0,0 @@ -import { route } from 'preact-router' - -export const relog = () => { - localStorage.removeItem('token') - route('/register') -} - -export const handleResponse = ({ resp, valid, resolveDataMessage }) => { - if (valid.includes(resp.kind)) { - if (resolveDataMessage) { - return { - data: resp.message - } - } - - return { - data: resp.data - } - } - return { - error: resp.message - } -} - -export const request = (method, endpoint, data) => { - let body = null - let qs = '' - if (method === 'GET' && data) { - // encode data into the querystring - // eslint-disable-next-line prefer-template - qs = '?' + Object.keys(data) - .filter(k => data[k] !== undefined) - .map(k => `${k}=${encodeURIComponent(data[k])}`) - .join('&') - } else { - body = data - } - const headers = { - Authorization: `Bearer ${localStorage.getItem('token')}` - } - if (body) { - headers['Content-Type'] = 'application/json' - } - return fetch(`/api/v1${endpoint}${qs}`, { - method, - headers, - body: body && JSON.stringify(body) - }) - .then(resp => resp.json()) - .then(resp => { - if (resp.kind === 'badToken') return relog() - - return resp - }) - .catch(err => { - console.debug(err) - }) -} diff --git a/client/src/app.tsx b/client/src/app.tsx new file mode 100644 index 000000000..2e25a41fc --- /dev/null +++ b/client/src/app.tsx @@ -0,0 +1,26 @@ +import { FunctionComponent } from 'react' +import { BrowserRouter as Router, Routes, Route } from 'react-router-dom' +import { ThemeProvider } from 'theme-ui' +import theme from './util/theme' +import { AuthProvider } from './util/auth' +import Home from './routes/home' + +const App: FunctionComponent = () => { + return ( + + } /> + + ) +} + +const WrappedApp: FunctionComponent = () => ( + + + + + + + +) + +export default WrappedApp diff --git a/client/src/components/action-button.js b/client/src/components/action-button.js deleted file mode 100644 index 54a644b4a..000000000 --- a/client/src/components/action-button.js +++ /dev/null @@ -1,31 +0,0 @@ -import withStyles from './jss' - -const ActionButton = withStyles({ - button: { - padding: '16px !important', - color: '#fff', - background: '#222', - boxShadow: 'rgba(250,250,250,0.6) 0px 0px 1rem 0px', - fontSize: '20px', - borderRadius: '1rem', - textAlign: 'center', - transition: 'box-shadow ease-in-out 0.2s, transform ease-in-out 0.2s', - margin: '20px auto', - '&:hover': { - boxShadow: 'rgba(250,250,250,0.6) 0px 0px 1.1rem 0px', - transform: 'scale(1.1)', - color: '#fff' - }, - '& svg': { - height: '1em', - position: 'relative', - top: '0.125em' - } - } -}, ({ classes, ...rest }) => ( -
- -
-)) - -export default ActionButton diff --git a/client/src/components/admin/problem.js b/client/src/components/admin/problem.js deleted file mode 100644 index 2eae3d119..000000000 --- a/client/src/components/admin/problem.js +++ /dev/null @@ -1,347 +0,0 @@ -import { Fragment } from 'preact' -import withStyles from '../../components/jss' -import { useState, useCallback } from 'preact/hooks' -import Modal from '../../components/modal' - -import { updateChallenge, deleteChallenge, uploadFiles } from '../../api/admin/challs' -import { useToast } from '../../components/toast' -import { encodeFile } from '../../util' - -const DeleteModal = withStyles({ - modalBody: { - paddingTop: '0em !important' // reduce space between header and body - }, - controls: { - display: 'flex', - justifyContent: 'center', - '& :first-child': { - marginLeft: '0em' - }, - '& :last-child': { - marginRight: '0em' - } - } -}, ({ open, onClose, onDelete, classes }) => { - const wrappedOnClose = useCallback((e) => { - e.preventDefault() - onClose() - }, [onClose]) - const wrappedOnDelete = useCallback((e) => { - e.preventDefault() - onDelete() - }, [onDelete]) - - return ( - - -
- This is an irreversible action that permanently deletes the challenge and revokes all solves. -
-
- -
-
- -
-
-
-
- ) -}) - -const Problem = ({ classes, problem, update: updateClient }) => { - const { toast } = useToast() - - const [flag, setFlag] = useState(problem.flag) - const handleFlagChange = useCallback(e => setFlag(e.target.value), []) - - const [description, setDescription] = useState(problem.description) - const handleDescriptionChange = useCallback(e => setDescription(e.target.value), []) - - const [category, setCategory] = useState(problem.category) - const handleCategoryChange = useCallback(e => setCategory(e.target.value), []) - - const [author, setAuthor] = useState(problem.author) - const handleAuthorChange = useCallback(e => setAuthor(e.target.value), []) - - const [name, setName] = useState(problem.name) - const handleNameChange = useCallback(e => setName(e.target.value), []) - - const [minPoints, setMinPoints] = useState(problem.points.min) - const handleMinPointsChange = useCallback(e => setMinPoints(Number.parseInt(e.target.value)), []) - - const [maxPoints, setMaxPoints] = useState(problem.points.max) - const handleMaxPointsChange = useCallback(e => setMaxPoints(Number.parseInt(e.target.value)), []) - - const [tiebreakEligible, setTiebreakEligible] = useState(problem.tiebreakEligible !== false) - const handleTiebreakEligibleChange = useCallback(e => setTiebreakEligible(e.target.checked), []) - - const handleFileUpload = useCallback(async e => { - e.preventDefault() - - const fileData = await Promise.all( - Array.from(e.target.files) - .map(async file => { - const data = await encodeFile(file) - - return { - data, - name: file.name - } - }) - ) - - const fileUpload = await uploadFiles({ - files: fileData - }) - - if (fileUpload.error) { - toast({ body: fileUpload.error, type: 'error' }) - return - } - - const data = await updateChallenge({ - id: problem.id, - data: { - files: fileUpload.data.concat(problem.files) - } - }) - - e.target.value = null - - updateClient({ - problem: data - }) - - toast({ body: 'Problem successfully updated' }) - }, [problem.id, problem.files, updateClient, toast]) - - const handleRemoveFile = file => async () => { - const newFiles = problem.files.filter(f => f !== file) - - const data = await updateChallenge({ - id: problem.id, - data: { - files: newFiles - } - }) - - updateClient({ - problem: data - }) - - toast({ body: 'Problem successfully updated' }) - } - - const handleUpdate = async e => { - e.preventDefault() - - const data = await updateChallenge({ - id: problem.id, - data: { - flag, - description, - category, - author, - name, - tiebreakEligible, - points: { - min: minPoints, - max: maxPoints - } - } - }) - - updateClient({ - problem: data - }) - - toast({ body: 'Problem successfully updated' }) - } - - const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false) - const openDeleteModal = useCallback(e => { - e.preventDefault() - setIsDeleteModalOpen(true) - }, []) - const closeDeleteModal = useCallback(() => { - setIsDeleteModalOpen(false) - }, []) - const handleDelete = useCallback(() => { - const action = async () => { - await deleteChallenge({ - id: problem.id - }) - toast({ - body: `${problem.name} successfully deleted`, - type: 'success' - }) - closeDeleteModal() - } - action() - }, [problem, toast, closeDeleteModal]) - - return ( - -
-
-
-
-
- - -
- - -
-
-
- - - -
-
- -
- -