coolify/src/lib/database/common.ts

317 lines
7.9 KiB
TypeScript
Raw Normal View History

2022-02-10 14:47:44 +00:00
import { dev } from '$app/env';
import { sentry } from '$lib/common';
import {
supportedDatabaseTypesAndVersions,
supportedServiceTypesAndVersions
} from '$lib/components/common';
2022-02-10 14:47:44 +00:00
import * as Prisma from '@prisma/client';
import { default as ProdPrisma } from '@prisma/client';
import type { Database, DatabaseSettings } from '@prisma/client';
2022-02-10 14:47:44 +00:00
import generator from 'generate-password';
import forge from 'node-forge';
import getPort, { portNumbers } from 'get-port';
2022-02-10 14:47:44 +00:00
2022-04-29 20:25:37 +00:00
export function generatePassword(length = 24, symbols = false): string {
2022-02-10 14:47:44 +00:00
return generator.generate({
length,
numbers: true,
2022-04-29 20:25:37 +00:00
strict: true,
symbols
2022-02-10 14:47:44 +00:00
});
}
let { PrismaClient } = Prisma;
let P = Prisma.Prisma;
if (!dev) {
PrismaClient = ProdPrisma.PrismaClient;
P = ProdPrisma.Prisma;
}
export const prisma = new PrismaClient({
2022-05-10 08:12:13 +00:00
errorFormat: 'minimal',
2022-02-10 14:47:44 +00:00
rejectOnNotFound: false
});
2022-02-10 14:47:44 +00:00
export function ErrorHandler(e: {
stdout?;
message?: string;
status?: number;
name?: string;
error?: string;
}): { status: number; body: { message: string; error: string } } {
if (e && e instanceof Error) {
2022-02-10 20:56:19 +00:00
e = new Error(e.toString());
}
let truncatedError = e;
if (e.stdout) {
truncatedError = e.stdout;
}
2022-02-14 15:52:00 +00:00
if (e.message?.includes('docker run')) {
const truncatedArray: string[] = truncatedError.message.split('-').filter((line) => {
if (!line.startsWith('e ')) {
return line;
}
});
truncatedError.message = truncatedArray.join('-');
}
2022-02-14 15:52:00 +00:00
if (e.message?.includes('git clone')) {
2022-02-14 08:48:46 +00:00
truncatedError.message = 'git clone failed';
}
if (!e.message?.includes('Coolify Proxy is not running')) {
2022-05-06 13:40:07 +00:00
// sentry.captureException(truncatedError);
}
2022-02-10 14:47:44 +00:00
const payload = {
status: truncatedError.status || 500,
2022-02-10 14:47:44 +00:00
body: {
message: 'Ooops, something is not okay, are you okay?',
error: truncatedError.error || truncatedError.message
2022-02-10 14:47:44 +00:00
}
};
2022-02-14 15:52:00 +00:00
if (truncatedError?.name === 'NotFoundError') {
2022-02-10 14:47:44 +00:00
payload.status = 404;
}
if (truncatedError instanceof P.PrismaClientKnownRequestError) {
2022-02-14 15:52:00 +00:00
if (truncatedError?.code === 'P2002') {
2022-02-10 14:47:44 +00:00
payload.body.message = 'Already exists. Choose another name.';
}
}
return payload;
}
2022-02-10 14:47:44 +00:00
export async function generateSshKeyPair(): Promise<{ publicKey: string; privateKey: string }> {
return await new Promise((resolve, reject) => {
2022-02-10 14:47:44 +00:00
forge.pki.rsa.generateKeyPair({ bits: 4096, workers: -1 }, function (err, keys) {
if (keys) {
resolve({
publicKey: forge.ssh.publicKeyToOpenSSH(keys.publicKey),
privateKey: forge.ssh.privateKeyToOpenSSH(keys.privateKey)
});
} else {
reject(keys);
}
});
});
}
export function getVersions(type: string): string[] {
2022-02-10 14:47:44 +00:00
const found = supportedDatabaseTypesAndVersions.find((t) => t.name === type);
if (found) {
return found.versions;
}
return [];
}
export function getDatabaseImage(type: string): string {
2022-02-10 14:47:44 +00:00
const found = supportedDatabaseTypesAndVersions.find((t) => t.name === type);
if (found) {
return found.baseImage;
}
return '';
}
export function getServiceImage(type: string): string {
2022-02-10 14:47:44 +00:00
const found = supportedServiceTypesAndVersions.find((t) => t.name === type);
if (found) {
return found.baseImage;
}
return '';
}
export function getServiceImages(type: string): string[] {
2022-03-27 20:03:21 +00:00
const found = supportedServiceTypesAndVersions.find((t) => t.name === type);
if (found) {
return found.images;
}
return [];
}
export function generateDatabaseConfiguration(database: Database & { settings: DatabaseSettings }):
| {
2022-05-06 13:40:07 +00:00
volume: string;
image: string;
ulimits: Record<string, unknown>;
privatePort: number;
environmentVariables: {
MYSQL_DATABASE: string;
MYSQL_PASSWORD: string;
MYSQL_ROOT_USER: string;
MYSQL_USER: string;
MYSQL_ROOT_PASSWORD: string;
};
}
| {
2022-05-06 13:40:07 +00:00
volume: string;
image: string;
ulimits: Record<string, unknown>;
privatePort: number;
environmentVariables: {
MONGODB_ROOT_USER: string;
MONGODB_ROOT_PASSWORD: string;
};
}
| {
2022-05-06 13:40:07 +00:00
volume: string;
image: string;
ulimits: Record<string, unknown>;
privatePort: number;
environmentVariables: {
MARIADB_ROOT_USER: string;
MARIADB_ROOT_PASSWORD: string;
MARIADB_USER: string;
MARIADB_PASSWORD: string;
MARIADB_DATABASE: string;
};
}
| {
2022-05-06 13:40:07 +00:00
volume: string;
image: string;
ulimits: Record<string, unknown>;
privatePort: number;
environmentVariables: {
POSTGRESQL_POSTGRES_PASSWORD: string;
POSTGRESQL_USERNAME: string;
POSTGRESQL_PASSWORD: string;
POSTGRESQL_DATABASE: string;
};
}
| {
2022-05-06 13:40:07 +00:00
volume: string;
image: string;
ulimits: Record<string, unknown>;
privatePort: number;
environmentVariables: {
REDIS_AOF_ENABLED: string;
REDIS_PASSWORD: string;
};
}
2022-05-02 14:25:24 +00:00
| {
2022-05-06 13:40:07 +00:00
volume: string;
image: string;
ulimits: Record<string, unknown>;
privatePort: number;
environmentVariables: {
COUCHDB_PASSWORD: string;
COUCHDB_USER: string;
};
} {
2022-02-10 14:47:44 +00:00
const {
id,
dbUser,
dbUserPassword,
rootUser,
rootUserPassword,
defaultDatabase,
version,
type,
settings: { appendOnly }
} = database;
const baseImage = getDatabaseImage(type);
if (type === 'mysql') {
return {
privatePort: 3306,
environmentVariables: {
MYSQL_USER: dbUser,
MYSQL_PASSWORD: dbUserPassword,
MYSQL_ROOT_PASSWORD: rootUserPassword,
MYSQL_ROOT_USER: rootUser,
MYSQL_DATABASE: defaultDatabase
},
image: `${baseImage}:${version}`,
volume: `${id}-${type}-data:/bitnami/mysql/data`,
2022-05-02 14:25:24 +00:00
ulimits: {}
};
} else if (type === 'mariadb') {
return {
privatePort: 3306,
environmentVariables: {
MARIADB_ROOT_USER: rootUser,
MARIADB_ROOT_PASSWORD: rootUserPassword,
MARIADB_USER: dbUser,
MARIADB_PASSWORD: dbUserPassword,
MARIADB_DATABASE: defaultDatabase
},
image: `${baseImage}:${version}`,
volume: `${id}-${type}-data:/bitnami/mariadb`,
2022-02-10 14:47:44 +00:00
ulimits: {}
};
} else if (type === 'mongodb') {
return {
privatePort: 27017,
environmentVariables: {
MONGODB_ROOT_USER: rootUser,
MONGODB_ROOT_PASSWORD: rootUserPassword
},
image: `${baseImage}:${version}`,
volume: `${id}-${type}-data:/bitnami/mongodb`,
ulimits: {}
};
} else if (type === 'postgresql') {
return {
privatePort: 5432,
environmentVariables: {
2022-04-23 14:12:16 +00:00
POSTGRESQL_POSTGRES_PASSWORD: rootUserPassword,
2022-02-10 14:47:44 +00:00
POSTGRESQL_PASSWORD: dbUserPassword,
POSTGRESQL_USERNAME: dbUser,
POSTGRESQL_DATABASE: defaultDatabase
},
image: `${baseImage}:${version}`,
volume: `${id}-${type}-data:/bitnami/postgresql`,
ulimits: {}
};
} else if (type === 'redis') {
return {
privatePort: 6379,
environmentVariables: {
REDIS_PASSWORD: dbUserPassword,
REDIS_AOF_ENABLED: appendOnly ? 'yes' : 'no'
},
image: `${baseImage}:${version}`,
volume: `${id}-${type}-data:/bitnami/redis/data`,
ulimits: {}
};
} else if (type === 'couchdb') {
return {
privatePort: 5984,
environmentVariables: {
COUCHDB_PASSWORD: dbUserPassword,
COUCHDB_USER: dbUser
},
image: `${baseImage}:${version}`,
volume: `${id}-${type}-data:/bitnami/couchdb`,
ulimits: {}
};
}
}
export async function getFreePort() {
const data = await prisma.setting.findFirst();
const { minPort, maxPort } = data;
const dbUsed = await (
await prisma.database.findMany({
where: { publicPort: { not: null } },
select: { publicPort: true }
})
).map((a) => a.publicPort);
const wpFtpUsed = await (
await prisma.wordpress.findMany({
where: { ftpPublicPort: { not: null } },
select: { ftpPublicPort: true }
})
).map((a) => a.ftpPublicPort);
const wpUsed = await (
await prisma.wordpress.findMany({
where: { mysqlPublicPort: { not: null } },
select: { mysqlPublicPort: true }
})
).map((a) => a.mysqlPublicPort);
2022-05-19 11:45:17 +00:00
const minioUsed = await (
2022-05-19 08:20:43 +00:00
await prisma.minio.findMany({
where: { publicPort: { not: null } },
select: { publicPort: true }
})
).map((a) => a.publicPort);
2022-05-19 11:45:17 +00:00
const usedPorts = [...dbUsed, ...wpFtpUsed, ...wpUsed, ...minioUsed];
return await getPort({ port: portNumbers(minPort, maxPort), exclude: usedPorts });
}