Skip to content
Extraits de code Groupes Projets
Valider 237ed292 rédigé par Will Hunt's avatar Will Hunt
Parcourir les fichiers

Add v2 and v3 schemas

parent 5fd6b1ae
Aucune branche associée trouvée
Aucune étiquette associée trouvée
Aucune requête de fusion associée trouvée
......@@ -2,4 +2,5 @@ import { DiscordStore } from "../store";
export interface IDbSchema {
description: string;
run(store: DiscordStore): Promise<null>;
rollBack(store: DiscordStore): Promise<null>;
}
......@@ -14,6 +14,12 @@ export class Schema implements IDbSchema {
userId TEXT UNIQUE NOT NULL,
token TEXT UNIQUE NOT NULL
);`, "user_tokens");
})
});
}
public rollBack(store: DiscordStore): Promise<null> {
return store.db.execAsync(
`DROP TABLE IF EXISTS schema;
DROP TABLE IF EXISTS user_tokens`,
);
}
}
import {IDbSchema} from "./dbschema";
import {DiscordStore} from "../store";
export class Schema implements IDbSchema {
public description = "Create DM Table, User Options";
public run(store: DiscordStore): Promise<null> {
return Promise.all([
store.create_table(`
CREATE TABLE dm_rooms (
discord_id TEXT NOT NULL,
channel_id TEXT NOT NULL,
room_id TEXT UNIQUE NOT NULL
);`, "dm_rooms"),
store.create_table(`
CREATE TABLE client_options (
discord_id TEXT UNIQUE NOT NULL,
options INTEGER NOT NULL
);`, "client_options",
)]);
}
public rollBack(store: DiscordStore): Promise<null> {
return store.db.execAsync(
`DROP TABLE IF EXISTS dm_rooms;
DROP TABLE IF EXISTS client_options;`,
);
}
}
import {IDbSchema} from "./dbschema";
import {DiscordStore} from "../store";
import { Client } from "discord.js";
import * as log from "npmlog";
import * as Bluebird from "bluebird";
const READY_TIMEOUT = 5000;
export class Schema implements IDbSchema {
public description = "user_tokens split into user_id_discord_id";
public run(store: DiscordStore): Promise<null> {
const promise = Promise.all([store.create_table(`
CREATE TABLE user_id_discord_id (
discord_id TEXT UNIQUE NOT NULL,
user_id TEXT UNIQUE NOT NULL
);`, "user_id_discord_id"),
store.create_table(`
CREATE TABLE discord_id_token (
discord_id TEXT UNIQUE NOT NULL,
token TEXT NOT NULL
);`, "discord_id_token",
)]);
return promise.then(() => {
// Backup before moving data.
return store.backup_database();
}).then(() => {
// Move old data to new tables.
return this.moveUserIds(store);
}).then(() => {
log.info("SchemaV3", "Dropping user_tokens. Check backup database for table.");
// Drop old table.
return store.db.execAsync(
`DROP TABLE user_tokens;`,
);
});
}
public rollBack(store: DiscordStore): Promise<null> {
return Promise.all([store.db.execAsync(
`DROP TABLE IF EXISTS user_id_discord_id;`,
), store.db.execAsync(
`DROP TABLE IF EXISTS discord_id_token;`,
)]);
}
private moveUserIds(store: DiscordStore): Promise<null> {
log.info("SchemaV3", "Performing one time moving of tokens to new table. Please wait.");
return store.db.allAsync(
`
SELECT *
FROM user_tokens
`,
).then( (rows) => {
let promises = [];
for (const row of rows) {
let discordId = null;
log.info("SchemaV3", "Moving %s.", row.userId);
promises.push(this.getDiscordIdFromToken(row.token).catch((err) => {
log.info("SchemaV3", "Dropping %s from database due to an invalid token.");
return null;
}).then((dId) => {
if(dId === null) {
return null;
}
discordId = dId;
log.verbose("SchemaV3", "INSERT INTO discord_id_token.");
return store.db.runAsync(
`
INSERT INTO discord_id_token (discord_id,token)
VALUES ($discordId,$token);
`
, {
$discordId: discordId,
$token: row.token,
});
}).then(() => {
if(discordId === null) {
return null;
}
log.verbose("SchemaV3", "INSERT INTO user_id_discord_id.");
return store.db.runAsync(
`
INSERT INTO user_id_discord_id (discord_id,user_id)
VALUES ($discordId,$userId);
`
, {
$discordId: discordId,
$userId: row.userId,
});
}));
}
return Bluebird.all(promises);
});
}
private getDiscordIdFromToken(token: String): any {
const client: any = new Client({
fetchAllMembers: false,
sync: false,
messageCacheLifetime: 5,
});
return new Bluebird((resolve, reject) => {
client.login(token).catch(reject);
client.on("ready", () => {
const id = client.user.id;
client.destroy();
resolve(id);
});
}).timeout(READY_TIMEOUT).catch((err: Error) => {
log.warn("SchemaV3", "Could not login as the bot user '%s'", err.message);
throw Error("Could not retrive ID");
});
}
}
import * as SQLite3 from "sqlite3";
import * as log from "npmlog";
import * as Bluebird from "bluebird";
import * as fs from "fs";
import { IDbSchema } from "./dbschema/dbschema";
const CURRENT_SCHEMA = 1;
const CURRENT_SCHEMA = 3;
/**
* Stores data for specific users and data not specific to rooms.
*/
......@@ -13,47 +14,70 @@ export class DiscordStore {
*/
public db: any;
private version: number;
private filepath: string;
constructor (filepath) {
this.db = new SQLite3.Database(filepath, (err) => {
this.version = null;
this.filepath = filepath;
}
public init () {
return Bluebird.coroutine(this.co_init.bind(this))();
}
public open_database(): Promise<null|Error> {
log.info("DiscordStore", "Opening SQLITE database %s", this.filepath);
return new Promise((resolve, reject) => {
this.db = new SQLite3.Database(this.filepath, (err) => {
if (err) {
log.error("DiscordStore", "Error opening database, %s");
throw new Error("Couldn't open database. The appservice won't be able to continue.");
reject(new Error("Couldn't open database. The appservice won't be able to continue."));
return;
}
});
this.db = Bluebird.promisifyAll(this.db);
this.version = null;
resolve();
});
});
}
public backup_database(): Promise<null|Error> {
return new Promise((resolve, reject) => {
const rd = fs.createReadStream(this.filepath);
rd.on("error", reject);
const wr = fs.createWriteStream(this.filepath + ".backup");
wr.on("error", reject);
wr.on("close", resolve);
rd.pipe(wr);
});
}
/**
* Checks the database has all the tables needed.
*/
public init () {
public * co_init () {
log.info("DiscordStore", "Starting DB Init");
let oldVersion;
let version;
return this.getSchemaVersion().then( (v) => {
oldVersion = v;
version = v;
yield this.open_database();
let oldVersion = yield this.getSchemaVersion();
let version = oldVersion;
let promises = [];
while (version < CURRENT_SCHEMA) {
version++;
const schemaClass = require(`./dbschema/v${version}.js`).Schema;
const schema = (new schemaClass() as IDbSchema);
log.info("DiscordStore", `Updating database to v${version}, ${schema.description}`);
promises.push(schema.run(this).then(() => {
log.info("DiscordStore", "Updated database v%s", version);
}));
log.info("DiscordStore", `Updating database to v${version}, "${schema.description}"`);
try {
yield schema.run(this);
log.info("DiscordStore", "Updated database to version %s", version);
} catch (ex) {
log.error("DiscordStore", "Couldn't update database to schema %s", version);
log.error("DiscordStore", ex);
log.error("DiscordStore", "Rolling back to version %s", version - 1);
yield schema.rollBack(this);
throw Error("Failure to update to latest schema.");
}
this.version = version;
yield this.setSchemaVersion(oldVersion, version);
}
return Promise.all(promises);
}).then( () => {
return this.setSchemaVersion(oldVersion, version).then( () => {
log.info("DiscordStore", "Updated database to the latest schema");
});
}).catch( (err) => {
log.error("DiscordStore", "Couldn't update database to the latest version! Bailing");
throw err;
});
}
public create_table (statement, tablename) {
......@@ -81,6 +105,18 @@ export class DiscordStore {
});
}
public delete_user_token(userId: string) {
log.silly("SQL", "delete_user_token => %s", userId);
return this.db.runAsync(
`DELETE FROM user_tokens WHERE userId = $id;`
, {
$id: userId,
}).catch( (err) => {
log.error("TwitDB", "Error deleting user token %s", err);
throw err;
});
}
public get_user_token(userId: string): Promise<string> {
log.silly("SQL", "get_user_token => %s", userId);
return this.db.getAsync(
......@@ -99,6 +135,45 @@ export class DiscordStore {
});
}
public get_dm_room(discordId, discordChannel): Promise<string> {
log.silly("SQL", "get_dm_room => %s", discordChannel); // Don't show discordId for privacy reasons
return this.db.getAsync(
`
SELECT room_id
FROM dm_rooms
WHERE dm_rooms.discord_id = $discordId
AND dm_rooms.discord_channel = $discordChannel;
`
, {
$discordId: discordId,
$discordChannel: discordChannel,
}).then( (row) => {
return row !== undefined ? row.room_id : null;
}).catch( (err) => {
log.error("TwitDB", "Error getting room_id %s", err.Error);
throw err;
});
}
public set_dm_room(discordId, discordChannel, roomId): Promise<null> {
log.silly("SQL", "set_dm_room => %s", discordChannel); // Don't show discordId for privacy reasons
return this.db.runAsync(
`
REPLACE INTO dm_rooms (discord_id,discord_channel,room_id)
VALUES ($discordId,$discordChannel,$roomId);
`
, {
$discordId: discordId,
$discordChannel: discordChannel,
$roomId: roomId,
}).then( () => {
return
}).catch( (err) => {
log.error("TwitDB", "Error executing set_dm_room query %s", err.Error);
throw err;
});
}
public get_users_tokens(): Promise<any> {
log.silly("SQL", "get_users_tokens");
return this.db.allAsync(
......
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment