mirror of
https://github.com/spacebarchat/server.git
synced 2026-05-24 08:05:29 +00:00
Switch to migrations fully
This commit is contained in:
@@ -45,7 +45,8 @@ router.post("/", route({ body: "InviteCreateSchema", permission: "CREATE_INSTANT
|
||||
channel_id: channel_id,
|
||||
inviter_id: user_id
|
||||
}).save();
|
||||
const data = invite.toJSON();
|
||||
//TODO: check this, removed toJSON call
|
||||
const data = JSON.parse(JSON.stringify(invite));
|
||||
data.inviter = await User.getPublicUser(req.user_id);
|
||||
data.guild = await Guild.findOne({ where: { id: guild_id } });
|
||||
data.channel = channel;
|
||||
|
||||
@@ -61,7 +61,8 @@ router.patch("/", route({ body: "GuildUpdateSchema"}), async (req: Request, res:
|
||||
// TODO: check if body ids are valid
|
||||
guild.assign(body);
|
||||
|
||||
const data = guild.toJSON();
|
||||
//TODO: check this, removed toJSON call
|
||||
const data = JSON.parse(JSON.stringify(guild));
|
||||
// TODO: guild hashes
|
||||
// TODO: fix vanity_url_code, template_id
|
||||
delete data.vanity_url_code;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Config, Guild, Session } from "@fosscord/util";
|
||||
import { createQueryBuilder } from "typeorm";
|
||||
|
||||
export async function initInstance() {
|
||||
// TODO: clean up database and delete tombstone data
|
||||
@@ -9,7 +10,7 @@ export async function initInstance() {
|
||||
const { autoJoin } = Config.get().guild;
|
||||
|
||||
if (autoJoin.enabled && !autoJoin.guilds?.length) {
|
||||
let guild = await Guild.findOne({});
|
||||
let guild = await Guild.findOne({where: {}, order: {id: "ASC"}});
|
||||
if (guild) {
|
||||
// @ts-ignore
|
||||
await Config.set({ guild: { autoJoin: { guilds: [guild.id] } } });
|
||||
|
||||
@@ -201,9 +201,10 @@ export async function postHandleMessage(message: Message) {
|
||||
export async function sendMessage(opts: MessageOptions) {
|
||||
const message = await handleMessage({ ...opts, timestamp: new Date() });
|
||||
|
||||
//TODO: check this, removed toJSON call
|
||||
await Promise.all([
|
||||
Message.insert(message),
|
||||
emitEvent({ event: "MESSAGE_CREATE", channel_id: opts.channel_id, data: message.toJSON() } as MessageCreateEvent)
|
||||
emitEvent({ event: "MESSAGE_CREATE", channel_id: opts.channel_id, data: message } as MessageCreateEvent)
|
||||
]);
|
||||
|
||||
postHandleMessage(message).catch((e) => {}); // no await as it should catch error non-blockingly
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
export * from "./entities/AssetCacheItem";
|
||||
export * from "./handlers/Message";
|
||||
export * from "./handlers/route";
|
||||
export * from "./handlers/Voice";
|
||||
export * from "./utility/Base64";
|
||||
export * from "./utility/ipAddress";
|
||||
export * from "./handlers/Message";
|
||||
export * from "./utility/passwordStrength";
|
||||
export * from "./utility/RandomInviteID";
|
||||
export * from "./handlers/route";
|
||||
export * from "./utility/String";
|
||||
export * from "./handlers/Voice";
|
||||
export * from "./entities/AssetCacheItem";
|
||||
export * from "./utility/String";
|
||||
Generated
BIN
Binary file not shown.
+2
-1
@@ -86,6 +86,7 @@
|
||||
"node-fetch": "^2.6.7",
|
||||
"node-os-utils": "^1.3.7",
|
||||
"patch-package": "^6.4.7",
|
||||
"pg": "^8.7.3",
|
||||
"picocolors": "^1.0.0",
|
||||
"proxy-agent": "^5.0.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
@@ -94,4 +95,4 @@
|
||||
"typescript": "^4.1.2",
|
||||
"ws": "^8.8.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"type": "sqlite",
|
||||
"database": "../bundle/database.db",
|
||||
"migrations": ["src/migrations/*.ts"],
|
||||
"entities": ["src/entities/*.ts"],
|
||||
"cli": {
|
||||
"migrationsDir": "src/migrations"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
|
||||
import { BaseClass } from "./BaseClass";
|
||||
import { User } from "./User";
|
||||
import crypto from "crypto";
|
||||
|
||||
@Entity("backup_codes")
|
||||
export class BackupCode extends BaseClass {
|
||||
@@ -17,19 +16,4 @@ export class BackupCode extends BaseClass {
|
||||
|
||||
@Column()
|
||||
expired: boolean;
|
||||
}
|
||||
|
||||
export function generateMfaBackupCodes(user_id: string) {
|
||||
let backup_codes: BackupCode[] = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const code = BackupCode.create({
|
||||
user: { id: user_id },
|
||||
code: crypto.randomBytes(4).toString("hex"), // 8 characters
|
||||
consumed: false,
|
||||
expired: false,
|
||||
});
|
||||
backup_codes.push(code);
|
||||
}
|
||||
|
||||
return backup_codes;
|
||||
}
|
||||
@@ -8,27 +8,30 @@ export class BaseClassWithoutId extends BaseEntity {
|
||||
this.assign(props);
|
||||
}
|
||||
|
||||
private get construct(): any {
|
||||
/*private get construct(): any {
|
||||
return this.constructor;
|
||||
}
|
||||
}*/
|
||||
|
||||
private get metadata() {
|
||||
return this.construct.getRepository().metadata as EntityMetadata;
|
||||
}
|
||||
/*private get metadata() {
|
||||
console.log("getMetadata")
|
||||
return dataSource.getRepository(this.constructor).metadata as EntityMetadata;
|
||||
//return this.construct.getRepository().metadata as EntityMetadata;
|
||||
}*/
|
||||
|
||||
assign(props: any = {}) {
|
||||
//console.log(`assign (${typeof this})...`)
|
||||
delete props.opts;
|
||||
delete props.props;
|
||||
|
||||
const properties = new Set(
|
||||
/*const properties = new Set(
|
||||
this.metadata.columns
|
||||
.map((x: any) => x.propertyName)
|
||||
.concat(this.metadata.relations.map((x) => x.propertyName))
|
||||
);
|
||||
);*/
|
||||
// will not include relational properties
|
||||
|
||||
for (const key in props) {
|
||||
if (!properties.has(key)) continue;
|
||||
//if (!properties.has(key)) continue;
|
||||
// @ts-ignore
|
||||
const setter = this[`set${key.capitalize()}`]; // use setter function if it exists
|
||||
|
||||
@@ -41,7 +44,8 @@ export class BaseClassWithoutId extends BaseEntity {
|
||||
}
|
||||
}
|
||||
|
||||
toJSON(): any {
|
||||
/*toJSON(): any {
|
||||
console.log("toJSON...")
|
||||
return Object.fromEntries(
|
||||
this.metadata.columns // @ts-ignore
|
||||
.map((x) => [x.propertyName, this[x.propertyName]]) // @ts-ignore
|
||||
@@ -54,6 +58,7 @@ export class BaseClassWithoutId extends BaseEntity {
|
||||
propertyPath: string,
|
||||
value: number | string
|
||||
) {
|
||||
console.log("increment...")
|
||||
const repository = this.getRepository();
|
||||
return repository.increment(conditions, propertyPath, value);
|
||||
}
|
||||
@@ -63,9 +68,10 @@ export class BaseClassWithoutId extends BaseEntity {
|
||||
propertyPath: string,
|
||||
value: number | string
|
||||
) {
|
||||
console.log("increment...")
|
||||
const repository = this.getRepository();
|
||||
return repository.decrement(conditions, propertyPath, value);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
export const PrimaryIdColumn = process.env.DATABASE?.startsWith("mongodb") ? ObjectIdColumn : PrimaryColumn;
|
||||
|
||||
@@ -122,17 +122,19 @@ export class Member extends BaseClassWithoutId {
|
||||
}
|
||||
|
||||
static async removeFromGuild(user_id: string, guild_id: string) {
|
||||
const guild = await Guild.findOneOrFail({ select: ["owner_id"], where: { id: guild_id } });
|
||||
const guild = await Guild.findOneOrFail({ select: ["owner_id", "member_count"], where: { id: guild_id } });
|
||||
if (guild.owner_id === user_id) throw new Error("The owner cannot be removed of the guild");
|
||||
const member = await Member.findOneOrFail({ where: { id: user_id, guild_id }, relations: ["user"] });
|
||||
|
||||
// use promise all to execute all promises at the same time -> save time
|
||||
//TODO: check for bugs
|
||||
if(guild.member_count) guild.member_count--;
|
||||
return Promise.all([
|
||||
Member.delete({
|
||||
id: user_id,
|
||||
guild_id,
|
||||
}),
|
||||
Guild.decrement({ id: guild_id }, "member_count", -1),
|
||||
//Guild.decrement({ id: guild_id }, "member_count", -1),
|
||||
|
||||
emitEvent({
|
||||
event: "GUILD_DELETE",
|
||||
@@ -259,7 +261,8 @@ export class Member extends BaseClassWithoutId {
|
||||
mute: false,
|
||||
pending: false,
|
||||
};
|
||||
|
||||
//TODO: check for bugs
|
||||
if(guild.member_count) guild.member_count++;
|
||||
await Promise.all([
|
||||
new Member({
|
||||
...member,
|
||||
@@ -276,7 +279,7 @@ export class Member extends BaseClassWithoutId {
|
||||
},
|
||||
// Member.save is needed because else the roles relations wouldn't be updated
|
||||
}).save(),
|
||||
Guild.increment({ id: guild_id }, "member_count", 1),
|
||||
//Guild.increment({ id: guild_id }, "member_count", 1),
|
||||
emitEvent({
|
||||
event: "GUILD_MEMBER_ADD",
|
||||
data: {
|
||||
|
||||
@@ -4,3 +4,4 @@ export * from "./util/index";
|
||||
export * from "./interfaces/index";
|
||||
export * from "./entities/index";
|
||||
export * from "./dtos/index";
|
||||
export * from "./util/MFA";
|
||||
@@ -1,13 +0,0 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class EmojiRoles1633864260873 implements MigrationInterface {
|
||||
name = "EmojiRoles1633864260873";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "emojis" ADD "roles" text NOT NULL DEFAULT ''`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "emojis" DROP COLUMN column_name "roles"`);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class EmojiUser1633864669243 implements MigrationInterface {
|
||||
name = "EmojiUser1633864669243";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "emojis" ADD "user_id" varchar`);
|
||||
try {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "emojis" ADD CONSTRAINT FK_fa7ddd5f9a214e28ce596548421 FOREIGN KEY (user_id) REFERENCES users(id)`
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"sqlite doesn't support altering foreign keys: https://stackoverflow.com/questions/1884818/how-do-i-add-a-foreign-key-to-an-existing-sqlite-table"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "emojis" DROP COLUMN column_name "user_id"`);
|
||||
await queryRunner.query(`ALTER TABLE "emojis" DROP CONSTRAINT FK_fa7ddd5f9a214e28ce596548421`);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class VanityInvite1633881705509 implements MigrationInterface {
|
||||
name = "VanityInvite1633881705509";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
try {
|
||||
await queryRunner.query(`ALTER TABLE "emojis" DROP COLUMN vanity_url_code`);
|
||||
await queryRunner.query(`ALTER TABLE "emojis" DROP CONSTRAINT FK_c2c1809d79eb120ea0cb8d342ad`);
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "emojis" ADD vanity_url_code varchar`);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "emojis" ADD CONSTRAINT FK_c2c1809d79eb120ea0cb8d342ad FOREIGN KEY ("vanity_url_code") REFERENCES "invites"("code") ON DELETE NO ACTION ON UPDATE NO ACTION`
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
import { MigrationInterface, QueryRunner, Table, TableColumn, TableForeignKey } from "typeorm";
|
||||
|
||||
export class Stickers1634308884591 implements MigrationInterface {
|
||||
name = "Stickers1634308884591";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.dropForeignKey("read_states", "FK_6f255d873cfbfd7a93849b7ff74");
|
||||
await queryRunner.changeColumn(
|
||||
"stickers",
|
||||
"tags",
|
||||
new TableColumn({ name: "tags", type: "varchar", isNullable: true })
|
||||
);
|
||||
await queryRunner.changeColumn(
|
||||
"stickers",
|
||||
"pack_id",
|
||||
new TableColumn({ name: "pack_id", type: "varchar", isNullable: true })
|
||||
);
|
||||
await queryRunner.changeColumn("stickers", "type", new TableColumn({ name: "type", type: "integer" }));
|
||||
await queryRunner.changeColumn(
|
||||
"stickers",
|
||||
"format_type",
|
||||
new TableColumn({ name: "format_type", type: "integer" })
|
||||
);
|
||||
await queryRunner.changeColumn(
|
||||
"stickers",
|
||||
"available",
|
||||
new TableColumn({ name: "available", type: "boolean", isNullable: true })
|
||||
);
|
||||
await queryRunner.changeColumn(
|
||||
"stickers",
|
||||
"user_id",
|
||||
new TableColumn({ name: "user_id", type: "boolean", isNullable: true })
|
||||
);
|
||||
await queryRunner.createForeignKey(
|
||||
"stickers",
|
||||
new TableForeignKey({
|
||||
name: "FK_8f4ee73f2bb2325ff980502e158",
|
||||
columnNames: ["user_id"],
|
||||
referencedColumnNames: ["id"],
|
||||
referencedTableName: "users",
|
||||
onDelete: "CASCADE",
|
||||
})
|
||||
);
|
||||
await queryRunner.createTable(
|
||||
new Table({
|
||||
name: "sticker_packs",
|
||||
columns: [
|
||||
new TableColumn({ name: "id", type: "varchar", isPrimary: true }),
|
||||
new TableColumn({ name: "name", type: "varchar" }),
|
||||
new TableColumn({ name: "description", type: "varchar", isNullable: true }),
|
||||
new TableColumn({ name: "banner_asset_id", type: "varchar", isNullable: true }),
|
||||
new TableColumn({ name: "cover_sticker_id", type: "varchar", isNullable: true }),
|
||||
],
|
||||
foreignKeys: [
|
||||
new TableForeignKey({
|
||||
columnNames: ["cover_sticker_id"],
|
||||
referencedColumnNames: ["id"],
|
||||
referencedTableName: "stickers",
|
||||
}),
|
||||
],
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import { MigrationInterface, QueryRunner, TableColumn } from "typeorm";
|
||||
|
||||
export class Presence1634424361103 implements MigrationInterface {
|
||||
name = "Presence1634424361103";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
queryRunner.addColumn("sessions", new TableColumn({ name: "activites", type: "text" }));
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import { MigrationInterface, QueryRunner, TableColumn } from "typeorm";
|
||||
|
||||
export class MigrationTimestamp1634426540271 implements MigrationInterface {
|
||||
name = "MigrationTimestamp1634426540271";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.changeColumn(
|
||||
"migrations",
|
||||
"timestamp",
|
||||
new TableColumn({ name: "timestampe", type: "bigint", isNullable: false })
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class ReleaseTypo1648643945733 implements MigrationInterface {
|
||||
name = "ReleaseTypo1648643945733";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
//drop table first because typeorm creates it before migrations run
|
||||
await queryRunner.dropTable("client_release", true);
|
||||
await queryRunner.renameTable("client_relase", "client_release");
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.dropTable("client_relase", true);
|
||||
await queryRunner.renameTable("client_release", "client_relase");
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+67
-39
@@ -1,72 +1,100 @@
|
||||
import path from "path";
|
||||
import "reflect-metadata";
|
||||
import { DataSource, createConnection } from "typeorm";
|
||||
import { DataSource, createConnection, DataSourceOptions, PrimaryColumn, PrimaryGeneratedColumn } from "typeorm";
|
||||
import * as Models from "../entities";
|
||||
import { Migration } from "../entities/Migration";
|
||||
import { yellow, green, red } from "picocolors";
|
||||
import fs from "fs";
|
||||
import { exit } from "process";
|
||||
import { BaseClass, BaseClassWithoutId } from "../entities";
|
||||
|
||||
// UUID extension option is only supported with postgres
|
||||
// We want to generate all id's with Snowflakes that's why we have our own BaseEntity class
|
||||
|
||||
let promise: Promise<any>;
|
||||
let dataSource: DataSource | undefined;
|
||||
let dbConnectionString = process.env.DATABASE || path.join(process.cwd(), "database.db");
|
||||
let verbose_db = false;
|
||||
let dataSource: DataSource;
|
||||
|
||||
|
||||
|
||||
export async function initDatabase(): Promise<DataSource> {
|
||||
if (dataSource) return dataSource; // prevent initalizing multiple times
|
||||
|
||||
let dso = getDataSourceOptions();
|
||||
console.log(`[Database] ${yellow(`Connecting to ${dso.type} database...`)}`);
|
||||
|
||||
//promise = dataSource.initialize();
|
||||
//await promise;
|
||||
|
||||
console.log(`[Database] ${green("Connected!")}`);
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
|
||||
export function closeDatabase() {
|
||||
dataSource?.destroy();
|
||||
}
|
||||
|
||||
function getDataSourceOptions(): DataSourceOptions {
|
||||
//get connection string and check for migrations
|
||||
const dbConnectionString = process.env.DATABASE || path.join(process.cwd(), "database.db");
|
||||
const type = dbConnectionString.includes("://") ? dbConnectionString.split(":")[0]?.replace("+srv", "") : "sqlite" as any;
|
||||
const isSqlite = type.includes("sqlite");
|
||||
if(process.env.DB_VERBOSE) verbose_db = true;
|
||||
|
||||
console.log(`[Database] ${yellow(`connecting to ${type} db`)}`);
|
||||
const migrationsExist = fs.existsSync(path.join(__dirname, "..", "migrations", type));
|
||||
//read env vars
|
||||
const synchronizeInsteadOfMigrations = "DB_UNSAFE" in process.env;
|
||||
const verboseDb = "DB_VERBOSE" in process.env;
|
||||
|
||||
if(isSqlite)
|
||||
console.log(`[Database] ${red(`You are running sqlite! Please keep in mind that we recommend setting up a dedicated database!`)}`);
|
||||
if(verbose_db)
|
||||
console.log(`[Database] ${red(`Verbose database logging is enabled, this might impact performance! Unset VERBOSE_DB to disable.`)}`);
|
||||
// @ts-ignore
|
||||
dataSource = new DataSource({
|
||||
if(verboseDb)
|
||||
console.log(`[Database] ${red(`Verbose database logging is enabled, this might impact performance! Unset DB_VERBOSE to disable.`)}`);
|
||||
|
||||
if(synchronizeInsteadOfMigrations){
|
||||
console.log(`[Database] ${red(`Unsafe database upgrades are enabled! We are not responsible for broken databases! Unset DB_UNSAFE to disable.`)}`);
|
||||
}
|
||||
else if(!migrationsExist) {
|
||||
console.log(`[Database] ${red(`Database engine not supported! Set UNSAFE_DB to bypass.`)}`);
|
||||
console.log(`[Database] ${red(`Please mention this to Fosscord developers, and provide this info:`)}`);
|
||||
console.log(`[Database]\n${red(JSON.stringify({
|
||||
db_type: type,
|
||||
migrations_exist: migrationsExist
|
||||
}, null, 4))}`);
|
||||
|
||||
//exit(1);
|
||||
}
|
||||
|
||||
return {
|
||||
type,
|
||||
charset: 'utf8mb4',
|
||||
url: isSqlite ? undefined : dbConnectionString,
|
||||
database: isSqlite ? dbConnectionString : undefined,
|
||||
// @ts-ignore
|
||||
entities: Object.values(Models).filter((x) => x.constructor.name !== "Object" && x.name),
|
||||
synchronize: type !== "mongodb",
|
||||
logging: verbose_db,
|
||||
//entities: Object.values(Models).filter((x) => x.constructor.name !== "Object" && x.constructor.name !== "Array" && x.constructor.name !== "BigInt" && x).map(x=>x.name),
|
||||
entities: Object.values(Models).filter((x) => x.constructor.name == "Function" && shouldIncludeEntity(x.name)),
|
||||
synchronize: synchronizeInsteadOfMigrations,
|
||||
logging: verboseDb,
|
||||
cache: {
|
||||
duration: 1000 * 3, // cache all find queries for 3 seconds
|
||||
},
|
||||
bigNumberStrings: false,
|
||||
supportBigNumbers: true,
|
||||
name: "default",
|
||||
migrations: [path.join(__dirname, "..", "migrations", "*.js")],
|
||||
});
|
||||
promise = dataSource.initialize();
|
||||
await promise;
|
||||
// run migrations, and if it is a new fresh database, set it to the last migration
|
||||
if (dataSource.migrations.length) {
|
||||
if (!(await Migration.findOne({}))) {
|
||||
let i = 0;
|
||||
|
||||
await Migration.insert(
|
||||
dataSource.migrations.map((x) => ({
|
||||
id: i++,
|
||||
name: x.name,
|
||||
timestamp: Date.now(),
|
||||
}))
|
||||
);
|
||||
migrations: synchronizeInsteadOfMigrations ? [] : [path.join(__dirname, "..", "migrations", type, "*.js")],
|
||||
migrationsRun: !synchronizeInsteadOfMigrations,
|
||||
cli: {
|
||||
migrationsDir: `src/migrations/${type}`
|
||||
}
|
||||
}
|
||||
await dataSource.runMigrations();
|
||||
console.log(`[Database] ${green("connected")}`);
|
||||
|
||||
return promise;
|
||||
} as DataSourceOptions;
|
||||
}
|
||||
|
||||
export { dataSource };
|
||||
|
||||
export function closeDatabase() {
|
||||
dataSource?.destroy();
|
||||
function shouldIncludeEntity(name: string): boolean {
|
||||
return ![
|
||||
BaseClassWithoutId,
|
||||
PrimaryColumn,
|
||||
BaseClass,
|
||||
PrimaryGeneratedColumn
|
||||
].map(x=>x.name).includes(name);
|
||||
}
|
||||
|
||||
export default dataSource = new DataSource(getDataSourceOptions());
|
||||
@@ -1,72 +0,0 @@
|
||||
import path from "path";
|
||||
import "reflect-metadata";
|
||||
import { Connection, createConnection } from "typeorm";
|
||||
import * as Models from "../entities";
|
||||
import { Migration } from "../entities/Migration";
|
||||
import { yellow, green, red } from "picocolors";
|
||||
|
||||
// UUID extension option is only supported with postgres
|
||||
// We want to generate all id's with Snowflakes that's why we have our own BaseEntity class
|
||||
|
||||
let promise: Promise<any>;
|
||||
let dbConnection: Connection | undefined;
|
||||
let dbConnectionString = process.env.DATABASE || path.join(process.cwd(), "database.db");
|
||||
|
||||
export function initDatabase(): Promise<Connection> {
|
||||
if (promise) return promise; // prevent initalizing multiple times
|
||||
|
||||
const type = dbConnectionString.includes("://") ? dbConnectionString.split(":")[0]?.replace("+srv", "") : "sqlite";
|
||||
const isSqlite = type.includes("sqlite");
|
||||
|
||||
console.log(`[Database] ${yellow(`connecting to ${type} db`)}`);
|
||||
if(isSqlite) {
|
||||
console.log(`[Database] ${red(`You are running sqlite! Please keep in mind that we recommend setting up a dedicated database!`)}`);
|
||||
}
|
||||
// @ts-ignore
|
||||
promise = createConnection({
|
||||
type,
|
||||
charset: 'utf8mb4',
|
||||
url: isSqlite ? undefined : dbConnectionString,
|
||||
database: isSqlite ? dbConnectionString : undefined,
|
||||
// @ts-ignore
|
||||
entities: Object.values(Models).filter((x) => x.constructor.name !== "Object" && x.name),
|
||||
synchronize: type !== "mongodb",
|
||||
logging: false,
|
||||
cache: {
|
||||
duration: 1000 * 3, // cache all find queries for 3 seconds
|
||||
},
|
||||
bigNumberStrings: false,
|
||||
supportBigNumbers: true,
|
||||
name: "default",
|
||||
migrations: [path.join(__dirname, "..", "migrations", "*.js")],
|
||||
});
|
||||
|
||||
promise.then(async (connection: Connection) => {
|
||||
dbConnection = connection;
|
||||
|
||||
// run migrations, and if it is a new fresh database, set it to the last migration
|
||||
if (connection.migrations.length) {
|
||||
if (!(await Migration.findOne({}))) {
|
||||
let i = 0;
|
||||
|
||||
await Migration.insert(
|
||||
connection.migrations.map((x) => ({
|
||||
id: i++,
|
||||
name: x.name,
|
||||
timestamp: Date.now(),
|
||||
}))
|
||||
);
|
||||
}
|
||||
}
|
||||
await connection.runMigrations();
|
||||
console.log(`[Database] ${green("connected")}`);
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
export { dbConnection };
|
||||
|
||||
export function closeDatabase() {
|
||||
dbConnection?.close();
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import crypto from "crypto";
|
||||
import { BackupCode } from "../entities/BackupCodes";
|
||||
|
||||
export function generateMfaBackupCodes(user_id: string) {
|
||||
let backup_codes: BackupCode[] = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const code = BackupCode.create({
|
||||
user: { id: user_id },
|
||||
code: crypto.randomBytes(4).toString("hex"), // 8 characters
|
||||
consumed: false,
|
||||
expired: false,
|
||||
});
|
||||
backup_codes.push(code);
|
||||
}
|
||||
|
||||
return backup_codes;
|
||||
}
|
||||
Reference in New Issue
Block a user