mirror of
https://github.com/smogon/pokemon-showdown-client.git
synced 2026-08-20 01:51:42 -05:00
Fix /api/replays/edit endpoint
This commit is contained in:
@@ -1182,40 +1182,23 @@ export const actions: { [k: string]: QueryHandler } = {
|
||||
async 'replays/edit'(params) {
|
||||
const user = await this.getUser();
|
||||
if (!user.isLeader()) throw new ActionError(`Access denied.`);
|
||||
const id = toID(params.id);
|
||||
const id = (params.id || '').toLowerCase();
|
||||
if (id && !/^[a-z0-9-]+$/.test(id)) throw new ActionError(`Invalid replay ID.`);
|
||||
if (!id) throw new ActionError(`No replay ID was provided.`);
|
||||
const replay = await tables.replays.get(id);
|
||||
const replay = await Replays.get(id);
|
||||
if (!replay) throw new ActionError(`Replay ${id} not found.`);
|
||||
let pw;
|
||||
switch (Number(params.private)) {
|
||||
case 3:
|
||||
await tables.replays.update(id, {
|
||||
password: null,
|
||||
private: 3,
|
||||
});
|
||||
break;
|
||||
case 2: // private [1], no pass
|
||||
await tables.replays.update(id, {
|
||||
private: 1,
|
||||
password: null,
|
||||
});
|
||||
break;
|
||||
case 2:
|
||||
case 1:
|
||||
if (!replay.password) replay.password = Replays.generatePassword();
|
||||
pw = replay.password;
|
||||
await tables.replays.update(id, {
|
||||
private: 1,
|
||||
password: replay.password,
|
||||
});
|
||||
case 0:
|
||||
replay.private = Number(params.private) as 0;
|
||||
break;
|
||||
default:
|
||||
await tables.replays.update(id, {
|
||||
password: null,
|
||||
private: 0,
|
||||
});
|
||||
break;
|
||||
throw new ActionError(`Invalid private value: ${params.private!}`);
|
||||
}
|
||||
return { password: pw };
|
||||
const editedReplay = await Replays.edit(replay);
|
||||
return { password: editedReplay.password || undefined };
|
||||
},
|
||||
async 'replays/batch'(params) {
|
||||
if (!params.ids) {
|
||||
|
||||
@@ -6,32 +6,10 @@
|
||||
* Ported to Postgres by Zarel.
|
||||
*/
|
||||
import { toID, time } from './utils.ts';
|
||||
import { replayPlayers, replays } from './tables.ts';
|
||||
import { replayPlayers, replays, type ReplayRow } from './tables.ts';
|
||||
import { SQL } from './database.ts';
|
||||
import * as crypto from 'node:crypto';
|
||||
|
||||
// must be a type and not an interface to qualify as an SQLRow
|
||||
export type ReplayRow = {
|
||||
id: string,
|
||||
format: string,
|
||||
/** player names delimited by `,`; starting with `!` denotes that player wants the replay private */
|
||||
players: string,
|
||||
log: string,
|
||||
inputlog: string | null,
|
||||
uploadtime: number,
|
||||
views: number,
|
||||
formatid: string,
|
||||
rating: number | null,
|
||||
/**
|
||||
* 0 = public
|
||||
* 1 = private (with or without password)
|
||||
* 2 = NOT USED; ONLY USED IN PREPREPLAY
|
||||
* 3 = deleted
|
||||
* 10 = autosaved
|
||||
*/
|
||||
private: 0 | 1 | 2 | 3 | 10,
|
||||
password: string | null,
|
||||
};
|
||||
type Replay = Omit<ReplayRow, 'players' | 'password' | 'views' | 'formatid' | 'inputlog'> & {
|
||||
players: string[],
|
||||
views?: number,
|
||||
@@ -154,9 +132,13 @@ export const Replays = new class {
|
||||
);
|
||||
}
|
||||
|
||||
/** can currently only edit privacy level */
|
||||
async edit(replay: Replay) {
|
||||
const replayData = this.toReplayRow(replay);
|
||||
await replays.update(replay.id, { private: replayData.private, password: replayData.password });
|
||||
const update = { private: replayData.private, password: replayData.password };
|
||||
await replays.update(replay.id, update);
|
||||
await replayPlayers.updateAll(update)`WHERE id = ${replay.id}`;
|
||||
return replayData;
|
||||
}
|
||||
|
||||
generatePassword(length = 31) {
|
||||
|
||||
@@ -5,7 +5,6 @@ import { MockDatabase, MySQLDatabase, PGDatabase, SQLiteDatabase } from './datab
|
||||
import { Config } from './config-loader.ts';
|
||||
|
||||
import type { LadderEntry } from './ladder.ts';
|
||||
import type { ReplayRow } from './replays.ts';
|
||||
import type { Suspect } from './actions.ts';
|
||||
|
||||
type DatabaseDriver = 'mysql' | 'postgres' | 'sqlite' | 'mock';
|
||||
@@ -85,6 +84,28 @@ export const replayPrep = replaysDB.getTable<{
|
||||
uploadtime: number,
|
||||
}>('replayprep', 'id');
|
||||
|
||||
// must be a type and not an interface to qualify as an SQLRow
|
||||
export type ReplayRow = {
|
||||
id: string,
|
||||
format: string,
|
||||
/** player names delimited by `,`; starting with `!` denotes that player wants the replay private */
|
||||
players: string,
|
||||
log: string,
|
||||
inputlog: string | null,
|
||||
uploadtime: number,
|
||||
views: number,
|
||||
formatid: string,
|
||||
rating: number | null,
|
||||
/**
|
||||
* 0 = public
|
||||
* 1 = private (with or without password)
|
||||
* 2 = NOT USED; ONLY USED IN PREPREPLAY
|
||||
* 3 = deleted
|
||||
* 10 = autosaved
|
||||
*/
|
||||
private: 0 | 1 | 2 | 3 | 10,
|
||||
password: string | null,
|
||||
};
|
||||
export const replays = replaysDB.getTable<
|
||||
ReplayRow
|
||||
>('replays', 'id');
|
||||
|
||||
@@ -5,7 +5,7 @@ import { strict as assert } from 'node:assert';
|
||||
import { suite, test } from 'node:test';
|
||||
|
||||
import { Replays } from '../replays.ts';
|
||||
import { replays } from '../tables.ts';
|
||||
import { replayPlayers, replays } from '../tables.ts';
|
||||
|
||||
void suite('Replay database manipulation', () => {
|
||||
// prepreplay no longer exists
|
||||
@@ -48,11 +48,15 @@ void suite('Replay database manipulation', () => {
|
||||
});
|
||||
|
||||
void test('should support editing replays', async () => {
|
||||
await replays.insert({
|
||||
await Replays.add({
|
||||
id: 'edittest',
|
||||
views: 1,
|
||||
players: 'annika,annikatesting',
|
||||
players: ['annika', 'annikatesting'],
|
||||
format: 'gen8ou',
|
||||
log: '',
|
||||
inputlog: null,
|
||||
uploadtime: 1,
|
||||
private: 0,
|
||||
rating: null,
|
||||
});
|
||||
|
||||
const original = await Replays.get('edittest');
|
||||
@@ -62,8 +66,14 @@ void suite('Replay database manipulation', () => {
|
||||
original.private = 2;
|
||||
await Replays.edit(original);
|
||||
|
||||
await Replays.get('edittest');
|
||||
assert.equal(original.private, 2);
|
||||
const edited = await Replays.get('edittest');
|
||||
assert.equal(edited?.private, 2);
|
||||
const playerRows = await replayPlayers.selectAll(['private', 'password'])`WHERE id = ${'edittest'}`;
|
||||
assert.equal(playerRows.length, 2);
|
||||
for (const row of playerRows) {
|
||||
assert.equal(row.private, 1);
|
||||
assert.equal(row.password, null);
|
||||
}
|
||||
});
|
||||
|
||||
void test('should properly upload replays', async () => {
|
||||
|
||||
Reference in New Issue
Block a user