Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 130 additions & 0 deletions src/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -441,4 +441,134 @@ export const actions: {[k: string]: QueryHandler} = {
if (stderr) throw new ActionError(stderr);
return {updated: update, success: true};
},
async uploadteam(params) {
if (this.request.method !== 'POST') {
throw new ActionError('Team uploading must be done through POST requests.');
}
if (!this.user.registered) {
throw new ActionError('Please register and log in to upload teams.');
}
const teams = await tables.teams.selectAll(
'*', SQL`WHERE userid = ${this.user.id}`
);
if (teams.length > 3000) {
throw new ActionError(
'You have reached the maximum number of teams (3000).' +
'Please delete some before uploading more.'
);
}
const formatid = toID(params.format);
if (!formatid) {
throw new ActionError('Specify a format.');
}
const team = (params.team || "").trim();
if (!toID(team) || team.length > 1200 || team.length < 5) {
throw new ActionError('Invalid team.');
}
const existing = await tables.teams.selectOne(
'*', SQL`team = ${team} AND userid = ${this.user.id}`
);
if (existing) {
throw new ActionError('You already uploaded this team.');
}
const id = Number(toID(params.id || ""));
if (id) {
const found = await tables.teams.get('*', id);
if (found) {
if (found.userid !== this.user.id) {
throw new ActionError('You cannot overwrite another user\'s team.');
}
await tables.teams.update(id, {
team,
format: formatid,
date: Date.now(),
});
return {success: true};
}
}
const result = await tables.teams.insert({
team,
format: formatid,
userid: this.user.id,
date: Date.now(),
});
return {success: !!result.affectedRows};
},
async deleteteam(params) {
// not regged, not logged in
if (!this.user.registered) {
throw new ActionError(
'You must be registered and logged in to delete your teams.'
);
}
const id = Number(toID(params.id || ""));
if (!id) {
throw new ActionError(
'No team was specified or the given team ID was invalid.'
);
}
const found = await tables.teams.get('*', id);
if (found?.userid !== this.user.id) {
throw new ActionError('You cannot delete another user\'s team.');
}
const result = await tables.teams.delete(id);
return {success: !!result.affectedRows};
},
async listteams() {
if (!this.user.registered) {
throw new ActionError('You must be registered and logged in to view your teams.');
}
const teams = await tables.teams.selectAll(
'*', SQL`WHERE userid = ${this.user.id}`
);
return {teams, count: teams.length};
},
// overriding since this takes only application/json
async batchuploadteam(params: {[k: string]: any}) {
if (this.request.method !== 'POST') {
throw new ActionError('Team uploading must be done through POST requests.');
}
if (this.request.headers['content-type'] !== 'application/json') {
throw new ActionError('Team uploading must be done through JSON.');
}
if (!this.user.registered) {
throw new ActionError(
'You must be registered and logged in to upload your teams.'
);
}
const teams = params.teams;
if (!Array.isArray(teams)) {
throw new ActionError('Malformed teams sent - must be an array.');
}
if (!teams.every(chunk => typeof chunk.team === 'string')) {
throw new ActionError('Malformed teams sent - must be all be packed.');
}
const failures = [];
for (const {team, format, id} of teams) {
if (team.length > 1200 || team.length < 5) {
throw new ActionError('Invalid team - ' + team);
}
const formatid = toID(format);
if (!formatid) {
throw new ActionError('Invalid format - ' + format);
}
try {
await actions.uploadteam.call(this, {
team,
format: formatid,
id,
});
} catch (e) {
if (!(e instanceof ActionError)) {
throw e;
}
failures.push({team, format, error: e.message});
}
}
return {
actionsuccess: failures.length === 0,
failures,
successes: teams.length - failures.length,
};
},
};
5 changes: 3 additions & 2 deletions src/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import * as gal from 'google-auth-library';
import SQL from 'sql-template-strings';
import {toID} from './server';
import {ladder, loginthrottle, sessions, users, usermodlog} from './tables';
import type {User} from './user';
import type {User, UserInfo} from './user';

const SID_DURATION = 2 * 7 * 24 * 60 * 60;
const LOGINTIME_INTERVAL = 24 * 60 * 60;
Expand Down Expand Up @@ -433,7 +433,7 @@ export class Session {
query.append(SQL`WHERE \`session\` = ${session} `);
query.append('AND `ntbb_sessions`.`userid` = `ntbb_users`.`userid` ');
query.append(' LIMIT 1');
const res = await users.database.get<{sid: string; timeout: number}>(query);
const res = await users.database.get<UserInfo & {sid: string; timeout: number}>(query);
if (!res || !(await bcrypt.compare(sid, res.sid))) {
// invalid session ID
this.deleteCookie();
Expand All @@ -451,5 +451,6 @@ export class Session {

this.sidhash = sid;
this.session = session;
this.dispatcher.user.registered = !!res.registertime;
}
}
8 changes: 8 additions & 0 deletions src/tables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,11 @@ export const userstatshistory = new DatabaseTable<{
usercount: number;
programid: 'showdown' | 'po';
}>('userstatshistory', 'id');

export const teams = new DatabaseTable<{
id: number;
userid: string;
team: string;
date: number;
format: string;
}>('teams', 'id');
1 change: 1 addition & 0 deletions src/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export interface UserInfo {
export class User {
name = 'Guest';
id = 'guest';
registered = false;
dispatcher: Dispatcher;
session: Session;
loggedin = false;
Expand Down
6 changes: 2 additions & 4 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
"skipLibCheck": true,
},
"types": ["node"],
"include": [
"./src/*", "./src/test/*", "scripts/run.js",
],
"exclude": ["./.dist/**/*", "./scripts/**/*"],
"include": ["./src/**/*"],
"exclude": ["./.dist/**/*", "./node_modules/**/*"],
}