Skip to content
This repository was archived by the owner on Feb 4, 2025. It is now read-only.

Commit 7f05d86

Browse files
authored
Merge pull request #391 from redpwn/refactor/cp-stricter-typechecking
Cherry-pick non-breaking TS config changes
2 parents b367340 + abec8cd commit 7f05d86

File tree

14 files changed

+223
-148
lines changed

14 files changed

+223
-148
lines changed

.eslintrc.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ module.exports = {
2020
maxBOF: 0
2121
}],
2222
'padding-line-between-statements': ['error',
23-
{blankLine: 'always', prev: 'block-like', next: 'export'}
24-
]
23+
{ blankLine: 'always', prev: 'block-like', next: 'export' }
24+
],
25+
'no-void': ['error', {
26+
allowAsStatement: true
27+
}]
2528
}
2629
}

package.json

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"license": "MIT",
55
"private": true,
66
"scripts": {
7-
"lint": "tsc --noEmit && eslint --ext .js,.ts .",
7+
"lint": "sh -c \"tsc --noEmit && eslint .\"",
8+
"lint:strict": "sh -c \"tsc --noEmit -p tsconfig.strict.json ; eslint .\"",
89
"start": "node --enable-source-maps dist/server/index.js",
910
"migrate": "sh -c \"yarn build:ts && RCTF_DATABASE_MIGRATE=only yarn start\"",
1011
"build:client": "preact build --src client/src --template client/index.html --dest dist/build --no-prerender --no-inline-css",
@@ -58,8 +59,8 @@
5859
"devDependencies": {
5960
"@types/content-disposition": "0.5.3",
6061
"@types/nodemailer": "6.4.0",
61-
"@typescript-eslint/eslint-plugin": "2.34.0",
62-
"@typescript-eslint/parser": "2.34.0",
62+
"@typescript-eslint/eslint-plugin": "3.5.0",
63+
"@typescript-eslint/parser": "3.5.0",
6364
"ava": "3.9.0",
6465
"babel-eslint": "10.1.0",
6566
"babel-plugin-transform-export-extensions": "6.22.0",
@@ -69,7 +70,7 @@
6970
"cpy-cli": "3.1.1",
7071
"cross-env": "7.0.2",
7172
"cz-conventional-changelog": "3.2.0",
72-
"eslint": "6.8.0",
73+
"eslint": "7.3.1",
7374
"eslint-config-preact": "1.1.1",
7475
"eslint-config-standard": "14.1.1",
7576
"eslint-plugin-ava": "10.3.1",
@@ -100,7 +101,7 @@
100101
"snarkdown": "1.2.2",
101102
"supertest": "4.0.2",
102103
"svg-sprite-loader": "5.0.0",
103-
"typescript": "3.9.5"
104+
"typescript": "3.9.6"
104105
},
105106
"description": "rctf is RedpwnCTF's CTF platform. It is developed and maintained by the [redpwn](https://redpwn.net) CTF team.",
106107
"repository": {

server/.eslintrc.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
const path = require('path')
2+
13
module.exports = {
24
env: {
35
node: true
@@ -7,14 +9,20 @@ module.exports = {
79
rules: {
810
},
911
parser: '@typescript-eslint/parser',
12+
parserOptions: {
13+
tsconfigRootDir: path.dirname(__dirname),
14+
project: ['./tsconfig.json']
15+
},
1016
overrides: [{
1117
files: ['*.ts'],
1218
extends: [
13-
'plugin:@typescript-eslint/eslint-recommended',
14-
'plugin:@typescript-eslint/recommended',
19+
'plugin:@typescript-eslint/recommended'
1520
],
1621
plugins: [
1722
'@typescript-eslint'
1823
]
24+
}, {
25+
files: ['.eslintrc.js'],
26+
parser: 'espree'
1927
}]
2028
}

server/challenges/Provider.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,7 @@ export interface Provider extends EventEmitter {
1313
deleteChallenge(id: string): void;
1414
cleanup (): void;
1515
}
16+
17+
export interface ProviderConstructor {
18+
new (options: unknown): Provider
19+
}

server/challenges/index.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
import config from '../../config/server'
22
import path from 'path'
33
import { Challenge, CleanedChallenge } from './types'
4-
import { Provider } from './Provider'
4+
import { Provider, ProviderConstructor } from './Provider'
55
import { challUpdateEmitter, publishChallUpdate } from '../cache/challs'
66

77
let provider: Provider
88

99
let challenges: Challenge[] = []
1010
let cleanedChallenges: CleanedChallenge[] = []
1111

12-
let challengesMap: Map<string, Challenge> = new Map()
13-
let cleanedChallengesMap: Map<string, CleanedChallenge> = new Map()
12+
let challengesMap = new Map<string, Challenge>()
13+
let cleanedChallengesMap = new Map<string, CleanedChallenge>()
1414

1515
const cleanChallenge = (chall: Challenge): CleanedChallenge => {
1616
const { files, description, author, points, id, name, category, sortWeight } = chall
@@ -34,8 +34,8 @@ const onUpdate = (newChallenges: Challenge[]): void => {
3434
cleanedChallengesMap = new Map(cleanedChallenges.map(c => [c.id, c]))
3535
}
3636

37-
import(path.join('../providers', config.challengeProvider.name))
38-
.then(({ default: Provider }) => {
37+
void import(path.join('../providers', config.challengeProvider.name))
38+
.then(({ default: Provider }: { default: ProviderConstructor }) => {
3939
provider = new Provider(config.challengeProvider.options)
4040

4141
provider.on('update', onUpdate)

server/providers/challenges/database/index.ts

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import * as db from '../../../database'
77
import { deepCopy } from '../../../util'
88

99
interface DatabaseProviderOptions {
10-
updateInterval: number;
10+
updateInterval?: number;
1111
}
1212

1313
interface DatabaseChallenge {
@@ -16,25 +16,25 @@ interface DatabaseChallenge {
1616
}
1717

1818
class DatabaseProvider extends EventEmitter implements Provider {
19-
private _updateInterval: number
20-
private _interval: NodeJS.Timeout
21-
private challenges: Challenge[]
19+
private updateInterval: number
20+
private interval: NodeJS.Timeout
21+
private challenges: Challenge[] = []
2222

23-
constructor (options: DatabaseProviderOptions) {
23+
constructor (_options: DatabaseProviderOptions) {
2424
super()
25-
options = {
25+
const options: Required<DatabaseProviderOptions> = {
2626
updateInterval: 60 * 1000,
27-
...options
27+
..._options
2828
}
2929

30-
this._updateInterval = options.updateInterval
31-
this._interval = setInterval(() => this._update(), this._updateInterval)
32-
this._update()
30+
this.updateInterval = options.updateInterval
31+
this.interval = setInterval(() => { void this.update() }, this.updateInterval)
32+
void this.update()
3333
}
3434

35-
async _update (): Promise<void> {
35+
private async update (): Promise<void> {
3636
try {
37-
const dbchallenges: DatabaseChallenge[] = await db.challenges.getAllChallenges()
37+
const dbchallenges = await db.challenges.getAllChallenges() as DatabaseChallenge[]
3838

3939
this.challenges = dbchallenges.map(({ id, data }) => {
4040
return {
@@ -51,7 +51,7 @@ class DatabaseProvider extends EventEmitter implements Provider {
5151
}
5252

5353
forceUpdate (): void {
54-
this._update()
54+
void this.update()
5555
}
5656

5757
challengeToRow (chall: Challenge): DatabaseChallenge {
@@ -69,7 +69,7 @@ class DatabaseProvider extends EventEmitter implements Provider {
6969
async updateChallenge (chall: Challenge): Promise<void> {
7070
const originalData = await db.challenges.getChallengeById({
7171
id: chall.id
72-
})
72+
}) as DatabaseChallenge
7373

7474
// If we're inserting, have sane defaults
7575
if (originalData === undefined) {
@@ -85,17 +85,17 @@ class DatabaseProvider extends EventEmitter implements Provider {
8585

8686
await db.challenges.upsertChallenge(data)
8787

88-
this._update()
88+
void this.update()
8989
}
9090

9191
async deleteChallenge (id: string): Promise<void> {
9292
await db.challenges.removeChallengeById({ id: id })
9393

94-
this._update()
94+
void this.update()
9595
}
9696

9797
cleanup (): void {
98-
clearInterval(this._interval)
98+
clearInterval(this.interval)
9999
}
100100
}
101101

server/providers/challenges/rdeploy-blob/index.ts

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,29 +29,29 @@ interface RDeployChallenge {
2929
}
3030

3131
class RDeployBlobProvider extends EventEmitter implements Provider {
32-
private _updateInterval: number
33-
private _rDeployDirectory: string
34-
private _interval: NodeJS.Timeout
35-
private challenges: Challenge[]
32+
private updateInterval: number
33+
private rDeployDirectory: string
34+
private interval: NodeJS.Timeout
35+
private challenges: Challenge[] = []
3636

3737
private ready = false
3838
private downloadMap: Map<string, string>
3939

40-
constructor (options: RDeployBlobProviderOptions) {
40+
constructor (_options: RDeployBlobProviderOptions) {
4141
super()
42-
options = {
42+
const options: Required<RDeployBlobProviderOptions> = {
4343
updateInterval: 60 * 1000,
44-
...options
44+
..._options
4545
}
4646

47-
this._updateInterval = options.updateInterval
48-
this._rDeployDirectory = path.join(__dirname, '../../../../../', options.rDeployDirectory)
49-
this._interval = setInterval(() => this._update(), this._updateInterval)
47+
this.updateInterval = options.updateInterval
48+
this.rDeployDirectory = path.join(__dirname, '../../../../../', options.rDeployDirectory)
49+
this.interval = setInterval(() => this.update(), this.updateInterval)
5050

51-
this.downloadMap = new Map()
51+
this.downloadMap = new Map<string, string>()
5252

53-
const fileDir = path.join(this._rDeployDirectory, options.rDeployFiles)
54-
fs.readdir(fileDir)
53+
const fileDir = path.join(this.rDeployDirectory, options.rDeployFiles)
54+
void fs.readdir(fileDir)
5555
.then(async files => {
5656
await Promise.all(files.map(async file => {
5757
const filePath = path.join(fileDir, file)
@@ -68,25 +68,29 @@ class RDeployBlobProvider extends EventEmitter implements Provider {
6868

6969
// When done uploading files, allow updates
7070
this.ready = true
71-
this._update()
71+
this.update()
7272
})
7373
}
7474

75-
_update (): void {
75+
private update (): void {
7676
// Prevent updates if downloads not initialized
7777
if (!this.ready) return
7878

79-
fs.readFile(path.join(this._rDeployDirectory, 'config.json'), 'utf8')
79+
fs.readFile(path.join(this.rDeployDirectory, 'config.json'), 'utf8')
8080
.then((data: string) => {
8181
try {
82-
const rawChallenges: RDeployChallenge[] = JSON.parse(data)
82+
const rawChallenges = JSON.parse(data) as RDeployChallenge[]
8383

8484
this.challenges = rawChallenges.map((chall: RDeployChallenge): Challenge => {
8585
const downloadUrls: File[] = chall.files.map(file => {
8686
const basename = path.basename(file)
87+
const fileUrl = this.downloadMap.get(basename)
88+
if (fileUrl === undefined) {
89+
throw new Error(`File not found: ${basename}`)
90+
}
8791
return {
8892
name: normalize.normalizeDownload(basename),
89-
url: this.downloadMap.get(basename)
93+
url: fileUrl
9094
}
9195
})
9296

@@ -109,7 +113,7 @@ class RDeployBlobProvider extends EventEmitter implements Provider {
109113
}
110114

111115
forceUpdate (): void {
112-
this._update()
116+
this.update()
113117
}
114118

115119
updateChallenge (chall: Challenge): void {
@@ -135,7 +139,7 @@ class RDeployBlobProvider extends EventEmitter implements Provider {
135139
}
136140

137141
cleanup (): void {
138-
clearInterval(this._interval)
142+
clearInterval(this.interval)
139143
}
140144
}
141145

server/providers/emails/ses/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ interface SesProviderOptions {
99
}
1010

1111
export default class SesProvider implements Provider {
12-
private sesSend: Function
12+
private sesSend: (params: AWS.SES.Types.SendEmailRequest) => Promise<AWS.SES.Types.SendEmailResponse>
13+
1314
constructor (options: SesProviderOptions) {
1415
const credentials = new AWS.Credentials({
1516
accessKeyId: options.awsKeyId || process.env.RCTF_SES_KEY_ID,

server/providers/uploads/gcs/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import crypto from 'crypto'
33
import { Provider } from '../../../uploads/types'
44

55
interface GcsProviderOptions {
6-
credentials: object;
6+
credentials: Record<string, unknown>;
77
bucketName: string;
88
}
99

server/uploads/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@ import { Provider } from './types'
44
import fastify from 'fastify'
55
import { Server, IncomingMessage, ServerResponse } from 'http'
66

7-
let provider: Provider = null
7+
let provider: Provider | null = null
88

99
export const init = (app: fastify.FastifyInstance<Server, IncomingMessage, ServerResponse> | null): void => {
1010
const name = app === null ? 'uploads/dummy' : config.uploadProvider.name
11+
12+
// FIXME: use async loading
13+
// eslint-disable-next-line @typescript-eslint/no-var-requires
1114
const ProviderClass = require(path.join('../providers', name)).default
15+
1216
provider = new ProviderClass(config.uploadProvider.options, app)
1317
}
1418

0 commit comments

Comments
 (0)