chore: Expose createDatabaseInstance, createMigrationRunner methods

This commit is contained in:
Tom Moor
2023-11-25 13:18:51 -05:00
parent b903be6804
commit a21e1d9fea
6 changed files with 96 additions and 69 deletions

View File

@@ -65,7 +65,7 @@ export default class DocumentPublishedOrUpdatedEmail extends BaseEmail<
const revision = await Revision.findByPk(revisionId);
if (revision) {
const before = await revision.previous();
const before = await revision.before();
const content = await DocumentHelper.toEmailDiff(before, revision, {
includeTitle: false,
centered: false,

View File

@@ -50,7 +50,7 @@ if (serviceNames.includes("collaboration")) {
// This function will only be called once in the original process
async function master() {
await checkConnection();
await checkConnection(sequelize);
await checkEnv();
await checkPendingMigrations();

View File

@@ -127,7 +127,12 @@ class Revision extends IdModel {
// instance methods
previous(): Promise<Revision | null> {
/**
* Find the revision for the document before this one.
*
* @returns A Promise that resolves to a Revision, or null if this is the first revision.
*/
before(): Promise<Revision | null> {
return (this.constructor as typeof Revision).findOne({
where: {
documentId: this.documentId,

View File

@@ -25,7 +25,7 @@ export default class RevisionCreatedNotificationsTask extends BaseTask<RevisionE
await createSubscriptionsForDocument(document, event);
// Send notifications to mentioned users first
const before = await revision.previous();
const before = await revision.before();
const oldMentions = before ? DocumentHelper.parseMentions(before) : [];
const newMentions = DocumentHelper.parseMentions(document);
const mentions = differenceBy(newMentions, oldMentions, "id");

View File

@@ -34,7 +34,7 @@ router.post(
});
authorize(user, "read", document);
after = revision;
before = await revision.previous();
before = await revision.before();
} else if (documentId) {
const document = await Document.findByPk(documentId, {
userId: user.id,
@@ -94,7 +94,7 @@ router.post(
);
}
} else {
before = await revision.previous();
before = await revision.before();
}
const accept = ctx.request.headers["accept"];

View File

@@ -1,5 +1,5 @@
import path from "path";
import { Sequelize } from "sequelize-typescript";
import { Model, Sequelize } from "sequelize-typescript";
import { Umzug, SequelizeStorage, MigrationError } from "umzug";
import env from "@server/env";
import Logger from "../logging/Logger";
@@ -8,41 +8,43 @@ import * as models from "../models";
const isSSLDisabled = env.PGSSLMODE === "disable";
const poolMax = env.DATABASE_CONNECTION_POOL_MAX ?? 5;
const poolMin = env.DATABASE_CONNECTION_POOL_MIN ?? 0;
const url =
env.DATABASE_CONNECTION_POOL_URL ||
env.DATABASE_URL ||
"postgres://localhost:5432/outline";
const url = env.DATABASE_CONNECTION_POOL_URL || env.DATABASE_URL;
export const sequelize = new Sequelize(url, {
logging: (msg) =>
process.env.DEBUG?.includes("database") && Logger.debug("database", msg),
typeValidation: true,
logQueryParameters: env.isDevelopment,
dialectOptions: {
ssl:
env.isProduction && !isSSLDisabled
? {
// Ref.: https://github.com/brianc/node-postgres/issues/2009
rejectUnauthorized: false,
}
: false,
},
models: Object.values(models),
pool: {
max: poolMax,
min: poolMin,
acquire: 30000,
idle: 10000,
},
});
export function createDatabaseInstance(
url: string,
models: { [key: string]: typeof Model }
) {
return new Sequelize(url, {
logging: (msg) =>
process.env.DEBUG?.includes("database") && Logger.debug("database", msg),
typeValidation: true,
logQueryParameters: env.isDevelopment,
dialectOptions: {
ssl:
env.isProduction && !isSSLDisabled
? {
// Ref.: https://github.com/brianc/node-postgres/issues/2009
rejectUnauthorized: false,
}
: false,
},
models: Object.values(models) as any,
pool: {
max: poolMax,
min: poolMin,
acquire: 30000,
idle: 10000,
},
});
}
/**
* This function is used to test the database connection on startup. It will
* throw a descriptive error if the connection fails.
*/
export const checkConnection = async () => {
export const checkConnection = async (db: Sequelize) => {
try {
await sequelize.authenticate();
await db.authenticate();
} catch (error) {
if (error.message.includes("does not support SSL")) {
Logger.fatal(
@@ -55,38 +57,58 @@ export const checkConnection = async () => {
}
};
export const migrations = new Umzug({
migrations: {
glob: ["migrations/*.js", { cwd: path.resolve("server") }],
resolve: ({ name, path, context }) => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const migration = require(path as string);
return {
name,
up: async () => migration.up(context, Sequelize),
down: async () => migration.down(context, Sequelize),
};
export function createMigrationRunner(
db: Sequelize,
glob:
| string
| [
string,
{
cwd?: string | undefined;
ignore?: string | string[] | undefined;
}
]
) {
return new Umzug({
migrations: {
glob,
resolve: ({ name, path, context }) => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const migration = require(path as string);
return {
name,
up: async () => migration.up(context, Sequelize),
down: async () => migration.down(context, Sequelize),
};
},
},
},
context: sequelize.getQueryInterface(),
storage: new SequelizeStorage({ sequelize }),
logger: {
warn: (params) => Logger.warn("database", params),
error: (params: Record<string, unknown> & MigrationError) =>
Logger.error(params.message, params),
info: (params) =>
Logger.info(
"database",
params.event === "migrating"
? `Migrating ${params.name}`
: `Migrated ${params.name} in ${params.durationSeconds}s`
),
debug: (params) =>
Logger.debug(
"database",
params.event === "migrating"
? `Migrating ${params.name}`
: `Migrated ${params.name} in ${params.durationSeconds}s`
),
},
});
context: db.getQueryInterface(),
storage: new SequelizeStorage({ sequelize: db }),
logger: {
warn: (params) => Logger.warn("database", params),
error: (params: Record<string, unknown> & MigrationError) =>
Logger.error(params.message, params),
info: (params) =>
Logger.info(
"database",
params.event === "migrating"
? `Migrating ${params.name}`
: `Migrated ${params.name} in ${params.durationSeconds}s`
),
debug: (params) =>
Logger.debug(
"database",
params.event === "migrating"
? `Migrating ${params.name}`
: `Migrated ${params.name} in ${params.durationSeconds}s`
),
},
});
}
export const sequelize = createDatabaseInstance(url, models);
export const migrations = createMigrationRunner(sequelize, [
"migrations/*.js",
{ cwd: path.resolve("server") },
]);