mirror of
https://github.com/ajnart/homarr.git
synced 2026-02-26 08:20:56 +01:00
refactor(db): move to core package (#4589)
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
// Importing env files here to validate on build
|
||||
import "@homarr/auth/env";
|
||||
import "@homarr/db/env";
|
||||
import "@homarr/core/infrastructure/db/env";
|
||||
import "@homarr/common/env";
|
||||
import "@homarr/core/infrastructure/logs/env";
|
||||
import "@homarr/docker/env";
|
||||
|
||||
@@ -5,6 +5,7 @@ import Database from "better-sqlite3";
|
||||
import { BetterSQLite3Database, drizzle } from "drizzle-orm/better-sqlite3";
|
||||
import { migrate } from "drizzle-orm/better-sqlite3/migrator";
|
||||
|
||||
import { DB_CASING } from "../../packages/core/src/infrastructure/db/constants";
|
||||
import * as sqliteSchema from "../../packages/db/schema/sqlite";
|
||||
|
||||
export const createSqliteDbFileAsync = async () => {
|
||||
@@ -16,7 +17,7 @@ export const createSqliteDbFileAsync = async () => {
|
||||
const connection = new Database(localDbUrl);
|
||||
const db = drizzle(connection, {
|
||||
schema: sqliteSchema,
|
||||
casing: "snake_case",
|
||||
casing: DB_CASING,
|
||||
});
|
||||
|
||||
await migrate(db, {
|
||||
|
||||
@@ -10,7 +10,10 @@
|
||||
"./infrastructure/logs": "./src/infrastructure/logs/index.ts",
|
||||
"./infrastructure/logs/constants": "./src/infrastructure/logs/constants.ts",
|
||||
"./infrastructure/logs/env": "./src/infrastructure/logs/env.ts",
|
||||
"./infrastructure/logs/error": "./src/infrastructure/logs/error.ts"
|
||||
"./infrastructure/logs/error": "./src/infrastructure/logs/error.ts",
|
||||
"./infrastructure/db": "./src/infrastructure/db/index.ts",
|
||||
"./infrastructure/db/env": "./src/infrastructure/db/env.ts",
|
||||
"./infrastructure/db/constants": "./src/infrastructure/db/constants.ts"
|
||||
},
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
@@ -28,7 +31,11 @@
|
||||
"prettier": "@homarr/prettier-config",
|
||||
"dependencies": {
|
||||
"@t3-oss/env-nextjs": "^0.13.8",
|
||||
"better-sqlite3": "^12.5.0",
|
||||
"drizzle-orm": "^0.45.1",
|
||||
"ioredis": "5.8.2",
|
||||
"mysql2": "3.15.3",
|
||||
"pg": "^8.16.3",
|
||||
"superjson": "2.2.6",
|
||||
"winston": "3.19.0",
|
||||
"zod": "^4.1.13"
|
||||
@@ -37,6 +44,8 @@
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/better-sqlite3": "7.6.13",
|
||||
"@types/pg": "^8.16.0",
|
||||
"eslint": "^9.39.1",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
|
||||
3
packages/core/src/infrastructure/db/constants.ts
Normal file
3
packages/core/src/infrastructure/db/constants.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import type { Casing } from "drizzle-orm";
|
||||
|
||||
export const DB_CASING: Casing = "snake_case";
|
||||
27
packages/core/src/infrastructure/db/drivers/index.ts
Normal file
27
packages/core/src/infrastructure/db/drivers/index.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { DB_CASING } from "../constants";
|
||||
import { createDbMapping } from "../mapping";
|
||||
import { createMysqlDb } from "./mysql";
|
||||
import { createPostgresDb } from "./postgresql";
|
||||
import type { SharedDrizzleConfig } from "./shared";
|
||||
import { WinstonDrizzleLogger } from "./shared";
|
||||
import { createSqliteDb } from "./sqlite";
|
||||
|
||||
export type Database<TSchema extends Record<string, unknown>> = ReturnType<typeof createSqliteDb<TSchema>>;
|
||||
|
||||
export const createSharedConfig = <TSchema extends Record<string, unknown>>(
|
||||
schema: TSchema,
|
||||
): SharedDrizzleConfig<TSchema> => ({
|
||||
logger: new WinstonDrizzleLogger(),
|
||||
casing: DB_CASING,
|
||||
schema,
|
||||
});
|
||||
|
||||
export const createDb = <TSchema extends Record<string, unknown>>(schema: TSchema) => {
|
||||
const config = createSharedConfig(schema);
|
||||
|
||||
return createDbMapping({
|
||||
mysql2: () => createMysqlDb(config),
|
||||
"node-postgres": () => createPostgresDb(config),
|
||||
"better-sqlite3": () => createSqliteDb(config),
|
||||
});
|
||||
};
|
||||
33
packages/core/src/infrastructure/db/drivers/mysql.ts
Normal file
33
packages/core/src/infrastructure/db/drivers/mysql.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { drizzle } from "drizzle-orm/mysql2";
|
||||
import mysql from "mysql2";
|
||||
import type { PoolOptions } from "mysql2";
|
||||
|
||||
import { dbEnv } from "../env";
|
||||
import type { SharedDrizzleConfig } from "./shared";
|
||||
|
||||
export const createMysqlDb = <TSchema extends Record<string, unknown>>(config: SharedDrizzleConfig<TSchema>) => {
|
||||
const connection = createMysqlDbConnection();
|
||||
return drizzle<TSchema>(connection, {
|
||||
...config,
|
||||
mode: "default",
|
||||
});
|
||||
};
|
||||
|
||||
const createMysqlDbConnection = () => {
|
||||
const defaultOptions = {
|
||||
maxIdle: 0,
|
||||
idleTimeout: 60000,
|
||||
enableKeepAlive: true,
|
||||
} satisfies Partial<PoolOptions>;
|
||||
|
||||
if (!dbEnv.HOST) {
|
||||
return mysql.createPool({ ...defaultOptions, uri: dbEnv.URL });
|
||||
}
|
||||
|
||||
return mysql.createPool({
|
||||
...defaultOptions,
|
||||
port: dbEnv.PORT,
|
||||
user: dbEnv.USER,
|
||||
password: dbEnv.PASSWORD,
|
||||
});
|
||||
};
|
||||
38
packages/core/src/infrastructure/db/drivers/postgresql.ts
Normal file
38
packages/core/src/infrastructure/db/drivers/postgresql.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { drizzle as drizzlePostgres } from "drizzle-orm/node-postgres";
|
||||
import type { PoolOptions as PostgresPoolOptions } from "pg";
|
||||
import { Pool as PostgresPool } from "pg";
|
||||
|
||||
import { dbEnv } from "../env";
|
||||
import type { SharedDrizzleConfig } from "./shared";
|
||||
|
||||
export const createPostgresDb = <TSchema extends Record<string, unknown>>(config: SharedDrizzleConfig<TSchema>) => {
|
||||
const connection = createPostgresDbConnection();
|
||||
return drizzlePostgres({
|
||||
...config,
|
||||
client: connection,
|
||||
});
|
||||
};
|
||||
|
||||
const createPostgresDbConnection = () => {
|
||||
const defaultOptions = {
|
||||
max: 0,
|
||||
idleTimeoutMillis: 60000,
|
||||
allowExitOnIdle: false,
|
||||
} satisfies Partial<PostgresPoolOptions>;
|
||||
|
||||
if (!dbEnv.HOST) {
|
||||
return new PostgresPool({
|
||||
...defaultOptions,
|
||||
connectionString: dbEnv.URL,
|
||||
});
|
||||
}
|
||||
|
||||
return new PostgresPool({
|
||||
...defaultOptions,
|
||||
host: dbEnv.HOST,
|
||||
port: dbEnv.PORT,
|
||||
database: dbEnv.NAME,
|
||||
user: dbEnv.USER,
|
||||
password: dbEnv.PASSWORD,
|
||||
});
|
||||
};
|
||||
15
packages/core/src/infrastructure/db/drivers/shared.ts
Normal file
15
packages/core/src/infrastructure/db/drivers/shared.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import type { DrizzleConfig, Logger } from "drizzle-orm";
|
||||
|
||||
import { createLogger } from "../../logs";
|
||||
|
||||
export type SharedDrizzleConfig<TSchema extends Record<string, unknown>> = Required<
|
||||
Pick<DrizzleConfig<TSchema>, "logger" | "casing" | "schema">
|
||||
>;
|
||||
|
||||
const logger = createLogger({ module: "db" });
|
||||
|
||||
export class WinstonDrizzleLogger implements Logger {
|
||||
logQuery(query: string, _: unknown[]): void {
|
||||
logger.debug("Executed SQL query", { query });
|
||||
}
|
||||
}
|
||||
10
packages/core/src/infrastructure/db/drivers/sqlite.ts
Normal file
10
packages/core/src/infrastructure/db/drivers/sqlite.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import Database from "better-sqlite3";
|
||||
import { drizzle as drizzleSqlite } from "drizzle-orm/better-sqlite3";
|
||||
|
||||
import { dbEnv } from "../env";
|
||||
import type { SharedDrizzleConfig } from "./shared";
|
||||
|
||||
export const createSqliteDb = <TSchema extends Record<string, unknown>>(config: SharedDrizzleConfig<TSchema>) => {
|
||||
const connection = new Database(dbEnv.URL);
|
||||
return drizzleSqlite<TSchema>(connection, config);
|
||||
};
|
||||
@@ -1,7 +1,6 @@
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { env as commonEnv } from "@homarr/common/env";
|
||||
import { createEnv } from "@homarr/core/infrastructure/env";
|
||||
import { createEnv, runtimeEnvWithPrefix } from "@homarr/core/infrastructure/env";
|
||||
|
||||
const drivers = {
|
||||
betterSqlite3: "better-sqlite3",
|
||||
@@ -15,40 +14,40 @@ const onlyAllowUrl = isDriver(drivers.betterSqlite3);
|
||||
const urlRequired = onlyAllowUrl || !isUsingDbHost;
|
||||
const hostRequired = isUsingDbHost && !onlyAllowUrl;
|
||||
|
||||
export const env = createEnv({
|
||||
export const dbEnv = createEnv({
|
||||
/**
|
||||
* Specify your server-side environment variables schema here. This way you can ensure the app isn't
|
||||
* built with invalid env vars.
|
||||
*/
|
||||
server: {
|
||||
DB_DRIVER: z
|
||||
DRIVER: z
|
||||
.union([z.literal(drivers.betterSqlite3), z.literal(drivers.mysql2), z.literal(drivers.nodePostgres)], {
|
||||
message: `Invalid database driver, supported are ${Object.keys(drivers).join(", ")}`,
|
||||
})
|
||||
.default(drivers.betterSqlite3),
|
||||
...(urlRequired
|
||||
? {
|
||||
DB_URL:
|
||||
URL:
|
||||
// Fallback to the default sqlite file path in production
|
||||
commonEnv.NODE_ENV === "production" && isDriver("better-sqlite3")
|
||||
process.env.NODE_ENV === "production" && isDriver("better-sqlite3")
|
||||
? z.string().default("/appdata/db/db.sqlite")
|
||||
: z.string().nonempty(),
|
||||
}
|
||||
: {}),
|
||||
...(hostRequired
|
||||
? {
|
||||
DB_HOST: z.string(),
|
||||
DB_PORT: z
|
||||
HOST: z.string(),
|
||||
PORT: z
|
||||
.string()
|
||||
.regex(/\d+/)
|
||||
.transform(Number)
|
||||
.refine((number) => number >= 1)
|
||||
.default(isDriver(drivers.mysql2) ? 3306 : 5432),
|
||||
DB_USER: z.string(),
|
||||
DB_PASSWORD: z.string(),
|
||||
DB_NAME: z.string(),
|
||||
USER: z.string(),
|
||||
PASSWORD: z.string(),
|
||||
NAME: z.string(),
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
experimental__runtimeEnv: process.env,
|
||||
runtimeEnv: runtimeEnvWithPrefix("DB_"),
|
||||
});
|
||||
9
packages/core/src/infrastructure/db/index.ts
Normal file
9
packages/core/src/infrastructure/db/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { createDbMapping } from "./mapping";
|
||||
|
||||
export { createDb } from "./drivers";
|
||||
export const createSchema = createDbMapping;
|
||||
|
||||
export { createMysqlDb } from "./drivers/mysql";
|
||||
export { createSqliteDb } from "./drivers/sqlite";
|
||||
export { createPostgresDb } from "./drivers/postgresql";
|
||||
export { createSharedConfig as createSharedDbConfig } from "./drivers";
|
||||
9
packages/core/src/infrastructure/db/mapping.ts
Normal file
9
packages/core/src/infrastructure/db/mapping.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { dbEnv } from "./env";
|
||||
|
||||
type DbMappingInput = Record<typeof dbEnv.DRIVER, () => unknown>;
|
||||
|
||||
export const createDbMapping = <TInput extends DbMappingInput>(input: TInput) => {
|
||||
// The DRIVER can be undefined when validation of env vars is skipped
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
return input[dbEnv.DRIVER ?? "better-sqlite3"]() as ReturnType<TInput["better-sqlite3"]>;
|
||||
};
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { InferInsertModel } from "drizzle-orm";
|
||||
|
||||
import { objectEntries } from "@homarr/common";
|
||||
import { dbEnv } from "@homarr/core/infrastructure/db/env";
|
||||
|
||||
import type { HomarrDatabase, HomarrDatabaseMysql, HomarrDatabasePostgresql } from "./driver";
|
||||
import { env } from "./env";
|
||||
import * as schema from "./schema";
|
||||
|
||||
type TableKey = {
|
||||
@@ -11,11 +11,11 @@ type TableKey = {
|
||||
}[keyof typeof schema];
|
||||
|
||||
export function isMysql(): boolean {
|
||||
return env.DB_DRIVER === "mysql2";
|
||||
return dbEnv.DRIVER === "mysql2";
|
||||
}
|
||||
|
||||
export function isPostgresql(): boolean {
|
||||
return env.DB_DRIVER === "node-postgres";
|
||||
return dbEnv.DRIVER === "node-postgres";
|
||||
}
|
||||
|
||||
export const createDbInsertCollectionForTransaction = <TTableKey extends TableKey>(
|
||||
@@ -66,7 +66,7 @@ export const createDbInsertCollectionWithoutTransaction = <TTableKey extends Tab
|
||||
return {
|
||||
...collection,
|
||||
insertAllAsync: async (db: HomarrDatabase) => {
|
||||
switch (env.DB_DRIVER) {
|
||||
switch (dbEnv.DRIVER) {
|
||||
case "mysql2":
|
||||
case "node-postgres":
|
||||
// For mysql2 and node-postgres, we can use the async insertAllAsync method
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import type { Config } from "drizzle-kit";
|
||||
|
||||
import { env } from "../env";
|
||||
import { DB_CASING } from "@homarr/core/infrastructure/db/constants";
|
||||
import { dbEnv } from "@homarr/core/infrastructure/db/env";
|
||||
|
||||
export default {
|
||||
dialect: "mysql",
|
||||
schema: "./schema",
|
||||
casing: "snake_case",
|
||||
dbCredentials: env.DB_URL
|
||||
? { url: env.DB_URL }
|
||||
casing: DB_CASING,
|
||||
dbCredentials: dbEnv.URL
|
||||
? { url: dbEnv.URL }
|
||||
: {
|
||||
host: env.DB_HOST,
|
||||
user: env.DB_USER,
|
||||
password: env.DB_PASSWORD,
|
||||
database: env.DB_NAME,
|
||||
port: env.DB_PORT,
|
||||
host: dbEnv.HOST,
|
||||
port: dbEnv.PORT,
|
||||
database: dbEnv.NAME,
|
||||
user: dbEnv.USER,
|
||||
password: dbEnv.PASSWORD,
|
||||
},
|
||||
out: "./migrations/mysql",
|
||||
} satisfies Config;
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
import type { Config } from "drizzle-kit";
|
||||
|
||||
import { env } from "../env";
|
||||
import { DB_CASING } from "@homarr/core/infrastructure/db/constants";
|
||||
import { dbEnv } from "@homarr/core/infrastructure/db/env";
|
||||
|
||||
export default {
|
||||
dialect: "postgresql",
|
||||
schema: "./schema",
|
||||
casing: "snake_case",
|
||||
casing: DB_CASING,
|
||||
|
||||
dbCredentials: env.DB_URL
|
||||
? { url: env.DB_URL }
|
||||
dbCredentials: dbEnv.URL
|
||||
? { url: dbEnv.URL }
|
||||
: {
|
||||
host: env.DB_HOST,
|
||||
port: env.DB_PORT,
|
||||
user: env.DB_USER,
|
||||
password: env.DB_PASSWORD,
|
||||
database: env.DB_NAME,
|
||||
host: dbEnv.HOST,
|
||||
port: dbEnv.PORT,
|
||||
database: dbEnv.NAME,
|
||||
user: dbEnv.USER,
|
||||
password: dbEnv.PASSWORD,
|
||||
},
|
||||
out: "./migrations/postgresql",
|
||||
} satisfies Config;
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import type { Config } from "drizzle-kit";
|
||||
|
||||
import { env } from "../env";
|
||||
import { DB_CASING } from "@homarr/core/infrastructure/db/constants";
|
||||
import { dbEnv } from "@homarr/core/infrastructure/db/env";
|
||||
|
||||
export default {
|
||||
dialect: "sqlite",
|
||||
schema: "./schema",
|
||||
casing: "snake_case",
|
||||
dbCredentials: { url: env.DB_URL },
|
||||
casing: DB_CASING,
|
||||
dbCredentials: { url: dbEnv.URL },
|
||||
out: "./migrations/sqlite",
|
||||
} satisfies Config;
|
||||
|
||||
@@ -1,115 +1,11 @@
|
||||
import type { Database as BetterSqlite3Connection } from "better-sqlite3";
|
||||
import Database from "better-sqlite3";
|
||||
import type { Logger } from "drizzle-orm";
|
||||
import type { BetterSQLite3Database } from "drizzle-orm/better-sqlite3";
|
||||
import { drizzle as drizzleSqlite } from "drizzle-orm/better-sqlite3";
|
||||
import type { MySql2Database } from "drizzle-orm/mysql2";
|
||||
import { drizzle as drizzleMysql } from "drizzle-orm/mysql2";
|
||||
import type { NodePgDatabase } from "drizzle-orm/node-postgres";
|
||||
import { drizzle as drizzlePg } from "drizzle-orm/node-postgres";
|
||||
import type { Pool as MysqlConnectionPool } from "mysql2";
|
||||
import mysql from "mysql2";
|
||||
import { Pool as PostgresPool } from "pg";
|
||||
|
||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
||||
|
||||
import { env } from "./env";
|
||||
import * as mysqlSchema from "./schema/mysql";
|
||||
import * as pgSchema from "./schema/postgresql";
|
||||
import * as sqliteSchema from "./schema/sqlite";
|
||||
|
||||
const logger = createLogger({ module: "db" });
|
||||
import type * as mysqlSchema from "./schema/mysql";
|
||||
import type * as pgSchema from "./schema/postgresql";
|
||||
import type * as sqliteSchema from "./schema/sqlite";
|
||||
|
||||
export type HomarrDatabase = BetterSQLite3Database<typeof sqliteSchema>;
|
||||
export type HomarrDatabaseMysql = MySql2Database<typeof mysqlSchema>;
|
||||
export type HomarrDatabasePostgresql = NodePgDatabase<typeof pgSchema>;
|
||||
|
||||
const init = () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!connection) {
|
||||
switch (env.DB_DRIVER) {
|
||||
case "mysql2":
|
||||
initMySQL2();
|
||||
break;
|
||||
case "node-postgres":
|
||||
initNodePostgres();
|
||||
break;
|
||||
default:
|
||||
initBetterSqlite();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export let connection: BetterSqlite3Connection | MysqlConnectionPool | PostgresPool;
|
||||
export let database: HomarrDatabase;
|
||||
|
||||
class WinstonDrizzleLogger implements Logger {
|
||||
logQuery(query: string, _: unknown[]): void {
|
||||
logger.debug("Executed SQL query", { query });
|
||||
}
|
||||
}
|
||||
|
||||
const initBetterSqlite = () => {
|
||||
connection = new Database(env.DB_URL);
|
||||
database = drizzleSqlite(connection, {
|
||||
schema: sqliteSchema,
|
||||
logger: new WinstonDrizzleLogger(),
|
||||
casing: "snake_case",
|
||||
}) as unknown as never;
|
||||
};
|
||||
|
||||
const initMySQL2 = () => {
|
||||
if (!env.DB_HOST) {
|
||||
connection = mysql.createPool({ uri: env.DB_URL, maxIdle: 0, idleTimeout: 60000, enableKeepAlive: true });
|
||||
} else {
|
||||
connection = mysql.createPool({
|
||||
host: env.DB_HOST,
|
||||
database: env.DB_NAME,
|
||||
port: env.DB_PORT,
|
||||
user: env.DB_USER,
|
||||
password: env.DB_PASSWORD,
|
||||
maxIdle: 0,
|
||||
idleTimeout: 60000,
|
||||
enableKeepAlive: true,
|
||||
});
|
||||
}
|
||||
|
||||
database = drizzleMysql(connection, {
|
||||
schema: mysqlSchema,
|
||||
mode: "default",
|
||||
logger: new WinstonDrizzleLogger(),
|
||||
casing: "snake_case",
|
||||
}) as unknown as HomarrDatabase;
|
||||
};
|
||||
|
||||
const initNodePostgres = () => {
|
||||
if (!env.DB_HOST) {
|
||||
connection = new PostgresPool({
|
||||
connectionString: env.DB_URL,
|
||||
max: 0,
|
||||
idleTimeoutMillis: 60000,
|
||||
allowExitOnIdle: false,
|
||||
});
|
||||
} else {
|
||||
connection = new PostgresPool({
|
||||
host: env.DB_HOST,
|
||||
database: env.DB_NAME,
|
||||
port: env.DB_PORT,
|
||||
user: env.DB_USER,
|
||||
password: env.DB_PASSWORD,
|
||||
max: 0,
|
||||
idleTimeoutMillis: 60000,
|
||||
allowExitOnIdle: false,
|
||||
});
|
||||
}
|
||||
|
||||
database = drizzlePg({
|
||||
logger: new WinstonDrizzleLogger(),
|
||||
schema: pgSchema,
|
||||
casing: "snake_case",
|
||||
client: connection,
|
||||
}) as unknown as HomarrDatabase;
|
||||
};
|
||||
|
||||
init();
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import Database from "better-sqlite3";
|
||||
import { createDb } from "@homarr/core/infrastructure/db";
|
||||
|
||||
import { database } from "./driver";
|
||||
import { schema } from "./schema";
|
||||
|
||||
export * from "drizzle-orm";
|
||||
|
||||
export const db = database;
|
||||
|
||||
export type Database = typeof db;
|
||||
export type { HomarrDatabaseMysql, HomarrDatabasePostgresql } from "./driver";
|
||||
|
||||
export const db = createDb(schema);
|
||||
|
||||
export type Database = typeof db;
|
||||
|
||||
export { handleDiffrentDbDriverOperationsAsync as handleTransactionsAsync } from "./transactions";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { applyCustomMigrationsAsync } from ".";
|
||||
import { database } from "../../driver";
|
||||
import { db } from "../..";
|
||||
|
||||
applyCustomMigrationsAsync(database)
|
||||
applyCustomMigrationsAsync(db)
|
||||
.then(() => {
|
||||
console.log("Custom migrations applied successfully");
|
||||
process.exit(0);
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { drizzle } from "drizzle-orm/mysql2";
|
||||
import { migrate } from "drizzle-orm/mysql2/migrator";
|
||||
import mysql from "mysql2";
|
||||
|
||||
import { createMysqlDb, createSharedDbConfig } from "@homarr/core/infrastructure/db";
|
||||
|
||||
import type { Database } from "../..";
|
||||
import { env } from "../../env";
|
||||
import * as mysqlSchema from "../../schema/mysql";
|
||||
import { applyCustomMigrationsAsync } from "../custom";
|
||||
import { seedDataAsync } from "../seed";
|
||||
@@ -11,23 +10,8 @@ import { seedDataAsync } from "../seed";
|
||||
const migrationsFolder = process.argv[2] ?? ".";
|
||||
|
||||
const migrateAsync = async () => {
|
||||
const mysql2 = mysql.createConnection(
|
||||
env.DB_URL
|
||||
? { uri: env.DB_URL }
|
||||
: {
|
||||
host: env.DB_HOST,
|
||||
database: env.DB_NAME,
|
||||
port: env.DB_PORT,
|
||||
user: env.DB_USER,
|
||||
password: env.DB_PASSWORD,
|
||||
},
|
||||
);
|
||||
|
||||
const db = drizzle(mysql2, {
|
||||
mode: "default",
|
||||
schema: mysqlSchema,
|
||||
casing: "snake_case",
|
||||
});
|
||||
const config = createSharedDbConfig(mysqlSchema);
|
||||
const db = createMysqlDb(config);
|
||||
|
||||
await migrate(db, { migrationsFolder });
|
||||
await seedDataAsync(db as unknown as Database);
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { drizzle } from "drizzle-orm/node-postgres";
|
||||
import { migrate } from "drizzle-orm/node-postgres/migrator";
|
||||
import { Pool } from "pg";
|
||||
|
||||
import { createPostgresDb, createSharedDbConfig } from "@homarr/core/infrastructure/db";
|
||||
|
||||
import type { Database } from "../..";
|
||||
import { env } from "../../env";
|
||||
import * as pgSchema from "../../schema/postgresql";
|
||||
import { applyCustomMigrationsAsync } from "../custom";
|
||||
import { seedDataAsync } from "../seed";
|
||||
@@ -11,23 +10,8 @@ import { seedDataAsync } from "../seed";
|
||||
const migrationsFolder = process.argv[2] ?? ".";
|
||||
|
||||
const migrateAsync = async () => {
|
||||
const pool = new Pool(
|
||||
env.DB_URL
|
||||
? { connectionString: env.DB_URL }
|
||||
: {
|
||||
host: env.DB_HOST,
|
||||
database: env.DB_NAME,
|
||||
port: env.DB_PORT,
|
||||
user: env.DB_USER,
|
||||
password: env.DB_PASSWORD,
|
||||
},
|
||||
);
|
||||
|
||||
const db = drizzle({
|
||||
schema: pgSchema,
|
||||
casing: "snake_case",
|
||||
client: pool,
|
||||
});
|
||||
const config = createSharedDbConfig(pgSchema);
|
||||
const db = createPostgresDb(config);
|
||||
|
||||
await migrate(db, { migrationsFolder });
|
||||
await seedDataAsync(db as unknown as Database);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { database } from "../driver";
|
||||
import { db } from "..";
|
||||
import { seedDataAsync } from "./seed";
|
||||
|
||||
seedDataAsync(database)
|
||||
seedDataAsync(db)
|
||||
.then(() => {
|
||||
console.log("Seed complete");
|
||||
process.exit(0);
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import Database from "better-sqlite3";
|
||||
import { drizzle } from "drizzle-orm/better-sqlite3";
|
||||
import { migrate } from "drizzle-orm/better-sqlite3/migrator";
|
||||
|
||||
import { env } from "../../env";
|
||||
import { createSharedDbConfig, createSqliteDb } from "@homarr/core/infrastructure/db";
|
||||
|
||||
import * as sqliteSchema from "../../schema/sqlite";
|
||||
import { applyCustomMigrationsAsync } from "../custom";
|
||||
import { seedDataAsync } from "../seed";
|
||||
@@ -10,9 +9,8 @@ import { seedDataAsync } from "../seed";
|
||||
const migrationsFolder = process.argv[2] ?? ".";
|
||||
|
||||
const migrateAsync = async () => {
|
||||
const sqlite = new Database(env.DB_URL.replace("file:", ""));
|
||||
|
||||
const db = drizzle(sqlite, { schema: sqliteSchema, casing: "snake_case" });
|
||||
const config = createSharedDbConfig(sqliteSchema);
|
||||
const db = createSqliteDb(config);
|
||||
|
||||
migrate(db, { migrationsFolder });
|
||||
|
||||
|
||||
@@ -10,8 +10,7 @@
|
||||
"./schema": "./schema/index.ts",
|
||||
"./test": "./test/index.ts",
|
||||
"./queries": "./queries/index.ts",
|
||||
"./validationSchemas": "./validationSchemas.ts",
|
||||
"./env": "./env.ts"
|
||||
"./validationSchemas": "./validationSchemas.ts"
|
||||
},
|
||||
"main": "./index.ts",
|
||||
"types": "./index.ts",
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
import type { InferSelectModel } from "drizzle-orm";
|
||||
|
||||
import { env } from "../env";
|
||||
import { createSchema } from "@homarr/core/infrastructure/db";
|
||||
|
||||
import * as mysqlSchema from "./mysql";
|
||||
import * as pgSchema from "./postgresql";
|
||||
import * as sqliteSchema from "./sqlite";
|
||||
|
||||
export type PostgreSqlSchema = typeof pgSchema;
|
||||
export type MySqlSchema = typeof mysqlSchema;
|
||||
type Schema = typeof sqliteSchema;
|
||||
|
||||
const schema =
|
||||
env.DB_DRIVER === "mysql2"
|
||||
? (mysqlSchema as unknown as Schema)
|
||||
: env.DB_DRIVER === "node-postgres"
|
||||
? (pgSchema as unknown as Schema)
|
||||
: sqliteSchema;
|
||||
export const schema = createSchema({
|
||||
"better-sqlite3": () => sqliteSchema,
|
||||
mysql2: () => mysqlSchema,
|
||||
"node-postgres": () => pgSchema,
|
||||
});
|
||||
|
||||
// Sadly we can't use export * from here as we have multiple possible exports
|
||||
export const {
|
||||
|
||||
@@ -2,11 +2,13 @@ import Database from "better-sqlite3";
|
||||
import { drizzle } from "drizzle-orm/better-sqlite3";
|
||||
import { migrate } from "drizzle-orm/better-sqlite3/migrator";
|
||||
|
||||
import { DB_CASING } from "@homarr/core/infrastructure/db/constants";
|
||||
|
||||
import * as sqliteSchema from "../schema/sqlite";
|
||||
|
||||
export const createDb = (debug?: boolean) => {
|
||||
const sqlite = new Database(":memory:");
|
||||
const db = drizzle(sqlite, { schema: sqliteSchema, logger: debug, casing: "snake_case" });
|
||||
const db = drizzle(sqlite, { schema: sqliteSchema, logger: debug, casing: DB_CASING });
|
||||
migrate(db, {
|
||||
migrationsFolder: "./packages/db/migrations/sqlite",
|
||||
});
|
||||
|
||||
@@ -5,6 +5,8 @@ import { migrate } from "drizzle-orm/mysql2/migrator";
|
||||
import mysql from "mysql2";
|
||||
import { describe, test } from "vitest";
|
||||
|
||||
import { DB_CASING } from "@homarr/core/infrastructure/db/constants";
|
||||
|
||||
import * as mysqlSchema from "../schema/mysql";
|
||||
|
||||
describe("Mysql Migration", () => {
|
||||
@@ -22,7 +24,7 @@ describe("Mysql Migration", () => {
|
||||
const database = drizzle(connection, {
|
||||
schema: mysqlSchema,
|
||||
mode: "default",
|
||||
casing: "snake_case",
|
||||
casing: DB_CASING,
|
||||
});
|
||||
|
||||
// Run migrations and check if it works
|
||||
|
||||
@@ -5,6 +5,8 @@ import { migrate } from "drizzle-orm/node-postgres/migrator";
|
||||
import { Pool } from "pg";
|
||||
import { describe, test } from "vitest";
|
||||
|
||||
import { DB_CASING } from "@homarr/core/infrastructure/db/constants";
|
||||
|
||||
import * as pgSchema from "../schema/postgresql";
|
||||
|
||||
describe("PostgreSql Migration", () => {
|
||||
@@ -26,7 +28,7 @@ describe("PostgreSql Migration", () => {
|
||||
|
||||
const database = drizzle({
|
||||
schema: pgSchema,
|
||||
casing: "snake_case",
|
||||
casing: DB_CASING,
|
||||
client: pool,
|
||||
});
|
||||
|
||||
|
||||
18
pnpm-lock.yaml
generated
18
pnpm-lock.yaml
generated
@@ -920,9 +920,21 @@ importers:
|
||||
'@t3-oss/env-nextjs':
|
||||
specifier: ^0.13.8
|
||||
version: 0.13.8(arktype@2.1.20)(typescript@5.9.3)(zod@4.1.13)
|
||||
better-sqlite3:
|
||||
specifier: ^12.5.0
|
||||
version: 12.5.0
|
||||
drizzle-orm:
|
||||
specifier: ^0.45.1
|
||||
version: 0.45.1(@libsql/client-wasm@0.14.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(better-sqlite3@12.5.0)(gel@2.0.0)(mysql2@3.15.3)(pg@8.16.3)
|
||||
ioredis:
|
||||
specifier: 5.8.2
|
||||
version: 5.8.2
|
||||
mysql2:
|
||||
specifier: 3.15.3
|
||||
version: 3.15.3
|
||||
pg:
|
||||
specifier: ^8.16.3
|
||||
version: 8.16.3
|
||||
superjson:
|
||||
specifier: 2.2.6
|
||||
version: 2.2.6
|
||||
@@ -942,6 +954,12 @@ importers:
|
||||
'@homarr/tsconfig':
|
||||
specifier: workspace:^0.1.0
|
||||
version: link:../../tooling/typescript
|
||||
'@types/better-sqlite3':
|
||||
specifier: 7.6.13
|
||||
version: 7.6.13
|
||||
'@types/pg':
|
||||
specifier: ^8.16.0
|
||||
version: 8.16.0
|
||||
eslint:
|
||||
specifier: ^9.39.1
|
||||
version: 9.39.1
|
||||
|
||||
@@ -10,7 +10,8 @@ if [ "$DB_MIGRATIONS_DISABLED" = "true" ]; then
|
||||
echo "DB migrations are disabled, skipping"
|
||||
else
|
||||
echo "Running DB migrations"
|
||||
node ./db/migrations/$DB_DIALECT/migrate.cjs ./db/migrations/$DB_DIALECT
|
||||
# We disable redis logs during migration as the redis client is not yet started
|
||||
DISABLE_REDIS_LOGS=true node ./db/migrations/$DB_DIALECT/migrate.cjs ./db/migrations/$DB_DIALECT
|
||||
fi
|
||||
|
||||
# Auth secret is generated every time the container starts as it is required, but not used because we don't need JWTs or Mail hashing
|
||||
|
||||
Reference in New Issue
Block a user