Merge pull request #293 from dominicbachmann/improve-typing

Started to introduce more typing
This commit is contained in:
Andras Bacsai 2022-04-11 22:40:47 +02:00 committed by GitHub
commit 6a833934ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 994 additions and 460 deletions

View File

@ -1,9 +1,17 @@
async function send({ method, path, data = {}, headers, timeout = 30000 }) { // TODO: Make this functions generic
async function send({
method,
path,
data = {},
headers,
timeout = 30000
}): Promise<Record<string, unknown>> {
const controller = new AbortController(); const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout); const id = setTimeout(() => controller.abort(), timeout);
const opts = { method, headers: {}, body: null, signal: controller.signal }; const opts = { method, headers: {}, body: null, signal: controller.signal };
if (Object.keys(data).length > 0) { if (Object.keys(data).length > 0) {
let parsedData = data; const parsedData = data;
for (const [key, value] of Object.entries(data)) { for (const [key, value] of Object.entries(data)) {
if (value === '') { if (value === '') {
parsedData[key] = null; parsedData[key] = null;
@ -43,18 +51,33 @@ async function send({ method, path, data = {}, headers, timeout = 30000 }) {
return responseData; return responseData;
} }
export function get(path, headers = {}): Promise<any> { export function get(
path: string,
headers: Record<string, unknown>
): Promise<Record<string, unknown>> {
return send({ method: 'GET', path, headers }); return send({ method: 'GET', path, headers });
} }
export function del(path, data = {}, headers = {}): Promise<any> { export function del(
path: string,
data: Record<string, unknown>,
headers: Record<string, unknown>
): Promise<Record<string, unknown>> {
return send({ method: 'DELETE', path, data, headers }); return send({ method: 'DELETE', path, data, headers });
} }
export function post(path, data, headers = {}): Promise<any> { export function post(
path: string,
data: Record<string, unknown>,
headers: Record<string, unknown>
): Promise<Record<string, unknown>> {
return send({ method: 'POST', path, data, headers }); return send({ method: 'POST', path, data, headers });
} }
export function put(path, data, headers = {}): Promise<any> { export function put(
path: string,
data: Record<string, unknown>,
headers: Record<string, unknown>
): Promise<Record<string, unknown>> {
return send({ method: 'PUT', path, data, headers }); return send({ method: 'PUT', path, data, headers });
} }

View File

@ -12,7 +12,8 @@ import { version as currentVersion } from '../../package.json';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import Cookie from 'cookie'; import Cookie from 'cookie';
import os from 'os'; import os from 'os';
import cuid from 'cuid'; import type { RequestEvent } from '@sveltejs/kit/types/internal';
import type { Job } from 'bullmq';
try { try {
if (!dev) { if (!dev) {
@ -45,13 +46,21 @@ const customConfig: Config = {
export const version = currentVersion; export const version = currentVersion;
export const asyncExecShell = util.promisify(child.exec); export const asyncExecShell = util.promisify(child.exec);
export const asyncSleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay)); export const asyncSleep = (delay: number): Promise<unknown> =>
new Promise((resolve) => setTimeout(resolve, delay));
export const sentry = Sentry; export const sentry = Sentry;
export const uniqueName = () => uniqueNamesGenerator(customConfig); export const uniqueName = (): string => uniqueNamesGenerator(customConfig);
export const saveBuildLog = async ({ line, buildId, applicationId }) => { export const saveBuildLog = async ({
line,
buildId,
applicationId
}: {
line: string;
buildId: string;
applicationId: string;
}): Promise<Job> => {
if (line) { if (line) {
if (line.includes('ghs_')) { if (line.includes('ghs_')) {
const regex = /ghs_.*@/g; const regex = /ghs_.*@/g;
@ -62,20 +71,7 @@ export const saveBuildLog = async ({ line, buildId, applicationId }) => {
} }
}; };
export const isTeamIdTokenAvailable = (request) => { export const getTeam = (event: RequestEvent): string | null => {
const cookie = request.headers.cookie
?.split(';')
.map((s) => s.trim())
.find((s) => s.startsWith('teamId='))
?.split('=')[1];
if (!cookie) {
return getTeam(request);
} else {
return cookie;
}
};
export const getTeam = (event) => {
const cookies = Cookie.parse(event.request.headers.get('cookie')); const cookies = Cookie.parse(event.request.headers.get('cookie'));
if (cookies?.teamId) { if (cookies?.teamId) {
return cookies.teamId; return cookies.teamId;
@ -85,7 +81,16 @@ export const getTeam = (event) => {
return null; return null;
}; };
export const getUserDetails = async (event, isAdminRequired = true) => { export const getUserDetails = async (
event: RequestEvent,
isAdminRequired = true
): Promise<{
teamId: string;
userId: string;
permission: string;
status: number;
body: { message: string };
}> => {
const teamId = getTeam(event); const teamId = getTeam(event);
const userId = event?.locals?.session?.data?.userId || null; const userId = event?.locals?.session?.data?.userId || null;
const { permission = 'read' } = await db.prisma.permission.findFirst({ const { permission = 'read' } = await db.prisma.permission.findFirst({
@ -112,11 +117,11 @@ export const getUserDetails = async (event, isAdminRequired = true) => {
return payload; return payload;
}; };
export function getEngine(engine) { export function getEngine(engine: string): string {
return engine === '/var/run/docker.sock' ? 'unix:///var/run/docker.sock' : engine; return engine === '/var/run/docker.sock' ? 'unix:///var/run/docker.sock' : engine;
} }
export async function removeContainer(id, engine) { export async function removeContainer(id: string, engine: string): Promise<void> {
const host = getEngine(engine); const host = getEngine(engine);
try { try {
const { stdout } = await asyncExecShell( const { stdout } = await asyncExecShell(
@ -132,11 +137,23 @@ export async function removeContainer(id, engine) {
} }
} }
export const removeDestinationDocker = async ({ id, engine }) => { export const removeDestinationDocker = async ({
id,
engine
}: {
id: string;
engine: string;
}): Promise<void> => {
return await removeContainer(id, engine); return await removeContainer(id, engine);
}; };
export const createDirectories = async ({ repository, buildId }) => { export const createDirectories = async ({
repository,
buildId
}: {
repository: string;
buildId: string;
}): Promise<{ workdir: string; repodir: string }> => {
const repodir = `/tmp/build-sources/${repository}/`; const repodir = `/tmp/build-sources/${repository}/`;
const workdir = `/tmp/build-sources/${repository}/${buildId}`; const workdir = `/tmp/build-sources/${repository}/${buildId}`;
@ -148,20 +165,10 @@ export const createDirectories = async ({ repository, buildId }) => {
}; };
}; };
export function generateTimestamp() { export function generateTimestamp(): string {
return `${dayjs().format('HH:mm:ss.SSS')} `; return `${dayjs().format('HH:mm:ss.SSS')} `;
} }
export function getDomain(domain) { export function getDomain(domain: string): string {
return domain?.replace('https://', '').replace('http://', ''); return domain?.replace('https://', '').replace('http://', '');
} }
export function dashify(str: string, options?: any): string {
if (typeof str !== 'string') return str;
return str
.trim()
.replace(/\W/g, (m) => (/[À-ž]/.test(m) ? m : '-'))
.replace(/^-+|-+$/g, '')
.replace(/-{2,}/g, (m) => (options && options.condense ? '-' : m))
.toLowerCase();
}

View File

@ -1,13 +1,13 @@
import crypto from 'crypto'; import crypto from 'crypto';
const algorithm = 'aes-256-ctr'; const algorithm = 'aes-256-ctr';
export const base64Encode = (text: string) => { export const base64Encode = (text: string): string => {
return Buffer.from(text).toString('base64'); return Buffer.from(text).toString('base64');
}; };
export const base64Decode = (text: string) => { export const base64Decode = (text: string): string => {
return Buffer.from(text, 'base64').toString('ascii'); return Buffer.from(text, 'base64').toString('ascii');
}; };
export const encrypt = (text: string) => { export const encrypt = (text: string): string => {
if (text) { if (text) {
const iv = crypto.randomBytes(16); const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(algorithm, process.env['COOLIFY_SECRET_KEY'], iv); const cipher = crypto.createCipheriv(algorithm, process.env['COOLIFY_SECRET_KEY'], iv);
@ -19,7 +19,7 @@ export const encrypt = (text: string) => {
} }
}; };
export const decrypt = (hashString: string) => { export const decrypt = (hashString: string): string => {
if (hashString) { if (hashString) {
const hash: Hash = JSON.parse(hashString); const hash: Hash = JSON.parse(hashString);
const decipher = crypto.createDecipheriv( const decipher = crypto.createDecipheriv(

View File

@ -1,10 +1,19 @@
import { decrypt, encrypt } from '$lib/crypto'; import { decrypt, encrypt } from '$lib/crypto';
import { asyncExecShell, getEngine } from '$lib/common'; import { asyncExecShell, getEngine } from '$lib/common';
import { getDomain, removeDestinationDocker } from '$lib/common'; import { removeDestinationDocker } from '$lib/common';
import { prisma } from './common'; import { prisma } from './common';
export async function listApplications(teamId) { import type {
DestinationDocker,
GitSource,
Secret,
ApplicationSettings,
Application,
ApplicationPersistentStorage
} from '@prisma/client';
export async function listApplications(teamId: string): Promise<Application[]> {
if (teamId === '0') { if (teamId === '0') {
return await prisma.application.findMany({ include: { teams: true } }); return await prisma.application.findMany({ include: { teams: true } });
} }
@ -14,7 +23,13 @@ export async function listApplications(teamId) {
}); });
} }
export async function newApplication({ name, teamId }) { export async function newApplication({
name,
teamId
}: {
name: string;
teamId: string;
}): Promise<Application> {
return await prisma.application.create({ return await prisma.application.create({
data: { data: {
name, name,
@ -24,34 +39,17 @@ export async function newApplication({ name, teamId }) {
}); });
} }
export async function importApplication({ export async function removeApplication({
name, id,
teamId, teamId
fqdn, }: {
port, id: string;
buildCommand, teamId: string;
startCommand, }): Promise<void> {
installCommand const { destinationDockerId, destinationDocker } = await prisma.application.findUnique({
}) {
return await prisma.application.create({
data: {
name,
fqdn,
port,
buildCommand,
startCommand,
installCommand,
teams: { connect: { id: teamId } }
}
});
}
export async function removeApplication({ id, teamId }) {
const { fqdn, destinationDockerId, destinationDocker } = await prisma.application.findUnique({
where: { id }, where: { id },
include: { destinationDocker: true } include: { destinationDocker: true }
}); });
const domain = getDomain(fqdn);
if (destinationDockerId) { if (destinationDockerId) {
const host = getEngine(destinationDocker.engine); const host = getEngine(destinationDocker.engine);
const { stdout: containers } = await asyncExecShell( const { stdout: containers } = await asyncExecShell(
@ -62,7 +60,6 @@ export async function removeApplication({ id, teamId }) {
for (const container of containersArray) { for (const container of containersArray) {
const containerObj = JSON.parse(container); const containerObj = JSON.parse(container);
const id = containerObj.ID; const id = containerObj.ID;
const preview = containerObj.Image.split('-')[1];
await removeDestinationDocker({ id, engine: destinationDocker.engine }); await removeDestinationDocker({ id, engine: destinationDocker.engine });
} }
} }
@ -80,9 +77,23 @@ export async function removeApplication({ id, teamId }) {
} }
} }
export async function getApplicationWebhook({ projectId, branch }) { export async function getApplicationWebhook({
projectId,
branch
}: {
projectId: number;
branch: string;
}): Promise<
Application & {
destinationDocker: DestinationDocker;
settings: ApplicationSettings;
gitSource: GitSource;
secrets: Secret[];
persistentStorage: ApplicationPersistentStorage[];
}
> {
try { try {
let application = await prisma.application.findFirst({ const application = await prisma.application.findFirst({
where: { projectId, branch, settings: { autodeploy: true } }, where: { projectId, branch, settings: { autodeploy: true } },
include: { include: {
destinationDocker: true, destinationDocker: true,
@ -131,16 +142,17 @@ export async function getApplicationWebhook({ projectId, branch }) {
throw { status: 404, body: { message: e.message } }; throw { status: 404, body: { message: e.message } };
} }
} }
export async function getApplicationById({ id }) {
const body = await prisma.application.findFirst({
where: { id },
include: { destinationDocker: true }
});
return { ...body }; export async function getApplication({ id, teamId }: { id: string; teamId: string }): Promise<
Application & {
destinationDocker: DestinationDocker;
settings: ApplicationSettings;
gitSource: GitSource;
secrets: Secret[];
persistentStorage: ApplicationPersistentStorage[];
} }
export async function getApplication({ id, teamId }) { > {
let body = {}; let body;
if (teamId === '0') { if (teamId === '0') {
body = await prisma.application.findFirst({ body = await prisma.application.findFirst({
where: { id }, where: { id },
@ -194,7 +206,14 @@ export async function configureGitRepository({
projectId, projectId,
webhookToken, webhookToken,
autodeploy autodeploy
}) { }: {
id: string;
repository: string;
branch: string;
projectId: number;
webhookToken: string;
autodeploy: boolean;
}): Promise<void> {
if (webhookToken) { if (webhookToken) {
const encryptedWebhookToken = encrypt(webhookToken); const encryptedWebhookToken = encrypt(webhookToken);
await prisma.application.update({ await prisma.application.update({
@ -224,7 +243,10 @@ export async function configureGitRepository({
} }
} }
export async function configureBuildPack({ id, buildPack }) { export async function configureBuildPack({
id,
buildPack
}: Pick<Application, 'id' | 'buildPack'>): Promise<Application> {
return await prisma.application.update({ where: { id }, data: { buildPack } }); return await prisma.application.update({ where: { id }, data: { buildPack } });
} }
@ -242,7 +264,21 @@ export async function configureApplication({
pythonWSGI, pythonWSGI,
pythonModule, pythonModule,
pythonVariable pythonVariable
}) { }: {
id: string;
buildPack: string;
name: string;
fqdn: string;
port: number;
installCommand: string;
buildCommand: string;
startCommand: string;
baseDirectory: string;
publishDirectory: string;
pythonWSGI: string;
pythonModule: string;
pythonVariable: string;
}): Promise<Application> {
return await prisma.application.update({ return await prisma.application.update({
where: { id }, where: { id },
data: { data: {
@ -262,11 +298,24 @@ export async function configureApplication({
}); });
} }
export async function checkDoubleBranch(branch, projectId) { export async function checkDoubleBranch(branch: string, projectId: number): Promise<boolean> {
const applications = await prisma.application.findMany({ where: { branch, projectId } }); const applications = await prisma.application.findMany({ where: { branch, projectId } });
return applications.length > 1; return applications.length > 1;
} }
export async function setApplicationSettings({ id, debug, previews, dualCerts, autodeploy }) {
export async function setApplicationSettings({
id,
debug,
previews,
dualCerts,
autodeploy
}: {
id: string;
debug: boolean;
previews: boolean;
dualCerts: boolean;
autodeploy: boolean;
}): Promise<Application & { destinationDocker: DestinationDocker }> {
return await prisma.application.update({ return await prisma.application.update({
where: { id }, where: { id },
data: { settings: { update: { debug, previews, dualCerts, autodeploy } } }, data: { settings: { update: { debug, previews, dualCerts, autodeploy } } },
@ -274,29 +323,6 @@ export async function setApplicationSettings({ id, debug, previews, dualCerts, a
}); });
} }
export async function createBuild({ export async function getPersistentStorage(id: string): Promise<ApplicationPersistentStorage[]> {
id,
applicationId,
destinationDockerId,
gitSourceId,
githubAppId,
gitlabAppId,
type
}) {
return await prisma.build.create({
data: {
id,
applicationId,
destinationDockerId,
gitSourceId,
githubAppId,
gitlabAppId,
status: 'running',
type
}
});
}
export async function getPersistentStorage(id) {
return await prisma.applicationPersistentStorage.findMany({ where: { applicationId: id } }); return await prisma.applicationPersistentStorage.findMany({ where: { applicationId: id } });
} }

View File

@ -1,7 +1,16 @@
import { getDomain } from '$lib/common'; import { getDomain } from '$lib/common';
import { prisma } from './common'; import { prisma } from './common';
import type { Application, ServiceSecret, DestinationDocker, Secret } from '@prisma/client';
export async function isBranchAlreadyUsed({ repository, branch, id }) { export async function isBranchAlreadyUsed({
repository,
branch,
id
}: {
id: string;
repository: string;
branch: string;
}): Promise<Application> {
const application = await prisma.application.findUnique({ const application = await prisma.application.findUnique({
where: { id }, where: { id },
include: { gitSource: true } include: { gitSource: true }
@ -11,18 +20,42 @@ export async function isBranchAlreadyUsed({ repository, branch, id }) {
}); });
} }
export async function isDockerNetworkExists({ network }) { export async function isDockerNetworkExists({
network
}: {
network: string;
}): Promise<DestinationDocker> {
return await prisma.destinationDocker.findFirst({ where: { network } }); return await prisma.destinationDocker.findFirst({ where: { network } });
} }
export async function isServiceSecretExists({ id, name }) { export async function isServiceSecretExists({
id,
name
}: {
id: string;
name: string;
}): Promise<ServiceSecret> {
return await prisma.serviceSecret.findFirst({ where: { name, serviceId: id } }); return await prisma.serviceSecret.findFirst({ where: { name, serviceId: id } });
} }
export async function isSecretExists({ id, name, isPRMRSecret }) { export async function isSecretExists({
id,
name,
isPRMRSecret
}: {
id: string;
name: string;
isPRMRSecret: boolean;
}): Promise<Secret> {
return await prisma.secret.findFirst({ where: { name, applicationId: id, isPRMRSecret } }); return await prisma.secret.findFirst({ where: { name, applicationId: id, isPRMRSecret } });
} }
export async function isDomainConfigured({ id, fqdn }) { export async function isDomainConfigured({
id,
fqdn
}: {
id: string;
fqdn: string;
}): Promise<boolean> {
const domain = getDomain(fqdn); const domain = getDomain(fqdn);
const nakedDomain = domain.replace('www.', ''); const nakedDomain = domain.replace('www.', '');
const foundApp = await prisma.application.findFirst({ const foundApp = await prisma.application.findFirst({
@ -55,6 +88,5 @@ export async function isDomainConfigured({ id, fqdn }) {
}, },
select: { fqdn: true } select: { fqdn: true }
}); });
if (foundApp || foundService || coolifyFqdn) return true; return !!(foundApp || foundService || coolifyFqdn);
return false;
} }

View File

@ -6,11 +6,11 @@ import {
} from '$lib/components/common'; } from '$lib/components/common';
import * as Prisma from '@prisma/client'; import * as Prisma from '@prisma/client';
import { default as ProdPrisma } from '@prisma/client'; import { default as ProdPrisma } from '@prisma/client';
import type { PrismaClientOptions } from '@prisma/client/runtime'; import type { Database, DatabaseSettings } from '@prisma/client';
import generator from 'generate-password'; import generator from 'generate-password';
import forge from 'node-forge'; import forge from 'node-forge';
export function generatePassword(length = 24) { export function generatePassword(length = 24): string {
return generator.generate({ return generator.generate({
length, length,
numbers: true, numbers: true,
@ -30,8 +30,14 @@ export const prisma = new PrismaClient({
rejectOnNotFound: false rejectOnNotFound: false
}); });
export function ErrorHandler(e) { export function ErrorHandler(e: {
if (e! instanceof Error) { stdout?;
message?: string;
status?: number;
name?: string;
error?: string;
}): { status: number; body: { message: string; error: string } } {
if (e && e instanceof Error) {
e = new Error(e.toString()); e = new Error(e.toString());
} }
let truncatedError = e; let truncatedError = e;
@ -39,8 +45,7 @@ export function ErrorHandler(e) {
truncatedError = e.stdout; truncatedError = e.stdout;
} }
if (e.message?.includes('docker run')) { if (e.message?.includes('docker run')) {
let truncatedArray = []; const truncatedArray: string[] = truncatedError.message.split('-').filter((line) => {
truncatedArray = truncatedError.message.split('-').filter((line) => {
if (!line.startsWith('e ')) { if (!line.startsWith('e ')) {
return line; return line;
} }
@ -68,11 +73,11 @@ export function ErrorHandler(e) {
payload.body.message = 'Already exists. Choose another name.'; payload.body.message = 'Already exists. Choose another name.';
} }
} }
// console.error(e)
return payload; return payload;
} }
export async function generateSshKeyPair(): Promise<{ publicKey: string; privateKey: string }> { export async function generateSshKeyPair(): Promise<{ publicKey: string; privateKey: string }> {
return await new Promise(async (resolve, reject) => { return await new Promise((resolve, reject) => {
forge.pki.rsa.generateKeyPair({ bits: 4096, workers: -1 }, function (err, keys) { forge.pki.rsa.generateKeyPair({ bits: 4096, workers: -1 }, function (err, keys) {
if (keys) { if (keys) {
resolve({ resolve({
@ -86,35 +91,93 @@ export async function generateSshKeyPair(): Promise<{ publicKey: string; private
}); });
} }
export function getVersions(type) { export function getVersions(type: string): string[] {
const found = supportedDatabaseTypesAndVersions.find((t) => t.name === type); const found = supportedDatabaseTypesAndVersions.find((t) => t.name === type);
if (found) { if (found) {
return found.versions; return found.versions;
} }
return []; return [];
} }
export function getDatabaseImage(type) {
export function getDatabaseImage(type: string): string {
const found = supportedDatabaseTypesAndVersions.find((t) => t.name === type); const found = supportedDatabaseTypesAndVersions.find((t) => t.name === type);
if (found) { if (found) {
return found.baseImage; return found.baseImage;
} }
return ''; return '';
} }
export function getServiceImage(type) {
export function getServiceImage(type: string): string {
const found = supportedServiceTypesAndVersions.find((t) => t.name === type); const found = supportedServiceTypesAndVersions.find((t) => t.name === type);
if (found) { if (found) {
return found.baseImage; return found.baseImage;
} }
return ''; return '';
} }
export function getServiceImages(type) {
export function getServiceImages(type: string): string[] {
const found = supportedServiceTypesAndVersions.find((t) => t.name === type); const found = supportedServiceTypesAndVersions.find((t) => t.name === type);
if (found) { if (found) {
return found.images; return found.images;
} }
return []; return [];
} }
export function generateDatabaseConfiguration(database) {
export function generateDatabaseConfiguration(database: Database & { settings: DatabaseSettings }):
| {
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;
};
}
| {
volume: string;
image: string;
ulimits: Record<string, unknown>;
privatePort: number;
environmentVariables: {
MONGODB_ROOT_USER: string;
MONGODB_ROOT_PASSWORD: string;
};
}
| {
volume: string;
image: string;
ulimits: Record<string, unknown>;
privatePort: number;
environmentVariables: {
POSTGRESQL_USERNAME: string;
POSTGRESQL_PASSWORD: string;
POSTGRESQL_DATABASE: string;
};
}
| {
volume: string;
image: string;
ulimits: Record<string, unknown>;
privatePort: number;
environmentVariables: {
REDIS_AOF_ENABLED: string;
REDIS_PASSWORD: string;
};
}
| {
volume: string;
image: string;
ulimits: Record<string, unknown>;
privatePort: number;
environmentVariables: {
COUCHDB_PASSWORD: string;
COUCHDB_USER: string;
};
} {
const { const {
id, id,
dbUser, dbUser,
@ -129,7 +192,6 @@ export function generateDatabaseConfiguration(database) {
const baseImage = getDatabaseImage(type); const baseImage = getDatabaseImage(type);
if (type === 'mysql') { if (type === 'mysql') {
return { return {
// url: `mysql://${dbUser}:${dbUserPassword}@${id}:${isPublic ? port : 3306}/${defaultDatabase}`,
privatePort: 3306, privatePort: 3306,
environmentVariables: { environmentVariables: {
MYSQL_USER: dbUser, MYSQL_USER: dbUser,
@ -144,7 +206,6 @@ export function generateDatabaseConfiguration(database) {
}; };
} else if (type === 'mongodb') { } else if (type === 'mongodb') {
return { return {
// url: `mongodb://${dbUser}:${dbUserPassword}@${id}:${isPublic ? port : 27017}/${defaultDatabase}`,
privatePort: 27017, privatePort: 27017,
environmentVariables: { environmentVariables: {
MONGODB_ROOT_USER: rootUser, MONGODB_ROOT_USER: rootUser,
@ -156,7 +217,6 @@ export function generateDatabaseConfiguration(database) {
}; };
} else if (type === 'postgresql') { } else if (type === 'postgresql') {
return { return {
// url: `psql://${dbUser}:${dbUserPassword}@${id}:${isPublic ? port : 5432}/${defaultDatabase}`,
privatePort: 5432, privatePort: 5432,
environmentVariables: { environmentVariables: {
POSTGRESQL_POSTGRES_PASSWORD: rootUserPassword, POSTGRESQL_POSTGRES_PASSWORD: rootUserPassword,
@ -170,7 +230,6 @@ export function generateDatabaseConfiguration(database) {
}; };
} else if (type === 'redis') { } else if (type === 'redis') {
return { return {
// url: `redis://${dbUser}:${dbUserPassword}@${id}:${isPublic ? port : 6379}/${defaultDatabase}`,
privatePort: 6379, privatePort: 6379,
environmentVariables: { environmentVariables: {
REDIS_PASSWORD: dbUserPassword, REDIS_PASSWORD: dbUserPassword,
@ -182,7 +241,6 @@ export function generateDatabaseConfiguration(database) {
}; };
} else if (type === 'couchdb') { } else if (type === 'couchdb') {
return { return {
// url: `couchdb://${dbUser}:${dbUserPassword}@${id}:${isPublic ? port : 5984}/${defaultDatabase}`,
privatePort: 5984, privatePort: 5984,
environmentVariables: { environmentVariables: {
COUCHDB_PASSWORD: dbUserPassword, COUCHDB_PASSWORD: dbUserPassword,
@ -193,18 +251,4 @@ export function generateDatabaseConfiguration(database) {
ulimits: {} ulimits: {}
}; };
} }
// } else if (type === 'clickhouse') {
// return {
// url: `clickhouse://${dbUser}:${dbUserPassword}@${id}:${port}/${defaultDatabase}`,
// privatePort: 9000,
// image: `bitnami/clickhouse-server:${version}`,
// volume: `${id}-${type}-data:/var/lib/clickhouse`,
// ulimits: {
// nofile: {
// soft: 262144,
// hard: 262144
// }
// }
// }
// }
} }

View File

@ -1,12 +1,11 @@
import { decrypt, encrypt } from '$lib/crypto'; import { decrypt, encrypt } from '$lib/crypto';
import * as db from '$lib/database';
import cuid from 'cuid'; import cuid from 'cuid';
import { generatePassword } from '.'; import { generatePassword } from '.';
import { prisma, ErrorHandler } from './common'; import { prisma } from './common';
import getPort, { portNumbers } from 'get-port';
import { asyncExecShell, getEngine, removeContainer } from '$lib/common'; import { asyncExecShell, getEngine, removeContainer } from '$lib/common';
import type { Database, DatabaseSettings, DestinationDocker } from '@prisma/client';
export async function listDatabases(teamId) { export async function listDatabases(teamId: string): Promise<Database[]> {
if (teamId === '0') { if (teamId === '0') {
return await prisma.database.findMany({ include: { teams: true } }); return await prisma.database.findMany({ include: { teams: true } });
} else { } else {
@ -16,7 +15,14 @@ export async function listDatabases(teamId) {
}); });
} }
} }
export async function newDatabase({ name, teamId }) {
export async function newDatabase({
name,
teamId
}: {
name: string;
teamId: string;
}): Promise<Database> {
const dbUser = cuid(); const dbUser = cuid();
const dbUserPassword = encrypt(generatePassword()); const dbUserPassword = encrypt(generatePassword());
const rootUser = cuid(); const rootUser = cuid();
@ -37,8 +43,14 @@ export async function newDatabase({ name, teamId }) {
}); });
} }
export async function getDatabase({ id, teamId }) { export async function getDatabase({
let body = {}; id,
teamId
}: {
id: string;
teamId: string;
}): Promise<Database & { destinationDocker: DestinationDocker; settings: DatabaseSettings }> {
let body;
if (teamId === '0') { if (teamId === '0') {
body = await prisma.database.findFirst({ body = await prisma.database.findFirst({
where: { id }, where: { id },
@ -50,20 +62,25 @@ export async function getDatabase({ id, teamId }) {
include: { destinationDocker: true, settings: true } include: { destinationDocker: true, settings: true }
}); });
} }
if (body.dbUserPassword) body.dbUserPassword = decrypt(body.dbUserPassword); if (body.dbUserPassword) body.dbUserPassword = decrypt(body.dbUserPassword);
if (body.rootUserPassword) body.rootUserPassword = decrypt(body.rootUserPassword); if (body.rootUserPassword) body.rootUserPassword = decrypt(body.rootUserPassword);
return { ...body }; return body;
} }
export async function removeDatabase({ id }) { export async function removeDatabase({ id }: { id: string }): Promise<void> {
await prisma.databaseSettings.deleteMany({ where: { databaseId: id } }); await prisma.databaseSettings.deleteMany({ where: { databaseId: id } });
await prisma.database.delete({ where: { id } }); await prisma.database.delete({ where: { id } });
return; return;
} }
export async function configureDatabaseType({ id, type }) { export async function configureDatabaseType({
id,
type
}: {
id: string;
type: string;
}): Promise<Database> {
return await prisma.database.update({ return await prisma.database.update({
where: { id }, where: { id },
data: { type } data: { type }
@ -79,7 +96,7 @@ export async function setDatabase({
version?: string; version?: string;
isPublic?: boolean; isPublic?: boolean;
appendOnly?: boolean; appendOnly?: boolean;
}) { }): Promise<Database> {
return await prisma.database.update({ return await prisma.database.update({
where: { id }, where: { id },
data: { data: {
@ -97,7 +114,16 @@ export async function updateDatabase({
rootUser, rootUser,
rootUserPassword, rootUserPassword,
version version
}) { }: {
id: string;
name: string;
defaultDatabase: string;
dbUser: string;
dbUserPassword: string;
rootUser: string;
rootUserPassword: string;
version: string;
}): Promise<Database> {
const encryptedDbUserPassword = dbUserPassword && encrypt(dbUserPassword); const encryptedDbUserPassword = dbUserPassword && encrypt(dbUserPassword);
const encryptedRootUserPassword = rootUserPassword && encrypt(rootUserPassword); const encryptedRootUserPassword = rootUserPassword && encrypt(rootUserPassword);
return await prisma.database.update({ return await prisma.database.update({
@ -114,7 +140,9 @@ export async function updateDatabase({
}); });
} }
export async function stopDatabase(database) { export async function stopDatabase(
database: Database & { destinationDocker: DestinationDocker }
): Promise<boolean> {
let everStarted = false; let everStarted = false;
const { const {
id, id,

View File

@ -1,11 +1,22 @@
import { asyncExecShell, getEngine } from '$lib/common'; import { asyncExecShell, getEngine } from '$lib/common';
import { decrypt, encrypt } from '$lib/crypto';
import { dockerInstance } from '$lib/docker'; import { dockerInstance } from '$lib/docker';
import { startCoolifyProxy } from '$lib/haproxy'; import { startCoolifyProxy } from '$lib/haproxy';
import { getDatabaseImage } from '.'; import { getDatabaseImage } from '.';
import { prisma } from './common'; import { prisma } from './common';
import type { DestinationDocker, Service, Application, Prisma } from '@prisma/client';
import type { CreateDockerDestination } from '$lib/types/destinations';
export async function listDestinations(teamId) { type DestinationConfigurationObject = {
id: string;
destinationId: string;
};
type FindDestinationFromTeam = {
id: string;
teamId: string;
};
export async function listDestinations(teamId: string): Promise<DestinationDocker[]> {
if (teamId === '0') { if (teamId === '0') {
return await prisma.destinationDocker.findMany({ include: { teams: true } }); return await prisma.destinationDocker.findMany({ include: { teams: true } });
} }
@ -15,19 +26,28 @@ export async function listDestinations(teamId) {
}); });
} }
export async function configureDestinationForService({ id, destinationId }) { export async function configureDestinationForService({
id,
destinationId
}: DestinationConfigurationObject): Promise<Service> {
return await prisma.service.update({ return await prisma.service.update({
where: { id }, where: { id },
data: { destinationDocker: { connect: { id: destinationId } } } data: { destinationDocker: { connect: { id: destinationId } } }
}); });
} }
export async function configureDestinationForApplication({ id, destinationId }) { export async function configureDestinationForApplication({
id,
destinationId
}: DestinationConfigurationObject): Promise<Application> {
return await prisma.application.update({ return await prisma.application.update({
where: { id }, where: { id },
data: { destinationDocker: { connect: { id: destinationId } } } data: { destinationDocker: { connect: { id: destinationId } } }
}); });
} }
export async function configureDestinationForDatabase({ id, destinationId }) { export async function configureDestinationForDatabase({
id,
destinationId
}: DestinationConfigurationObject): Promise<void> {
await prisma.database.update({ await prisma.database.update({
where: { id }, where: { id },
data: { destinationDocker: { connect: { id: destinationId } } } data: { destinationDocker: { connect: { id: destinationId } } }
@ -48,7 +68,12 @@ export async function configureDestinationForDatabase({ id, destinationId }) {
} }
} }
} }
export async function updateDestination({ id, name, engine, network }) { export async function updateDestination({
id,
name,
engine,
network
}: Pick<DestinationDocker, 'id' | 'name' | 'engine' | 'network'>): Promise<DestinationDocker> {
return await prisma.destinationDocker.update({ where: { id }, data: { name, engine, network } }); return await prisma.destinationDocker.update({ where: { id }, data: { name, engine, network } });
} }
@ -58,13 +83,8 @@ export async function newRemoteDestination({
engine, engine,
network, network,
isCoolifyProxyUsed, isCoolifyProxyUsed,
remoteEngine, remoteEngine
ipAddress, }: CreateDockerDestination): Promise<string> {
user,
port,
sshPrivateKey
}) {
const encryptedPrivateKey = encrypt(sshPrivateKey);
const destination = await prisma.destinationDocker.create({ const destination = await prisma.destinationDocker.create({
data: { data: {
name, name,
@ -72,16 +92,18 @@ export async function newRemoteDestination({
engine, engine,
network, network,
isCoolifyProxyUsed, isCoolifyProxyUsed,
remoteEngine, remoteEngine
ipAddress,
user,
port,
sshPrivateKey: encryptedPrivateKey
} }
}); });
return destination.id; return destination.id;
} }
export async function newLocalDestination({ name, teamId, engine, network, isCoolifyProxyUsed }) { export async function newLocalDestination({
name,
teamId,
engine,
network,
isCoolifyProxyUsed
}: CreateDockerDestination): Promise<string> {
const host = getEngine(engine); const host = getEngine(engine);
const docker = dockerInstance({ destinationDocker: { engine, network } }); const docker = dockerInstance({ destinationDocker: { engine, network } });
const found = await docker.engine.listNetworks({ filters: { name: [`^${network}$`] } }); const found = await docker.engine.listNetworks({ filters: { name: [`^${network}$`] } });
@ -99,18 +121,14 @@ export async function newLocalDestination({ name, teamId, engine, network, isCoo
(destination) => destination.network !== network && destination.isCoolifyProxyUsed === true (destination) => destination.network !== network && destination.isCoolifyProxyUsed === true
); );
if (proxyConfigured) { if (proxyConfigured) {
if (proxyConfigured.isCoolifyProxyUsed) { isCoolifyProxyUsed = !!proxyConfigured.isCoolifyProxyUsed;
isCoolifyProxyUsed = true;
} else {
isCoolifyProxyUsed = false;
}
} }
await prisma.destinationDocker.updateMany({ where: { engine }, data: { isCoolifyProxyUsed } }); await prisma.destinationDocker.updateMany({ where: { engine }, data: { isCoolifyProxyUsed } });
} }
if (isCoolifyProxyUsed) await startCoolifyProxy(engine); if (isCoolifyProxyUsed) await startCoolifyProxy(engine);
return destination.id; return destination.id;
} }
export async function removeDestination({ id }) { export async function removeDestination({ id }: Pick<DestinationDocker, 'id'>): Promise<void> {
const destination = await prisma.destinationDocker.delete({ where: { id } }); const destination = await prisma.destinationDocker.delete({ where: { id } });
if (destination.isCoolifyProxyUsed) { if (destination.isCoolifyProxyUsed) {
const host = getEngine(destination.engine); const host = getEngine(destination.engine);
@ -127,8 +145,11 @@ export async function removeDestination({ id }) {
} }
} }
export async function getDestination({ id, teamId }) { export async function getDestination({
let destination = {}; id,
teamId
}: FindDestinationFromTeam): Promise<DestinationDocker & { sshPrivateKey?: string }> {
let destination;
if (teamId === '0') { if (teamId === '0') {
destination = await prisma.destinationDocker.findFirst({ destination = await prisma.destinationDocker.findFirst({
where: { id } where: { id }
@ -141,13 +162,22 @@ export async function getDestination({ id, teamId }) {
return destination; return destination;
} }
export async function getDestinationByApplicationId({ id, teamId }) { export async function getDestinationByApplicationId({
id,
teamId
}: FindDestinationFromTeam): Promise<DestinationDocker> {
return await prisma.destinationDocker.findFirst({ return await prisma.destinationDocker.findFirst({
where: { application: { some: { id } }, teams: { some: { id: teamId } } } where: { application: { some: { id } }, teams: { some: { id: teamId } } }
}); });
} }
export async function setDestinationSettings({ engine, isCoolifyProxyUsed }) { export async function setDestinationSettings({
engine,
isCoolifyProxyUsed
}: {
engine: string;
isCoolifyProxyUsed: boolean;
}): Promise<Prisma.BatchPayload> {
return await prisma.destinationDocker.updateMany({ return await prisma.destinationDocker.updateMany({
where: { engine }, where: { engine },
data: { isCoolifyProxyUsed } data: { isCoolifyProxyUsed }

View File

@ -1,7 +1,10 @@
import { decrypt, encrypt } from '$lib/crypto'; import { decrypt, encrypt } from '$lib/crypto';
import { prisma } from './common'; import { prisma } from './common';
import type { GithubApp, GitlabApp, GitSource, Prisma, Application } from '@prisma/client';
export async function listSources(teamId) { export async function listSources(
teamId: string | Prisma.StringFilter
): Promise<(GitSource & { githubApp?: GithubApp; gitlabApp?: GitlabApp })[]> {
if (teamId === '0') { if (teamId === '0') {
return await prisma.gitSource.findMany({ return await prisma.gitSource.findMany({
include: { githubApp: true, gitlabApp: true, teams: true } include: { githubApp: true, gitlabApp: true, teams: true }
@ -13,7 +16,21 @@ export async function listSources(teamId) {
}); });
} }
export async function newSource({ teamId, name }) { export async function newSource({
name,
teamId,
type,
htmlUrl,
apiUrl,
organization
}: {
name: string;
teamId: string;
type: string;
htmlUrl: string;
apiUrl: string;
organization: string;
}): Promise<GitSource> {
return await prisma.gitSource.create({ return await prisma.gitSource.create({
data: { data: {
name, name,
@ -21,7 +38,7 @@ export async function newSource({ teamId, name }) {
} }
}); });
} }
export async function removeSource({ id }) { export async function removeSource({ id }: { id: string }): Promise<void> {
const source = await prisma.gitSource.delete({ const source = await prisma.gitSource.delete({
where: { id }, where: { id },
include: { githubApp: true, gitlabApp: true } include: { githubApp: true, gitlabApp: true }
@ -30,8 +47,14 @@ export async function removeSource({ id }) {
if (source.gitlabAppId) await prisma.gitlabApp.delete({ where: { id: source.gitlabAppId } }); if (source.gitlabAppId) await prisma.gitlabApp.delete({ where: { id: source.gitlabAppId } });
} }
export async function getSource({ id, teamId }) { export async function getSource({
let body = {}; id,
teamId
}: {
id: string;
teamId: string;
}): Promise<GitSource & { githubApp: GithubApp; gitlabApp: GitlabApp }> {
let body;
if (teamId === '0') { if (teamId === '0') {
body = await prisma.gitSource.findFirst({ body = await prisma.gitSource.findFirst({
where: { id }, where: { id },
@ -80,19 +103,31 @@ export async function addGitLabSource({
appId, appId,
oauthId, oauthId,
groupName, groupName,
appSecret: encrptedAppSecret, appSecret: encryptedAppSecret,
gitSource: { connect: { id } } gitSource: { connect: { id } }
} }
}); });
} }
export async function configureGitsource({ id, gitSourceId }) { export async function configureGitsource({
id,
gitSourceId
}: {
id: string;
gitSourceId: string;
}): Promise<Application> {
return await prisma.application.update({ return await prisma.application.update({
where: { id }, where: { id },
data: { gitSource: { connect: { id: gitSourceId } } } data: { gitSource: { connect: { id: gitSourceId } } }
}); });
} }
export async function updateGitsource({ id, name, htmlUrl, apiUrl }) { export async function updateGitsource({
id,
name
}: {
id: string;
name: string;
}): Promise<GitSource> {
return await prisma.gitSource.update({ return await prisma.gitSource.update({
where: { id }, where: { id },
data: { name, htmlUrl, apiUrl } data: { name, htmlUrl, apiUrl }

View File

@ -1,7 +1,15 @@
import { decrypt, encrypt } from '$lib/crypto'; import { decrypt, encrypt } from '$lib/crypto';
import { prisma } from './common'; import { prisma } from './common';
import type { GithubApp } from '@prisma/client';
export async function addInstallation({ gitSourceId, installation_id }) { // TODO: We should change installation_id to be camelCase
export async function addInstallation({
gitSourceId,
installation_id
}: {
gitSourceId: string;
installation_id: string;
}): Promise<GithubApp> {
const source = await prisma.gitSource.findUnique({ const source = await prisma.gitSource.findUnique({
where: { id: gitSourceId }, where: { id: gitSourceId },
include: { githubApp: true } include: { githubApp: true }
@ -12,8 +20,12 @@ export async function addInstallation({ gitSourceId, installation_id }) {
}); });
} }
export async function getUniqueGithubApp({ githubAppId }) { export async function getUniqueGithubApp({
let body = await prisma.githubApp.findUnique({ where: { id: githubAppId } }); githubAppId
}: {
githubAppId: string;
}): Promise<GithubApp> {
const body = await prisma.githubApp.findUnique({ where: { id: githubAppId } });
if (body.privateKey) body.privateKey = decrypt(body.privateKey); if (body.privateKey) body.privateKey = decrypt(body.privateKey);
return body; return body;
} }
@ -26,7 +38,15 @@ export async function createGithubApp({
pem, pem,
webhook_secret, webhook_secret,
state state
}) { }: {
id: number;
client_id: string;
slug: string;
client_secret: string;
pem: string;
webhook_secret: string;
state: string;
}): Promise<GithubApp> {
const encryptedClientSecret = encrypt(client_secret); const encryptedClientSecret = encrypt(client_secret);
const encryptedWebhookSecret = encrypt(webhook_secret); const encryptedWebhookSecret = encrypt(webhook_secret);
const encryptedPem = encrypt(pem); const encryptedPem = encrypt(pem);

View File

@ -1,7 +1,14 @@
import { encrypt } from '$lib/crypto'; import { encrypt } from '$lib/crypto';
import { generateSshKeyPair, prisma } from './common'; import { generateSshKeyPair, prisma } from './common';
import type { GitlabApp } from '@prisma/client';
export async function updateDeployKey({ id, deployKeyId }) { export async function updateDeployKey({
id,
deployKeyId
}: {
id: string;
deployKeyId: number;
}): Promise<GitlabApp> {
const application = await prisma.application.findUnique({ const application = await prisma.application.findUnique({
where: { id }, where: { id },
include: { gitSource: { include: { gitlabApp: true } } } include: { gitSource: { include: { gitlabApp: true } } }
@ -11,14 +18,24 @@ export async function updateDeployKey({ id, deployKeyId }) {
data: { deployKeyId } data: { deployKeyId }
}); });
} }
export async function getSshKey({ id }) { export async function getSshKey({
id
}: {
id: string;
}): Promise<{ status: number; body: { publicKey: string } }> {
const application = await prisma.application.findUnique({ const application = await prisma.application.findUnique({
where: { id }, where: { id },
include: { gitSource: { include: { gitlabApp: true } } } include: { gitSource: { include: { gitlabApp: true } } }
}); });
return { status: 200, body: { publicKey: application.gitSource.gitlabApp.publicSshKey } }; return { status: 200, body: { publicKey: application.gitSource.gitlabApp.publicSshKey } };
} }
export async function generateSshKey({ id }) { export async function generateSshKey({
id
}: {
id: string;
}): Promise<
{ status: number; body: { publicKey: string } } | { status: number; body?: undefined }
> {
const application = await prisma.application.findUnique({ const application = await prisma.application.findUnique({
where: { id }, where: { id },
include: { gitSource: { include: { gitlabApp: true } } } include: { gitSource: { include: { gitlabApp: true } } }

View File

@ -1,6 +1,13 @@
import type { BuildLog } from '@prisma/client';
import { prisma, ErrorHandler } from './common'; import { prisma, ErrorHandler } from './common';
export async function listLogs({ buildId, last = 0 }) { export async function listLogs({
buildId,
last = 0
}: {
buildId: string;
last: number;
}): Promise<BuildLog[] | { status: number; body: { message: string; error: string } }> {
try { try {
const body = await prisma.buildLog.findMany({ const body = await prisma.buildLog.findMany({
where: { buildId, time: { gt: last } }, where: { buildId, time: { gt: last } },

View File

@ -1,7 +1,8 @@
import { encrypt, decrypt } from '$lib/crypto'; import { encrypt, decrypt } from '$lib/crypto';
import { prisma } from './common'; import { prisma } from './common';
import type { ServiceSecret, Secret, Prisma } from '@prisma/client';
export async function listServiceSecrets(serviceId: string) { export async function listServiceSecrets(serviceId: string): Promise<ServiceSecret[]> {
let secrets = await prisma.serviceSecret.findMany({ let secrets = await prisma.serviceSecret.findMany({
where: { serviceId }, where: { serviceId },
orderBy: { createdAt: 'desc' } orderBy: { createdAt: 'desc' }
@ -14,7 +15,7 @@ export async function listServiceSecrets(serviceId: string) {
return secrets; return secrets;
} }
export async function listSecrets(applicationId: string) { export async function listSecrets(applicationId: string): Promise<Secret[]> {
let secrets = await prisma.secret.findMany({ let secrets = await prisma.secret.findMany({
where: { applicationId }, where: { applicationId },
orderBy: { createdAt: 'desc' } orderBy: { createdAt: 'desc' }
@ -27,20 +28,48 @@ export async function listSecrets(applicationId: string) {
return secrets; return secrets;
} }
export async function createServiceSecret({ id, name, value }) { export async function createServiceSecret({
id,
name,
value
}: {
id: string;
name: string;
value: string;
}): Promise<ServiceSecret> {
value = encrypt(value); value = encrypt(value);
return await prisma.serviceSecret.create({ return await prisma.serviceSecret.create({
data: { name, value, service: { connect: { id } } } data: { name, value, service: { connect: { id } } }
}); });
} }
export async function createSecret({ id, name, value, isBuildSecret, isPRMRSecret }) { export async function createSecret({
id,
name,
value,
isBuildSecret,
isPRMRSecret
}: {
id: string;
name: string;
value: string;
isBuildSecret: boolean;
isPRMRSecret: boolean;
}): Promise<Secret> {
value = encrypt(value); value = encrypt(value);
return await prisma.secret.create({ return await prisma.secret.create({
data: { name, value, isBuildSecret, isPRMRSecret, application: { connect: { id } } } data: { name, value, isBuildSecret, isPRMRSecret, application: { connect: { id } } }
}); });
} }
export async function updateServiceSecret({ id, name, value }) { export async function updateServiceSecret({
id,
name,
value
}: {
id: string;
name: string;
value: string;
}): Promise<Prisma.BatchPayload | ServiceSecret> {
value = encrypt(value); value = encrypt(value);
const found = await prisma.serviceSecret.findFirst({ where: { serviceId: id, name } }); const found = await prisma.serviceSecret.findFirst({ where: { serviceId: id, name } });
@ -55,7 +84,19 @@ export async function updateServiceSecret({ id, name, value }) {
}); });
} }
} }
export async function updateSecret({ id, name, value, isBuildSecret, isPRMRSecret }) { export async function updateSecret({
id,
name,
value,
isBuildSecret,
isPRMRSecret
}: {
id: string;
name: string;
value: string;
isBuildSecret: boolean;
isPRMRSecret: boolean;
}): Promise<Prisma.BatchPayload | Secret> {
value = encrypt(value); value = encrypt(value);
const found = await prisma.secret.findFirst({ where: { applicationId: id, name, isPRMRSecret } }); const found = await prisma.secret.findFirst({ where: { applicationId: id, name, isPRMRSecret } });
@ -71,10 +112,22 @@ export async function updateSecret({ id, name, value, isBuildSecret, isPRMRSecre
} }
} }
export async function removeServiceSecret({ id, name }) { export async function removeServiceSecret({
id,
name
}: {
id: string;
name: string;
}): Promise<Prisma.BatchPayload> {
return await prisma.serviceSecret.deleteMany({ where: { serviceId: id, name } }); return await prisma.serviceSecret.deleteMany({ where: { serviceId: id, name } });
} }
export async function removeSecret({ id, name }) { export async function removeSecret({
id,
name
}: {
id: string;
name: string;
}): Promise<Prisma.BatchPayload> {
return await prisma.secret.deleteMany({ where: { applicationId: id, name } }); return await prisma.secret.deleteMany({ where: { applicationId: id, name } });
} }

View File

@ -1,10 +1,10 @@
import { asyncExecShell, getEngine } from '$lib/common';
import { decrypt, encrypt } from '$lib/crypto'; import { decrypt, encrypt } from '$lib/crypto';
import type { Minio, Service } from '@prisma/client';
import cuid from 'cuid'; import cuid from 'cuid';
import { generatePassword } from '.'; import { generatePassword } from '.';
import { prisma } from './common'; import { prisma } from './common';
export async function listServices(teamId) { export async function listServices(teamId: string): Promise<Service[]> {
if (teamId === '0') { if (teamId === '0') {
return await prisma.service.findMany({ include: { teams: true } }); return await prisma.service.findMany({ include: { teams: true } });
} else { } else {
@ -15,12 +15,18 @@ export async function listServices(teamId) {
} }
} }
export async function newService({ name, teamId }) { export async function newService({
name,
teamId
}: {
name: string;
teamId: string;
}): Promise<Service> {
return await prisma.service.create({ data: { name, teams: { connect: { id: teamId } } } }); return await prisma.service.create({ data: { name, teams: { connect: { id: teamId } } } });
} }
export async function getService({ id, teamId }) { export async function getService({ id, teamId }: { id: string; teamId: string }): Promise<Service> {
let body = {}; let body;
const include = { const include = {
destinationDocker: true, destinationDocker: true,
plausibleAnalytics: true, plausibleAnalytics: true,
@ -83,7 +89,13 @@ export async function getService({ id, teamId }) {
return { ...body, settings }; return { ...body, settings };
} }
export async function configureServiceType({ id, type }) { export async function configureServiceType({
id,
type
}: {
id: string;
type: string;
}): Promise<void> {
if (type === 'plausibleanalytics') { if (type === 'plausibleanalytics') {
const password = encrypt(generatePassword()); const password = encrypt(generatePassword());
const postgresqlUser = cuid(); const postgresqlUser = cuid();
@ -199,44 +211,157 @@ export async function configureServiceType({ id, type }) {
}); });
} }
} }
export async function setServiceVersion({ id, version }) {
export async function setServiceVersion({
id,
version
}: {
id: string;
version: string;
}): Promise<Service> {
return await prisma.service.update({ return await prisma.service.update({
where: { id }, where: { id },
data: { version } data: { version }
}); });
} }
export async function setServiceSettings({ id, dualCerts }) { export async function setServiceSettings({
id,
dualCerts
}: {
id: string;
dualCerts: boolean;
}): Promise<Service> {
return await prisma.service.update({ return await prisma.service.update({
where: { id }, where: { id },
data: { dualCerts } data: { dualCerts }
}); });
} }
export async function updatePlausibleAnalyticsService({ id, fqdn, email, username, name }) { export async function updatePlausibleAnalyticsService({
id,
fqdn,
email,
username,
name
}: {
id: string;
fqdn: string;
name: string;
email: string;
username: string;
}): Promise<void> {
await prisma.plausibleAnalytics.update({ where: { serviceId: id }, data: { email, username } }); await prisma.plausibleAnalytics.update({ where: { serviceId: id }, data: { email, username } });
await prisma.service.update({ where: { id }, data: { name, fqdn } }); await prisma.service.update({ where: { id }, data: { name, fqdn } });
} }
export async function updateService({ id, fqdn, name }) {
export async function updateService({
id,
fqdn,
name
}: {
id: string;
fqdn: string;
name: string;
}): Promise<Service> {
return await prisma.service.update({ where: { id }, data: { fqdn, name } }); return await prisma.service.update({ where: { id }, data: { fqdn, name } });
} }
export async function updateWordpress({ id, fqdn, name, mysqlDatabase, extraConfig }) {
export async function updateLanguageToolService({
id,
fqdn,
name
}: {
id: string;
fqdn: string;
name: string;
}): Promise<Service> {
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
}
export async function updateMeiliSearchService({
id,
fqdn,
name
}: {
id: string;
fqdn: string;
name: string;
}): Promise<Service> {
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
}
export async function updateVaultWardenService({
id,
fqdn,
name
}: {
id: string;
fqdn: string;
name: string;
}): Promise<Service> {
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
}
export async function updateVsCodeServer({
id,
fqdn,
name
}: {
id: string;
fqdn: string;
name: string;
}): Promise<Service> {
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
}
export async function updateWordpress({
id,
fqdn,
name,
mysqlDatabase,
extraConfig
}: {
id: string;
fqdn: string;
name: string;
mysqlDatabase: string;
extraConfig: string;
}): Promise<Service> {
return await prisma.service.update({ return await prisma.service.update({
where: { id }, where: { id },
data: { fqdn, name, wordpress: { update: { mysqlDatabase, extraConfig } } } data: { fqdn, name, wordpress: { update: { mysqlDatabase, extraConfig } } }
}); });
} }
export async function updateMinioService({ id, publicPort }) {
export async function updateMinioService({
id,
publicPort
}: {
id: string;
publicPort: number;
}): Promise<Minio> {
return await prisma.minio.update({ where: { serviceId: id }, data: { publicPort } }); return await prisma.minio.update({ where: { serviceId: id }, data: { publicPort } });
} }
export async function updateGhostService({ id, fqdn, name, mariadbDatabase }) {
export async function updateGhostService({
id,
fqdn,
name,
mariadbDatabase
}: {
id: string;
fqdn: string;
name: string;
mariadbDatabase: string;
}): Promise<Service> {
return await prisma.service.update({ return await prisma.service.update({
where: { id }, where: { id },
data: { fqdn, name, ghost: { update: { mariadbDatabase } } } data: { fqdn, name, ghost: { update: { mariadbDatabase } } }
}); });
} }
export async function removeService({ id }) { export async function removeService({ id }: { id: string }): Promise<void> {
await prisma.meiliSearch.deleteMany({ where: { serviceId: id } }); await prisma.meiliSearch.deleteMany({ where: { serviceId: id } });
await prisma.ghost.deleteMany({ where: { serviceId: id } }); await prisma.ghost.deleteMany({ where: { serviceId: id } });
await prisma.plausibleAnalytics.deleteMany({ where: { serviceId: id } }); await prisma.plausibleAnalytics.deleteMany({ where: { serviceId: id } });

View File

@ -1,8 +1,9 @@
import { decrypt } from '$lib/crypto'; import { decrypt } from '$lib/crypto';
import { prisma } from './common'; import { prisma } from './common';
import type { Setting } from '@prisma/client';
export async function listSettings() { export async function listSettings(): Promise<Setting> {
let settings = await prisma.setting.findFirst({}); const settings = await prisma.setting.findFirst({});
if (settings.proxyPassword) settings.proxyPassword = decrypt(settings.proxyPassword); if (settings.proxyPassword) settings.proxyPassword = decrypt(settings.proxyPassword);
return settings; return settings;
} }

View File

@ -1,9 +1,10 @@
import type { Team, Permission } from '@prisma/client';
import { prisma } from './common'; import { prisma } from './common';
export async function listTeams() { export async function listTeams(): Promise<Team[]> {
return await prisma.team.findMany(); return await prisma.team.findMany();
} }
export async function newTeam({ name, userId }) { export async function newTeam({ name, userId }: { name: string; userId: string }): Promise<Team> {
return await prisma.team.create({ return await prisma.team.create({
data: { data: {
name, name,
@ -12,7 +13,11 @@ export async function newTeam({ name, userId }) {
} }
}); });
} }
export async function getMyTeams({ userId }) { export async function getMyTeams({
userId
}: {
userId: string;
}): Promise<(Permission & { team: Team & { _count: { users: number } } })[]> {
return await prisma.permission.findMany({ return await prisma.permission.findMany({
where: { userId }, where: { userId },
include: { team: { include: { _count: { select: { users: true } } } } } include: { team: { include: { _count: { select: { users: true } } } } }

View File

@ -3,14 +3,28 @@ import bcrypt from 'bcryptjs';
import { prisma } from './common'; import { prisma } from './common';
import { asyncExecShell, uniqueName } from '$lib/common'; import { asyncExecShell, uniqueName } from '$lib/common';
import * as db from '$lib/database'; import * as db from '$lib/database';
import { startCoolifyProxy } from '$lib/haproxy'; import { startCoolifyProxy } from '$lib/haproxy';
export async function hashPassword(password: string) { import type { User } from '@prisma/client';
export async function hashPassword(password: string): Promise<string> {
const saltRounds = 15; const saltRounds = 15;
return bcrypt.hash(password, saltRounds); return bcrypt.hash(password, saltRounds);
} }
export async function login({ email, password, isLogin }) {
export async function login({
email,
password,
isLogin
}: {
email: string;
password: string;
isLogin: boolean;
}): Promise<{
status: number;
headers: { 'Set-Cookie': string };
body: { userId: string; teamId: string; permission: string; isAdmin: boolean };
}> {
const users = await prisma.user.count(); const users = await prisma.user.count();
const userFound = await prisma.user.findUnique({ const userFound = await prisma.user.findUnique({
where: { email }, where: { email },
@ -140,6 +154,6 @@ export async function login({ email, password, isLogin }) {
}; };
} }
export async function getUser({ userId }) { export async function getUser({ userId }: { userId: string }): Promise<User> {
return await prisma.user.findUnique({ where: { id: userId } }); return await prisma.user.findUnique({ where: { id: userId } });
} }

View File

@ -1,5 +1,6 @@
import { toast } from '@zerodevx/svelte-toast'; import { toast } from '@zerodevx/svelte-toast';
export function errorNotification(message: string) {
export function errorNotification(message: string): void {
console.error(message); console.error(message);
if (typeof message !== 'string') { if (typeof message !== 'string') {
toast.push('Ooops, something is not okay, are you okay?'); toast.push('Ooops, something is not okay, are you okay?');
@ -30,7 +31,7 @@ export function enhance(
e.preventDefault(); e.preventDefault();
let body = new FormData(form); let body = new FormData(form);
let parsedData = body; const parsedData = body;
body.forEach((data, key) => { body.forEach((data, key) => {
if (data === '' || data === null) parsedData.delete(key); if (data === '' || data === null) parsedData.delete(key);

View File

@ -1,8 +1,5 @@
import { dev } from '$app/env'; import { dev } from '$app/env';
import got from 'got'; import got, { type Got } from 'got';
import mustache from 'mustache';
import crypto from 'crypto';
import * as db from '$lib/database'; import * as db from '$lib/database';
import { checkContainer, checkHAProxy } from '.'; import { checkContainer, checkHAProxy } from '.';
import { asyncExecShell, getDomain, getEngine } from '$lib/common'; import { asyncExecShell, getDomain, getEngine } from '$lib/common';
@ -10,7 +7,7 @@ import { supportedServiceTypesAndVersions } from '$lib/components/common';
const url = dev ? 'http://localhost:5555' : 'http://coolify-haproxy:5555'; const url = dev ? 'http://localhost:5555' : 'http://coolify-haproxy:5555';
let template = `program api const template = `program api
command /usr/bin/dataplaneapi -f /usr/local/etc/haproxy/dataplaneapi.hcl --userlist haproxy-dataplaneapi command /usr/bin/dataplaneapi -f /usr/local/etc/haproxy/dataplaneapi.hcl --userlist haproxy-dataplaneapi
no option start-on-reload no option start-on-reload
@ -128,7 +125,8 @@ backend {{domain}}
server {{id}} {{id}}:{{port}} check fall 10 server {{id}} {{id}}:{{port}} check fall 10
{{/coolify}} {{/coolify}}
`; `;
export async function haproxyInstance() {
export async function haproxyInstance(): Promise<Got> {
const { proxyPassword } = await db.listSettings(); const { proxyPassword } = await db.listSettings();
return got.extend({ return got.extend({
prefixUrl: url, prefixUrl: url,
@ -137,11 +135,10 @@ export async function haproxyInstance() {
}); });
} }
export async function configureHAProxy() { export async function configureHAProxy(): Promise<void> {
const haproxy = await haproxyInstance(); const haproxy = await haproxyInstance();
await checkHAProxy(haproxy); await checkHAProxy(haproxy);
try {
const data = { const data = {
applications: [], applications: [],
services: [], services: [],
@ -192,7 +189,7 @@ export async function configureHAProxy() {
.map((c) => c.replace(/"/g, '')); .map((c) => c.replace(/"/g, ''));
if (containers.length > 0) { if (containers.length > 0) {
for (const container of containers) { for (const container of containers) {
let previewDomain = `${container.split('-')[1]}.${domain}`; const previewDomain = `${container.split('-')[1]}.${domain}`;
data.applications.push({ data.applications.push({
id: container, id: container,
port: port || 3000, port: port || 3000,
@ -265,23 +262,36 @@ export async function configureHAProxy() {
redirectValue, redirectValue,
redirectTo: isWWW ? domain.replace('www.', '') : 'www.' + domain redirectTo: isWWW ? domain.replace('www.', '') : 'www.' + domain
}); });
} for (const service of services) {
const output = mustache.render(template, data); const { fqdn, id, type, destinationDocker, destinationDockerId, updatedAt } = service;
const newHash = crypto.createHash('md5').update(output).digest('hex'); if (destinationDockerId) {
const { proxyHash, id } = await db.listSettings(); const { engine } = destinationDocker;
if (proxyHash !== newHash) { const found = supportedServiceTypesAndVersions.find((a) => a.name === type);
await db.prisma.setting.update({ where: { id }, data: { proxyHash: newHash } }); if (found) {
await haproxy.post(`v2/services/haproxy/configuration/raw`, { const port = found.ports.main;
searchParams: { const publicPort = service[type]?.publicPort;
skip_version: true const isRunning = await checkContainer(engine, id);
}, if (fqdn) {
body: output, const domain = getDomain(fqdn);
headers: { const isHttps = fqdn.startsWith('https://');
'Content-Type': 'text/plain' const isWWW = fqdn.includes('www.');
} const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`;
if (isRunning) {
data.services.push({
id,
port,
publicPort,
domain,
isRunning,
isHttps,
redirectValue,
redirectTo: isWWW ? domain.replace('www.', '') : 'www.' + domain,
updatedAt: updatedAt.getTime()
}); });
} }
} catch (error) { }
throw error; }
}
}
} }
} }

View File

@ -1,7 +1,8 @@
import { dev } from '$app/env'; import { dev } from '$app/env';
import { asyncExecShell, getEngine } from '$lib/common'; import { asyncExecShell, getEngine } from '$lib/common';
import got from 'got'; import got, { type Got, type Response } from 'got';
import * as db from '$lib/database'; import * as db from '$lib/database';
import type { DestinationDocker } from '@prisma/client';
const url = dev ? 'http://localhost:5555' : 'http://coolify-haproxy:5555'; const url = dev ? 'http://localhost:5555' : 'http://coolify-haproxy:5555';
@ -9,7 +10,7 @@ export const defaultProxyImage = `coolify-haproxy-alpine:latest`;
export const defaultProxyImageTcp = `coolify-haproxy-tcp-alpine:latest`; export const defaultProxyImageTcp = `coolify-haproxy-tcp-alpine:latest`;
export const defaultProxyImageHttp = `coolify-haproxy-http-alpine:latest`; export const defaultProxyImageHttp = `coolify-haproxy-http-alpine:latest`;
export async function haproxyInstance() { export async function haproxyInstance(): Promise<Got> {
const { proxyPassword } = await db.listSettings(); const { proxyPassword } = await db.listSettings();
return got.extend({ return got.extend({
prefixUrl: url, prefixUrl: url,
@ -17,6 +18,7 @@ export async function haproxyInstance() {
password: proxyPassword password: proxyPassword
}); });
} }
export async function getRawConfiguration(): Promise<RawHaproxyConfiguration> { export async function getRawConfiguration(): Promise<RawHaproxyConfiguration> {
return await (await haproxyInstance()).get(`v2/services/haproxy/configuration/raw`).json(); return await (await haproxyInstance()).get(`v2/services/haproxy/configuration/raw`).json();
} }
@ -43,11 +45,12 @@ export async function getNextTransactionId(): Promise<string> {
return newTransaction.id; return newTransaction.id;
} }
export async function completeTransaction(transactionId) { export async function completeTransaction(transactionId: string): Promise<Response<string>> {
const haproxy = await haproxyInstance(); const haproxy = await haproxyInstance();
return await haproxy.put(`v2/services/haproxy/transactions/${transactionId}`); return await haproxy.put(`v2/services/haproxy/transactions/${transactionId}`);
} }
export async function deleteProxy({ id }) {
export async function deleteProxy({ id }: { id: string }): Promise<void> {
const haproxy = await haproxyInstance(); const haproxy = await haproxyInstance();
await checkHAProxy(haproxy); await checkHAProxy(haproxy);
@ -77,11 +80,12 @@ export async function deleteProxy({ id }) {
} }
} }
export async function reloadHaproxy(engine) { export async function reloadHaproxy(engine: string): Promise<{ stdout: string; stderr: string }> {
const host = getEngine(engine); const host = getEngine(engine);
return await asyncExecShell(`DOCKER_HOST=${host} docker exec coolify-haproxy kill -HUP 1`); return await asyncExecShell(`DOCKER_HOST=${host} docker exec coolify-haproxy kill -HUP 1`);
} }
export async function checkHAProxy(haproxy?: any) {
export async function checkHAProxy(haproxy?: Got): Promise<void> {
if (!haproxy) haproxy = await haproxyInstance(); if (!haproxy) haproxy = await haproxyInstance();
try { try {
await haproxy.get('v2/info'); await haproxy.get('v2/info');
@ -93,7 +97,10 @@ export async function checkHAProxy(haproxy?: any) {
} }
} }
export async function stopTcpHttpProxy(destinationDocker, publicPort) { export async function stopTcpHttpProxy(
destinationDocker: DestinationDocker,
publicPort: number
): Promise<{ stdout: string; stderr: string } | Error> {
const { engine } = destinationDocker; const { engine } = destinationDocker;
const host = getEngine(engine); const host = getEngine(engine);
const containerName = `haproxy-for-${publicPort}`; const containerName = `haproxy-for-${publicPort}`;
@ -108,7 +115,13 @@ export async function stopTcpHttpProxy(destinationDocker, publicPort) {
return error; return error;
} }
} }
export async function startTcpProxy(destinationDocker, id, publicPort, privatePort, volume = null) { export async function startTcpProxy(
destinationDocker: DestinationDocker,
id: string,
publicPort: number,
privatePort: number,
volume?: string
): Promise<{ stdout: string; stderr: string } | Error> {
const { network, engine } = destinationDocker; const { network, engine } = destinationDocker;
const host = getEngine(engine); const host = getEngine(engine);
@ -132,7 +145,13 @@ export async function startTcpProxy(destinationDocker, id, publicPort, privatePo
return error; return error;
} }
} }
export async function startHttpProxy(destinationDocker, id, publicPort, privatePort) {
export async function startHttpProxy(
destinationDocker: DestinationDocker,
id: string,
publicPort: number,
privatePort: number
): Promise<{ stdout: string; stderr: string } | Error> {
const { network, engine } = destinationDocker; const { network, engine } = destinationDocker;
const host = getEngine(engine); const host = getEngine(engine);
@ -154,7 +173,8 @@ export async function startHttpProxy(destinationDocker, id, publicPort, privateP
return error; return error;
} }
} }
export async function startCoolifyProxy(engine) {
export async function startCoolifyProxy(engine: string): Promise<void> {
const host = getEngine(engine); const host = getEngine(engine);
const found = await checkContainer(engine, 'coolify-haproxy'); const found = await checkContainer(engine, 'coolify-haproxy');
const { proxyPassword, proxyUser, id } = await db.listSettings(); const { proxyPassword, proxyUser, id } = await db.listSettings();
@ -170,7 +190,8 @@ export async function startCoolifyProxy(engine) {
} }
await configureNetworkCoolifyProxy(engine); await configureNetworkCoolifyProxy(engine);
} }
export async function checkContainer(engine, container) {
export async function checkContainer(engine: string, container: string): Promise<boolean> {
const host = getEngine(engine); const host = getEngine(engine);
let containerFound = false; let containerFound = false;
@ -180,7 +201,7 @@ export async function checkContainer(engine, container) {
); );
const parsedStdout = JSON.parse(stdout); const parsedStdout = JSON.parse(stdout);
const status = parsedStdout.Status; const status = parsedStdout.Status;
const isRunning = status === 'running' ? true : false; const isRunning = status === 'running';
if (status === 'exited' || status === 'created') { if (status === 'exited' || status === 'created') {
await asyncExecShell(`DOCKER_HOST="${host}" docker rm ${container}`); await asyncExecShell(`DOCKER_HOST="${host}" docker rm ${container}`);
} }
@ -193,7 +214,9 @@ export async function checkContainer(engine, container) {
return containerFound; return containerFound;
} }
export async function stopCoolifyProxy(engine) { export async function stopCoolifyProxy(
engine: string
): Promise<{ stdout: string; stderr: string } | Error> {
const host = getEngine(engine); const host = getEngine(engine);
const found = await checkContainer(engine, 'coolify-haproxy'); const found = await checkContainer(engine, 'coolify-haproxy');
await db.setDestinationSettings({ engine, isCoolifyProxyUsed: false }); await db.setDestinationSettings({ engine, isCoolifyProxyUsed: false });
@ -210,16 +233,12 @@ export async function stopCoolifyProxy(engine) {
} }
} }
export async function configureNetworkCoolifyProxy(engine) { export async function configureNetworkCoolifyProxy(engine: string): Promise<void> {
const host = getEngine(engine); const host = getEngine(engine);
const destinations = await db.prisma.destinationDocker.findMany({ where: { engine } }); const destinations = await db.prisma.destinationDocker.findMany({ where: { engine } });
destinations.forEach(async (destination) => { for (const destination of destinations) {
try {
await asyncExecShell( await asyncExecShell(
`DOCKER_HOST="${host}" docker network connect ${destination.network} coolify-haproxy` `DOCKER_HOST="${host}" docker network connect ${destination.network} coolify-haproxy`
); );
} catch (err) {
// TODO: handle error
} }
});
} }

View File

@ -2,11 +2,9 @@ import { asyncExecShell, saveBuildLog } from '$lib/common';
import got from 'got'; import got from 'got';
import jsonwebtoken from 'jsonwebtoken'; import jsonwebtoken from 'jsonwebtoken';
import * as db from '$lib/database'; import * as db from '$lib/database';
import { ErrorHandler } from '$lib/database';
export default async function ({ export default async function ({
applicationId, applicationId,
debug,
workdir, workdir,
githubAppId, githubAppId,
repository, repository,
@ -14,7 +12,16 @@ export default async function ({
htmlUrl, htmlUrl,
branch, branch,
buildId buildId
}): Promise<any> { }: {
applicationId: string;
workdir: string;
githubAppId: string;
repository: string;
apiUrl: string;
htmlUrl: string;
branch: string;
buildId: string;
}): Promise<string> {
const url = htmlUrl.replace('https://', '').replace('http://', ''); const url = htmlUrl.replace('https://', '').replace('http://', '');
await saveBuildLog({ line: 'GitHub importer started.', buildId, applicationId }); await saveBuildLog({ line: 'GitHub importer started.', buildId, applicationId });
const { privateKey, appId, installationId } = await db.getUniqueGithubApp({ githubAppId }); const { privateKey, appId, installationId } = await db.getUniqueGithubApp({ githubAppId });

View File

@ -9,7 +9,16 @@ export default async function ({
branch, branch,
buildId, buildId,
privateSshKey privateSshKey
}): Promise<any> { }: {
applicationId: string;
workdir: string;
repository: string;
htmlUrl: string;
branch: string;
buildId: string;
repodir: string;
privateSshKey: string;
}): Promise<string> {
const url = htmlUrl.replace('https://', '').replace('http://', '').replace(/\/$/, ''); const url = htmlUrl.replace('https://', '').replace('http://', '').replace(/\/$/, '');
await saveBuildLog({ line: 'GitLab importer started.', buildId, applicationId }); await saveBuildLog({ line: 'GitLab importer started.', buildId, applicationId });
await asyncExecShell(`echo '${privateSshKey}' > ${repodir}/id.rsa`); await asyncExecShell(`echo '${privateSshKey}' > ${repodir}/id.rsa`);

View File

@ -7,7 +7,7 @@ import fs from 'fs/promises';
import getPort, { portNumbers } from 'get-port'; import getPort, { portNumbers } from 'get-port';
import { supportedServiceTypesAndVersions } from '$lib/components/common'; import { supportedServiceTypesAndVersions } from '$lib/components/common';
export async function letsEncrypt(domain, id = null, isCoolify = false) { export async function letsEncrypt(domain: string, id?: string, isCoolify = false): Promise<void> {
try { try {
const data = await db.prisma.setting.findFirst(); const data = await db.prisma.setting.findFirst();
const { minPort, maxPort } = data; const { minPort, maxPort } = data;
@ -98,7 +98,7 @@ export async function letsEncrypt(domain, id = null, isCoolify = false) {
} }
} }
export async function generateSSLCerts() { export async function generateSSLCerts(): Promise<void> {
const ssls = []; const ssls = [];
const applications = await db.prisma.application.findMany({ const applications = await db.prisma.application.findMany({
include: { destinationDocker: true, settings: true }, include: { destinationDocker: true, settings: true },
@ -131,7 +131,7 @@ export async function generateSSLCerts() {
.map((c) => c.replace(/"/g, '')); .map((c) => c.replace(/"/g, ''));
if (containers.length > 0) { if (containers.length > 0) {
for (const container of containers) { for (const container of containers) {
let previewDomain = `${container.split('-')[1]}.${domain}`; const previewDomain = `${container.split('-')[1]}.${domain}`;
if (isHttps) ssls.push({ domain: previewDomain, id, isCoolify: false }); if (isHttps) ssls.push({ domain: previewDomain, id, isCoolify: false });
} }
} }

View File

@ -20,27 +20,22 @@ import {
setDefaultConfiguration setDefaultConfiguration
} from '$lib/buildPacks/common'; } from '$lib/buildPacks/common';
import yaml from 'js-yaml'; import yaml from 'js-yaml';
import type { Job } from 'bullmq';
import type { BuilderJob } from '$lib/types/builderJob';
import type { ComposeFile } from '$lib/types/composeFile'; import type { ComposeFile } from '$lib/types/composeFile';
export default async function (job) { export default async function (job: Job<BuilderJob, void, string>): Promise<void> {
let { const {
id: applicationId, id: applicationId,
repository, repository,
branch,
buildPack,
name, name,
destinationDocker, destinationDocker,
destinationDockerId, destinationDockerId,
gitSource, gitSource,
build_id: buildId, build_id: buildId,
configHash, configHash,
port,
installCommand,
buildCommand,
startCommand,
fqdn, fqdn,
baseDirectory,
publishDirectory,
projectId, projectId,
secrets, secrets,
phpModules, phpModules,
@ -53,6 +48,16 @@ export default async function (job) {
pythonModule, pythonModule,
pythonVariable pythonVariable
} = job.data; } = job.data;
let {
branch,
buildPack,
port,
installCommand,
buildCommand,
startCommand,
baseDirectory,
publishDirectory
} = job.data;
const { debug } = settings; const { debug } = settings;
await asyncSleep(500); await asyncSleep(500);
@ -67,7 +72,7 @@ export default async function (job) {
}); });
let imageId = applicationId; let imageId = applicationId;
let domain = getDomain(fqdn); let domain = getDomain(fqdn);
let volumes = const volumes =
persistentStorage?.map((storage) => { persistentStorage?.map((storage) => {
return `${applicationId}${storage.path.replace(/\//gi, '-')}:${ return `${applicationId}${storage.path.replace(/\//gi, '-')}:${
buildPack !== 'docker' ? '/app' : '' buildPack !== 'docker' ? '/app' : ''
@ -103,7 +108,7 @@ export default async function (job) {
publishDirectory = configuration.publishDirectory; publishDirectory = configuration.publishDirectory;
baseDirectory = configuration.baseDirectory; baseDirectory = configuration.baseDirectory;
let commit = await importers[gitSource.type]({ const commit = await importers[gitSource.type]({
applicationId, applicationId,
debug, debug,
workdir, workdir,
@ -210,9 +215,7 @@ export default async function (job) {
await saveBuildLog({ line: `Build pack ${buildPack} not found`, buildId, applicationId }); await saveBuildLog({ line: `Build pack ${buildPack} not found`, buildId, applicationId });
throw new Error(`Build pack ${buildPack} not found.`); throw new Error(`Build pack ${buildPack} not found.`);
} }
deployNeeded = true;
} else { } else {
deployNeeded = false;
await saveBuildLog({ line: 'Nothing changed.', buildId, applicationId }); await saveBuildLog({ line: 'Nothing changed.', buildId, applicationId });
} }

View File

@ -1,8 +1,6 @@
import { dev } from '$app/env';
import { asyncExecShell, getEngine, version } from '$lib/common'; import { asyncExecShell, getEngine, version } from '$lib/common';
import { prisma } from '$lib/database'; import { prisma } from '$lib/database';
import { defaultProxyImageHttp, defaultProxyImageTcp } from '$lib/haproxy'; export default async function (): Promise<void> {
export default async function () {
const destinationDockers = await prisma.destinationDocker.findMany(); const destinationDockers = await prisma.destinationDocker.findMany();
for (const destinationDocker of destinationDockers) { for (const destinationDocker of destinationDockers) {
const host = getEngine(destinationDocker.engine); const host = getEngine(destinationDocker.engine);

View File

@ -1,11 +1,8 @@
import * as Bullmq from 'bullmq'; import * as Bullmq from 'bullmq';
import { default as ProdBullmq, Job, QueueEvents, QueueScheduler } from 'bullmq'; import { default as ProdBullmq, QueueScheduler } from 'bullmq';
import cuid from 'cuid';
import { dev } from '$app/env'; import { dev } from '$app/env';
import { prisma } from '$lib/database'; import { prisma } from '$lib/database';
import builder from './builder'; import builder from './builder';
import logger from './logger';
import cleanup from './cleanup'; import cleanup from './cleanup';
import proxy from './proxy'; import proxy from './proxy';
import ssl from './ssl'; import ssl from './ssl';
@ -28,7 +25,7 @@ const connectionOptions = {
} }
}; };
const cron = async () => { const cron = async (): Promise<void> => {
new QueueScheduler('proxy', connectionOptions); new QueueScheduler('proxy', connectionOptions);
new QueueScheduler('cleanup', connectionOptions); new QueueScheduler('cleanup', connectionOptions);
new QueueScheduler('ssl', connectionOptions); new QueueScheduler('ssl', connectionOptions);
@ -89,18 +86,6 @@ const cron = async () => {
await queue.ssl.add('ssl', {}, { repeat: { every: dev ? 10000 : 60000 } }); await queue.ssl.add('ssl', {}, { repeat: { every: dev ? 10000 : 60000 } });
if (!dev) await queue.cleanup.add('cleanup', {}, { repeat: { every: 300000 } }); if (!dev) await queue.cleanup.add('cleanup', {}, { repeat: { every: 300000 } });
await queue.sslRenew.add('sslRenew', {}, { repeat: { every: 1800000 } }); await queue.sslRenew.add('sslRenew', {}, { repeat: { every: 1800000 } });
const events = {
proxy: new QueueEvents('proxy', { ...connectionOptions }),
ssl: new QueueEvents('ssl', { ...connectionOptions })
};
events.proxy.on('completed', (data) => {
// console.log(data)
});
events.ssl.on('completed', (data) => {
// console.log(data)
});
}; };
cron().catch((error) => { cron().catch((error) => {
console.log('cron failed to start'); console.log('cron failed to start');
@ -157,9 +142,5 @@ buildWorker.on('failed', async (job: Bullmq.Job, failedReason) => {
const buildLogQueueName = 'log_queue'; const buildLogQueueName = 'log_queue';
const buildLogQueue = new Queue(buildLogQueueName, connectionOptions); const buildLogQueue = new Queue(buildLogQueueName, connectionOptions);
const buildLogWorker = new Worker(buildLogQueueName, async (job) => await logger(job), {
concurrency: 1,
...connectionOptions
});
export { buildQueue, buildLogQueue }; export { buildQueue, buildLogQueue };

View File

@ -1,7 +1,8 @@
import { prisma } from '$lib/database'; import { prisma } from '$lib/database';
import { dev } from '$app/env'; import { dev } from '$app/env';
import type { Job } from 'bullmq';
export default async function (job) { export default async function (job: Job): Promise<void> {
const { line, applicationId, buildId } = job.data; const { line, applicationId, buildId } = job.data;
if (dev) console.debug(`[${applicationId}] ${line}`); if (dev) console.debug(`[${applicationId}] ${line}`);
await prisma.buildLog.create({ data: { line, buildId, time: Number(job.id), applicationId } }); await prisma.buildLog.create({ data: { line, buildId, time: Number(job.id), applicationId } });

View File

@ -1,7 +1,10 @@
import { ErrorHandler } from '$lib/database'; import { ErrorHandler } from '$lib/database';
import { configureHAProxy } from '$lib/haproxy/configuration'; import { configureHAProxy } from '$lib/haproxy/configuration';
export default async function () { export default async function (): Promise<void | {
status: number;
body: { message: string; error: string };
}> {
try { try {
return await configureHAProxy(); return await configureHAProxy();
} catch (error) { } catch (error) {

View File

@ -1,6 +1,6 @@
import { generateSSLCerts } from '$lib/letsencrypt'; import { generateSSLCerts } from '$lib/letsencrypt';
export default async function () { export default async function (): Promise<void> {
try { try {
return await generateSSLCerts(); return await generateSSLCerts();
} catch (error) { } catch (error) {

View File

@ -1,13 +1,9 @@
import { asyncExecShell } from '$lib/common'; import { asyncExecShell } from '$lib/common';
import { reloadHaproxy } from '$lib/haproxy'; import { reloadHaproxy } from '$lib/haproxy';
export default async function () { export default async function (): Promise<void> {
try {
await asyncExecShell( await asyncExecShell(
`docker run --rm --name certbot-renewal -v "coolify-letsencrypt:/etc/letsencrypt" certbot/certbot --logs-dir /etc/letsencrypt/logs renew` `docker run --rm --name certbot-renewal -v "coolify-letsencrypt:/etc/letsencrypt" certbot/certbot --logs-dir /etc/letsencrypt/logs renew`
); );
await reloadHaproxy('unix:///var/run/docker.sock'); await reloadHaproxy('unix:///var/run/docker.sock');
} catch (error) {
throw error;
}
} }

View File

@ -1,6 +1,7 @@
import { writable } from 'svelte/store'; import { writable, type Writable } from 'svelte/store';
export const gitTokens = writable({ export const gitTokens: Writable<{ githubToken: string | null; gitlabToken: string | null }> =
writable({
githubToken: null, githubToken: null,
gitlabToken: null gitlabToken: null
}); });

View File

@ -0,0 +1,51 @@
import type { DestinationDocker, GithubApp, GitlabApp, GitSource, Secret } from '@prisma/client';
export type BuilderJob = {
build_id: string;
type: BuildType;
id: string;
name: string;
fqdn: string;
repository: string;
configHash: unknown;
branch: string;
buildPack: BuildPackName;
projectId: number;
port: number;
installCommand: string;
buildCommand?: string;
startCommand?: string;
baseDirectory: string;
publishDirectory: string;
phpModules: string;
pythonWSGI: string;
pythonModule: string;
pythonVariable: string;
createdAt: string;
updatedAt: string;
destinationDockerId: string;
destinationDocker: DestinationDocker;
gitSource: GitSource & { githubApp?: GithubApp; gitlabApp?: GitlabApp };
settings: BuilderJobSettings;
secrets: Secret[];
persistentStorage: { path: string }[];
pullmergeRequestId?: unknown;
sourceBranch?: string;
};
// TODO: Add the other build types
export type BuildType = 'manual';
// TODO: Add the other buildpack names
export type BuildPackName = 'node' | 'docker';
export type BuilderJobSettings = {
id: string;
applicationId: string;
dualCerts: boolean;
debug: boolean;
previews: boolean;
autodeploy: boolean;
createdAt: string;
updatedAt: string;
};

View File

@ -0,0 +1,8 @@
export type CreateDockerDestination = {
name: string;
engine: string;
remoteEngine: boolean;
network: string;
isCoolifyProxyUsed: boolean;
teamId: string;
};

View File

@ -1,4 +1,4 @@
import { getTeam, getUserDetails } from '$lib/common'; import { getUserDetails } from '$lib/common';
import * as db from '$lib/database'; import * as db from '$lib/database';
import { ErrorHandler } from '$lib/database'; import { ErrorHandler } from '$lib/database';
import type { RequestHandler } from '@sveltejs/kit'; import type { RequestHandler } from '@sveltejs/kit';

View File

@ -1,43 +1,22 @@
import { asyncExecShell, getUserDetails } from '$lib/common'; import { getUserDetails } from '$lib/common';
import * as db from '$lib/database'; import * as db from '$lib/database';
import { ErrorHandler } from '$lib/database'; import { ErrorHandler } from '$lib/database';
import { dockerInstance } from '$lib/docker';
import type { RequestHandler } from '@sveltejs/kit'; import type { RequestHandler } from '@sveltejs/kit';
import type { CreateDockerDestination } from '$lib/types/destinations';
export const post: RequestHandler = async (event) => { export const post: RequestHandler = async (event) => {
const { teamId, status, body } = await getUserDetails(event); const { teamId, status, body } = await getUserDetails(event);
if (status === 401) return { status, body }; if (status === 401) return { status, body };
const { const dockerDestinationProps = {
name, ...((await event.request.json()) as Omit<CreateDockerDestination, 'teamId'>),
engine, teamId
network, };
isCoolifyProxyUsed,
remoteEngine,
ipAddress,
user,
port,
sshPrivateKey
} = await event.request.json();
try { try {
let id = null; const id = dockerDestinationProps.remoteEngine
if (remoteEngine) { ? await db.newRemoteDestination(dockerDestinationProps)
id = await db.newRemoteDestination({ : await db.newLocalDestination(dockerDestinationProps);
name,
teamId,
engine,
network,
isCoolifyProxyUsed,
remoteEngine,
ipAddress,
user,
port,
sshPrivateKey
});
} else {
id = await db.newLocalDestination({ name, teamId, engine, network, isCoolifyProxyUsed });
}
return { status: 200, body: { id } }; return { status: 200, body: { id } };
} catch (error) { } catch (error) {
return ErrorHandler(error); return ErrorHandler(error);