fix: always require db-user and password for mysql (#1730)
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
// Importing env files here to validate on build
|
// Importing env files here to validate on build
|
||||||
import "@homarr/auth/env.mjs";
|
import "@homarr/auth/env.mjs";
|
||||||
|
import "@homarr/db/env.mjs";
|
||||||
|
|
||||||
import MillionLint from "@million/lint";
|
import MillionLint from "@million/lint";
|
||||||
import createNextIntlPlugin from "next-intl/plugin";
|
import createNextIntlPlugin from "next-intl/plugin";
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
import { createEnv } from "@t3-oss/env-nextjs";
|
import { createEnv } from "@t3-oss/env-nextjs";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
const isUsingDbUrl = Boolean(process.env.DB_URL);
|
|
||||||
const isUsingDbHost = Boolean(process.env.DB_HOST);
|
|
||||||
const isUsingDbCredentials = process.env.DB_DRIVER === "mysql2";
|
|
||||||
|
|
||||||
export const env = createEnv({
|
export const env = createEnv({
|
||||||
shared: {
|
shared: {
|
||||||
VERCEL_URL: z
|
VERCEL_URL: z
|
||||||
@@ -19,21 +15,6 @@ export const env = createEnv({
|
|||||||
* built with invalid env vars.
|
* built with invalid env vars.
|
||||||
*/
|
*/
|
||||||
server: {
|
server: {
|
||||||
DB_DRIVER: z.enum(["better-sqlite3", "mysql2"]).default("better-sqlite3"),
|
|
||||||
// If the DB_HOST is set, the DB_URL is optional
|
|
||||||
DB_URL: isUsingDbHost ? z.string().optional() : z.string(),
|
|
||||||
DB_HOST: isUsingDbUrl ? z.string().optional() : z.string(),
|
|
||||||
DB_PORT: isUsingDbUrl
|
|
||||||
? z.string().regex(/\d+/).transform(Number).optional()
|
|
||||||
: z
|
|
||||||
.string()
|
|
||||||
.regex(/\d+/)
|
|
||||||
.transform(Number)
|
|
||||||
.refine((number) => number >= 1)
|
|
||||||
.default("3306"),
|
|
||||||
DB_USER: isUsingDbCredentials ? z.string() : z.string().optional(),
|
|
||||||
DB_PASSWORD: isUsingDbCredentials ? z.string() : z.string().optional(),
|
|
||||||
DB_NAME: isUsingDbUrl ? z.string().optional() : z.string(),
|
|
||||||
// Comma separated list of docker hostnames that can be used to connect to query the docker endpoints (localhost:2375,host.docker.internal:2375, ...)
|
// Comma separated list of docker hostnames that can be used to connect to query the docker endpoints (localhost:2375,host.docker.internal:2375, ...)
|
||||||
DOCKER_HOSTNAMES: z.string().optional(),
|
DOCKER_HOSTNAMES: z.string().optional(),
|
||||||
DOCKER_PORTS: z.number().optional(),
|
DOCKER_PORTS: z.number().optional(),
|
||||||
@@ -51,13 +32,6 @@ export const env = createEnv({
|
|||||||
runtimeEnv: {
|
runtimeEnv: {
|
||||||
VERCEL_URL: process.env.VERCEL_URL,
|
VERCEL_URL: process.env.VERCEL_URL,
|
||||||
PORT: process.env.PORT,
|
PORT: process.env.PORT,
|
||||||
DB_URL: process.env.DB_URL,
|
|
||||||
DB_HOST: process.env.DB_HOST,
|
|
||||||
DB_USER: process.env.DB_USER,
|
|
||||||
DB_PASSWORD: process.env.DB_PASSWORD,
|
|
||||||
DB_NAME: process.env.DB_NAME,
|
|
||||||
DB_PORT: process.env.DB_PORT,
|
|
||||||
DB_DRIVER: process.env.DB_DRIVER,
|
|
||||||
NODE_ENV: process.env.NODE_ENV,
|
NODE_ENV: process.env.NODE_ENV,
|
||||||
DOCKER_HOSTNAMES: process.env.DOCKER_HOSTNAMES,
|
DOCKER_HOSTNAMES: process.env.DOCKER_HOSTNAMES,
|
||||||
DOCKER_PORTS: process.env.DOCKER_PORTS,
|
DOCKER_PORTS: process.env.DOCKER_PORTS,
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
||||||
import * as dotenv from "dotenv";
|
|
||||||
import type { Config } from "drizzle-kit";
|
import type { Config } from "drizzle-kit";
|
||||||
|
|
||||||
dotenv.config({ path: "../../.env" });
|
import { env } from "../env.mjs";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
dialect: "mysql",
|
dialect: "mysql",
|
||||||
schema: "./schema",
|
schema: "./schema",
|
||||||
casing: "snake_case",
|
casing: "snake_case",
|
||||||
dbCredentials: {
|
dbCredentials: env.DB_URL
|
||||||
host: process.env.DB_HOST!,
|
? { url: env.DB_URL }
|
||||||
user: process.env.DB_USER!,
|
: {
|
||||||
password: process.env.DB_PASSWORD!,
|
host: env.DB_HOST,
|
||||||
database: process.env.DB_NAME!,
|
user: env.DB_USER,
|
||||||
port: parseInt(process.env.DB_PORT!),
|
password: env.DB_PASSWORD,
|
||||||
},
|
database: env.DB_NAME,
|
||||||
|
port: env.DB_PORT,
|
||||||
|
},
|
||||||
out: "./migrations/mysql",
|
out: "./migrations/mysql",
|
||||||
} satisfies Config;
|
} satisfies Config;
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
import * as dotenv from "dotenv";
|
|
||||||
import type { Config } from "drizzle-kit";
|
import type { Config } from "drizzle-kit";
|
||||||
|
|
||||||
dotenv.config({ path: "../../.env" });
|
import { env } from "../env.mjs";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
dialect: "sqlite",
|
dialect: "sqlite",
|
||||||
schema: "./schema",
|
schema: "./schema",
|
||||||
casing: "snake_case",
|
casing: "snake_case",
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
dbCredentials: { url: env.DB_URL },
|
||||||
dbCredentials: { url: process.env.DB_URL! },
|
|
||||||
out: "./migrations/sqlite",
|
out: "./migrations/sqlite",
|
||||||
} satisfies Config;
|
} satisfies Config;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import mysql from "mysql2";
|
|||||||
|
|
||||||
import { logger } from "@homarr/log";
|
import { logger } from "@homarr/log";
|
||||||
|
|
||||||
|
import { env } from "./env.mjs";
|
||||||
import * as mysqlSchema from "./schema/mysql";
|
import * as mysqlSchema from "./schema/mysql";
|
||||||
import * as sqliteSchema from "./schema/sqlite";
|
import * as sqliteSchema from "./schema/sqlite";
|
||||||
|
|
||||||
@@ -15,7 +16,7 @@ type HomarrDatabase = BetterSQLite3Database<typeof sqliteSchema>;
|
|||||||
const init = () => {
|
const init = () => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||||
if (!connection) {
|
if (!connection) {
|
||||||
switch (process.env.DB_DRIVER) {
|
switch (env.DB_DRIVER) {
|
||||||
case "mysql2":
|
case "mysql2":
|
||||||
initMySQL2();
|
initMySQL2();
|
||||||
break;
|
break;
|
||||||
@@ -36,7 +37,7 @@ class WinstonDrizzleLogger implements Logger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const initBetterSqlite = () => {
|
const initBetterSqlite = () => {
|
||||||
connection = new Database(process.env.DB_URL);
|
connection = new Database(env.DB_URL);
|
||||||
database = drizzleSqlite(connection, {
|
database = drizzleSqlite(connection, {
|
||||||
schema: sqliteSchema,
|
schema: sqliteSchema,
|
||||||
logger: new WinstonDrizzleLogger(),
|
logger: new WinstonDrizzleLogger(),
|
||||||
@@ -45,16 +46,15 @@ const initBetterSqlite = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const initMySQL2 = () => {
|
const initMySQL2 = () => {
|
||||||
if (!process.env.DB_HOST) {
|
if (!env.DB_HOST) {
|
||||||
connection = mysql.createConnection({ uri: process.env.DB_URL });
|
connection = mysql.createConnection({ uri: env.DB_URL });
|
||||||
} else {
|
} else {
|
||||||
connection = mysql.createConnection({
|
connection = mysql.createConnection({
|
||||||
host: process.env.DB_HOST,
|
host: env.DB_HOST,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
database: env.DB_NAME,
|
||||||
database: process.env.DB_NAME!,
|
port: env.DB_PORT,
|
||||||
port: Number(process.env.DB_PORT),
|
user: env.DB_USER,
|
||||||
user: process.env.DB_USER,
|
password: env.DB_PASSWORD,
|
||||||
password: process.env.DB_PASSWORD,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
60
packages/db/env.mjs
Normal file
60
packages/db/env.mjs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { createEnv } from "@t3-oss/env-nextjs";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const drivers = {
|
||||||
|
betterSqlite3: "better-sqlite3",
|
||||||
|
mysql2: "mysql2",
|
||||||
|
};
|
||||||
|
|
||||||
|
const isDriver = (driver) => process.env.DB_DRIVER === driver;
|
||||||
|
const isUsingDbHost = Boolean(process.env.DB_HOST);
|
||||||
|
const onlyAllowUrl = isDriver(drivers.betterSqlite3);
|
||||||
|
const urlRequired = onlyAllowUrl || !isUsingDbHost;
|
||||||
|
const hostRequired = isUsingDbHost && !onlyAllowUrl;
|
||||||
|
|
||||||
|
export const env = 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
|
||||||
|
.union([z.literal(drivers.betterSqlite3), z.literal(drivers.mysql2)], {
|
||||||
|
message: `Invalid database driver, supported are ${Object.keys(drivers).join(", ")}`,
|
||||||
|
})
|
||||||
|
.default(drivers.betterSqlite3),
|
||||||
|
...(urlRequired
|
||||||
|
? {
|
||||||
|
DB_URL: z.string(),
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
...(hostRequired
|
||||||
|
? {
|
||||||
|
DB_HOST: z.string(),
|
||||||
|
DB_PORT: z
|
||||||
|
.string()
|
||||||
|
.regex(/\d+/)
|
||||||
|
.transform(Number)
|
||||||
|
.refine((number) => number >= 1)
|
||||||
|
.default("3306"),
|
||||||
|
DB_USER: z.string(),
|
||||||
|
DB_PASSWORD: z.string(),
|
||||||
|
DB_NAME: z.string(),
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Destructure all variables from `process.env` to make sure they aren't tree-shaken away.
|
||||||
|
*/
|
||||||
|
runtimeEnv: {
|
||||||
|
DB_DRIVER: process.env.DB_DRIVER,
|
||||||
|
DB_URL: process.env.DB_URL,
|
||||||
|
DB_HOST: process.env.DB_HOST,
|
||||||
|
DB_USER: process.env.DB_USER,
|
||||||
|
DB_PASSWORD: process.env.DB_PASSWORD,
|
||||||
|
DB_NAME: process.env.DB_NAME,
|
||||||
|
DB_PORT: process.env.DB_PORT,
|
||||||
|
},
|
||||||
|
skipValidation:
|
||||||
|
Boolean(process.env.CI) || Boolean(process.env.SKIP_ENV_VALIDATION) || process.env.npm_lifecycle_event === "lint",
|
||||||
|
});
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
||||||
import { drizzle } from "drizzle-orm/mysql2";
|
import { drizzle } from "drizzle-orm/mysql2";
|
||||||
import { migrate } from "drizzle-orm/mysql2/migrator";
|
import { migrate } from "drizzle-orm/mysql2/migrator";
|
||||||
import mysql from "mysql2";
|
import mysql from "mysql2";
|
||||||
|
|
||||||
import type { Database } from "../..";
|
import type { Database } from "../..";
|
||||||
|
import { env } from "../../env.mjs";
|
||||||
import * as mysqlSchema from "../../schema/mysql";
|
import * as mysqlSchema from "../../schema/mysql";
|
||||||
import { seedDataAsync } from "../seed";
|
import { seedDataAsync } from "../seed";
|
||||||
|
|
||||||
@@ -11,15 +11,15 @@ const migrationsFolder = process.argv[2] ?? ".";
|
|||||||
|
|
||||||
const migrateAsync = async () => {
|
const migrateAsync = async () => {
|
||||||
const mysql2 = mysql.createConnection(
|
const mysql2 = mysql.createConnection(
|
||||||
process.env.DB_HOST
|
env.DB_URL
|
||||||
? {
|
? { uri: env.DB_URL }
|
||||||
host: process.env.DB_HOST,
|
: {
|
||||||
database: process.env.DB_NAME!,
|
host: env.DB_HOST,
|
||||||
port: Number(process.env.DB_PORT),
|
database: env.DB_NAME,
|
||||||
user: process.env.DB_USER,
|
port: env.DB_PORT,
|
||||||
password: process.env.DB_PASSWORD,
|
user: env.DB_USER,
|
||||||
}
|
password: env.DB_PASSWORD,
|
||||||
: { uri: process.env.DB_URL },
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const db = drizzle(mysql2, {
|
const db = drizzle(mysql2, {
|
||||||
|
|||||||
@@ -2,13 +2,14 @@ import Database from "better-sqlite3";
|
|||||||
import { drizzle } from "drizzle-orm/better-sqlite3";
|
import { drizzle } from "drizzle-orm/better-sqlite3";
|
||||||
import { migrate } from "drizzle-orm/better-sqlite3/migrator";
|
import { migrate } from "drizzle-orm/better-sqlite3/migrator";
|
||||||
|
|
||||||
|
import { env } from "../../env.mjs";
|
||||||
import * as sqliteSchema from "../../schema/sqlite";
|
import * as sqliteSchema from "../../schema/sqlite";
|
||||||
import { seedDataAsync } from "../seed";
|
import { seedDataAsync } from "../seed";
|
||||||
|
|
||||||
const migrationsFolder = process.argv[2] ?? ".";
|
const migrationsFolder = process.argv[2] ?? ".";
|
||||||
|
|
||||||
const migrateAsync = async () => {
|
const migrateAsync = async () => {
|
||||||
const sqlite = new Database(process.env.DB_URL?.replace("file:", ""));
|
const sqlite = new Database(env.DB_URL.replace("file:", ""));
|
||||||
|
|
||||||
const db = drizzle(sqlite, { schema: sqliteSchema, casing: "snake_case" });
|
const db = drizzle(sqlite, { schema: sqliteSchema, casing: "snake_case" });
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
"./schema": "./schema/index.ts",
|
"./schema": "./schema/index.ts",
|
||||||
"./test": "./test/index.ts",
|
"./test": "./test/index.ts",
|
||||||
"./queries": "./queries/index.ts",
|
"./queries": "./queries/index.ts",
|
||||||
"./validationSchemas": "./validationSchemas.ts"
|
"./validationSchemas": "./validationSchemas.ts",
|
||||||
|
"./env.mjs": "./env.mjs"
|
||||||
},
|
},
|
||||||
"main": "./index.ts",
|
"main": "./index.ts",
|
||||||
"types": "./index.ts",
|
"types": "./index.ts",
|
||||||
@@ -21,16 +22,16 @@
|
|||||||
"clean": "rm -rf .turbo node_modules",
|
"clean": "rm -rf .turbo node_modules",
|
||||||
"format": "prettier --check . --ignore-path ../../.gitignore",
|
"format": "prettier --check . --ignore-path ../../.gitignore",
|
||||||
"lint": "eslint",
|
"lint": "eslint",
|
||||||
"migration:mysql:drop": "drizzle-kit drop --config ./configs/mysql.config.ts",
|
"migration:mysql:drop": "pnpm with-env drizzle-kit drop --config ./configs/mysql.config.ts",
|
||||||
"migration:mysql:generate": "drizzle-kit generate --config ./configs/mysql.config.ts",
|
"migration:mysql:generate": "pnpm with-env drizzle-kit generate --config ./configs/mysql.config.ts",
|
||||||
"migration:mysql:run": "drizzle-kit migrate --config ./configs/mysql.config.ts && pnpm run seed",
|
"migration:mysql:run": "pnpm with-env drizzle-kit migrate --config ./configs/mysql.config.ts && pnpm run seed",
|
||||||
"migration:sqlite:drop": "drizzle-kit drop --config ./configs/sqlite.config.ts",
|
"migration:sqlite:drop": "pnpm with-env drizzle-kit drop --config ./configs/sqlite.config.ts",
|
||||||
"migration:sqlite:generate": "drizzle-kit generate --config ./configs/sqlite.config.ts",
|
"migration:sqlite:generate": "pnpm with-env drizzle-kit generate --config ./configs/sqlite.config.ts",
|
||||||
"migration:sqlite:run": "drizzle-kit migrate --config ./configs/sqlite.config.ts && pnpm run seed",
|
"migration:sqlite:run": "pnpm with-env drizzle-kit migrate --config ./configs/sqlite.config.ts && pnpm run seed",
|
||||||
"push:mysql": "drizzle-kit push --config ./configs/mysql.config.ts",
|
"push:mysql": "pnpm with-env drizzle-kit push --config ./configs/mysql.config.ts",
|
||||||
"push:sqlite": "drizzle-kit push --config ./configs/sqlite.config.ts",
|
"push:sqlite": "pnpm with-env drizzle-kit push --config ./configs/sqlite.config.ts",
|
||||||
"seed": "pnpm with-env tsx ./migrations/run-seed.ts",
|
"seed": "pnpm with-env tsx ./migrations/run-seed.ts",
|
||||||
"studio": "drizzle-kit studio --config ./configs/sqlite.config.ts",
|
"studio": "pnpm with-env drizzle-kit studio --config ./configs/sqlite.config.ts",
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
"with-env": "dotenv -e ../../.env --"
|
"with-env": "dotenv -e ../../.env --"
|
||||||
},
|
},
|
||||||
@@ -42,6 +43,7 @@
|
|||||||
"@homarr/log": "workspace:^0.1.0",
|
"@homarr/log": "workspace:^0.1.0",
|
||||||
"@homarr/server-settings": "workspace:^0.1.0",
|
"@homarr/server-settings": "workspace:^0.1.0",
|
||||||
"@paralleldrive/cuid2": "^2.2.2",
|
"@paralleldrive/cuid2": "^2.2.2",
|
||||||
|
"@t3-oss/env-nextjs": "^0.11.1",
|
||||||
"@testcontainers/mysql": "^10.16.0",
|
"@testcontainers/mysql": "^10.16.0",
|
||||||
"better-sqlite3": "^11.7.0",
|
"better-sqlite3": "^11.7.0",
|
||||||
"dotenv": "^16.4.7",
|
"dotenv": "^16.4.7",
|
||||||
|
|||||||
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
@@ -895,6 +895,9 @@ importers:
|
|||||||
'@paralleldrive/cuid2':
|
'@paralleldrive/cuid2':
|
||||||
specifier: ^2.2.2
|
specifier: ^2.2.2
|
||||||
version: 2.2.2
|
version: 2.2.2
|
||||||
|
'@t3-oss/env-nextjs':
|
||||||
|
specifier: ^0.11.1
|
||||||
|
version: 0.11.1(typescript@5.7.2)(zod@3.24.1)
|
||||||
'@testcontainers/mysql':
|
'@testcontainers/mysql':
|
||||||
specifier: ^10.16.0
|
specifier: ^10.16.0
|
||||||
version: 10.16.0
|
version: 10.16.0
|
||||||
|
|||||||
Reference in New Issue
Block a user