Merge branch 'main' into arm

This commit is contained in:
Andras Bacsai
2022-04-11 20:29:29 +02:00
143 changed files with 3665 additions and 1496 deletions

View File

@@ -5,7 +5,13 @@ import { getDomain, removeDestinationDocker } from '$lib/common';
import { prisma } from './common';
export async function listApplications(teamId) {
return await prisma.application.findMany({ where: { teams: { some: { id: teamId } } } });
if (teamId === '0') {
return await prisma.application.findMany({ include: { teams: true } });
}
return await prisma.application.findMany({
where: { teams: { some: { id: teamId } } },
include: { teams: true }
});
}
export async function newApplication({ name, teamId }) {
@@ -67,7 +73,11 @@ export async function removeApplication({ id, teamId }) {
await prisma.build.deleteMany({ where: { applicationId: id } });
await prisma.secret.deleteMany({ where: { applicationId: id } });
await prisma.applicationPersistentStorage.deleteMany({ where: { applicationId: id } });
await prisma.application.deleteMany({ where: { id, teams: { some: { id: teamId } } } });
if (teamId === '0') {
await prisma.application.deleteMany({ where: { id } });
} else {
await prisma.application.deleteMany({ where: { id, teams: { some: { id: teamId } } } });
}
}
export async function getApplicationWebhook({ projectId, branch }) {
@@ -130,16 +140,30 @@ export async function getApplicationById({ id }) {
return { ...body };
}
export async function getApplication({ id, teamId }) {
let body = await prisma.application.findFirst({
where: { id, teams: { some: { id: teamId } } },
include: {
destinationDocker: true,
settings: true,
gitSource: { include: { githubApp: true, gitlabApp: true } },
secrets: true,
persistentStorage: true
}
});
let body = {};
if (teamId === '0') {
body = await prisma.application.findFirst({
where: { id },
include: {
destinationDocker: true,
settings: true,
gitSource: { include: { githubApp: true, gitlabApp: true } },
secrets: true,
persistentStorage: true
}
});
} else {
body = await prisma.application.findFirst({
where: { id, teams: { some: { id: teamId } } },
include: {
destinationDocker: true,
settings: true,
gitSource: { include: { githubApp: true, gitlabApp: true } },
secrets: true,
persistentStorage: true
}
});
}
if (body?.gitSource?.githubApp?.clientSecret) {
body.gitSource.githubApp.clientSecret = decrypt(body.gitSource.githubApp.clientSecret);
@@ -214,11 +238,15 @@ export async function configureApplication({
buildCommand,
startCommand,
baseDirectory,
publishDirectory
publishDirectory,
pythonWSGI,
pythonModule,
pythonVariable
}) {
return await prisma.application.update({
where: { id },
data: {
name,
buildPack,
fqdn,
port,
@@ -227,7 +255,9 @@ export async function configureApplication({
startCommand,
baseDirectory,
publishDirectory,
name
pythonWSGI,
pythonModule,
pythonVariable
}
});
}

View File

@@ -1,5 +1,9 @@
import { dev } from '$app/env';
import { sentry } from '$lib/common';
import {
supportedDatabaseTypesAndVersions,
supportedServiceTypesAndVersions
} from '$lib/components/common';
import * as Prisma from '@prisma/client';
import { default as ProdPrisma } from '@prisma/client';
import type { PrismaClientOptions } from '@prisma/client/runtime';
@@ -46,7 +50,9 @@ export function ErrorHandler(e) {
if (e.message?.includes('git clone')) {
truncatedError.message = 'git clone failed';
}
sentry.captureException(truncatedError);
if (!e.message?.includes('Coolify Proxy is not running')) {
sentry.captureException(truncatedError);
}
const payload = {
status: truncatedError.status || 500,
body: {
@@ -80,124 +86,6 @@ export async function generateSshKeyPair(): Promise<{ publicKey: string; private
});
}
export const supportedDatabaseTypesAndVersions = [
{
name: 'mongodb',
fancyName: 'MongoDB',
baseImage: 'bitnami/mongodb',
versions: ['5.0.5', '4.4.11', '4.2.18', '4.0.27']
},
{ name: 'mysql', fancyName: 'MySQL', baseImage: 'bitnami/mysql', versions: ['8.0.27', '5.7.36'] },
{
name: 'postgresql',
fancyName: 'PostgreSQL',
baseImage: 'bitnami/postgresql',
versions: ['14.1.0', '13.5.0', '12.9.0', '11.14.0', '10.19.0', '9.6.24']
},
{
name: 'redis',
fancyName: 'Redis',
baseImage: 'bitnami/redis',
versions: ['6.2.6', '6.0.16', '5.0.14']
},
{ name: 'couchdb', fancyName: 'CouchDB', baseImage: 'bitnami/couchdb', versions: ['3.2.1'] }
];
export const supportedServiceTypesAndVersions = [
{
name: 'plausibleanalytics',
fancyName: 'Plausible Analytics',
baseImage: 'plausible/analytics',
images: ['bitnami/postgresql:13.2.0', 'yandex/clickhouse-server:21.3.2.5'],
versions: ['latest'],
ports: {
main: 8000
}
},
{
name: 'nocodb',
fancyName: 'NocoDB',
baseImage: 'nocodb/nocodb',
versions: ['latest'],
ports: {
main: 8080
}
},
{
name: 'minio',
fancyName: 'MinIO',
baseImage: 'minio/minio',
versions: ['latest'],
ports: {
main: 9001
}
},
{
name: 'vscodeserver',
fancyName: 'VSCode Server',
baseImage: 'codercom/code-server',
versions: ['latest'],
ports: {
main: 8080
}
},
{
name: 'wordpress',
fancyName: 'Wordpress',
baseImage: 'wordpress',
images: ['bitnami/mysql:5.7'],
versions: ['latest', 'php8.1', 'php8.0', 'php7.4', 'php7.3'],
ports: {
main: 80
}
},
{
name: 'vaultwarden',
fancyName: 'Vaultwarden',
baseImage: 'vaultwarden/server',
versions: ['latest'],
ports: {
main: 80
}
},
{
name: 'languagetool',
fancyName: 'LanguageTool',
baseImage: 'silviof/docker-languagetool',
versions: ['latest'],
ports: {
main: 8010
}
},
{
name: 'n8n',
fancyName: 'n8n',
baseImage: 'n8nio/n8n',
versions: ['latest'],
ports: {
main: 5678
}
},
{
name: 'uptimekuma',
fancyName: 'Uptime Kuma',
baseImage: 'louislam/uptime-kuma',
versions: ['latest'],
ports: {
main: 3001
}
},
{
name: 'ghost',
fancyName: 'Ghost',
baseImage: 'bitnami/ghost',
images: ['bitnami/mariadb'],
versions: ['latest'],
ports: {
main: 2368
}
}
];
export function getVersions(type) {
const found = supportedDatabaseTypesAndVersions.find((t) => t.name === type);
if (found) {
@@ -271,6 +159,7 @@ export function generateDatabaseConfiguration(database) {
// url: `psql://${dbUser}:${dbUserPassword}@${id}:${isPublic ? port : 5432}/${defaultDatabase}`,
privatePort: 5432,
environmentVariables: {
POSTGRESQL_POSTGRES_PASSWORD: rootUserPassword,
POSTGRESQL_PASSWORD: dbUserPassword,
POSTGRESQL_USERNAME: dbUser,
POSTGRESQL_DATABASE: defaultDatabase

View File

@@ -7,7 +7,14 @@ import getPort, { portNumbers } from 'get-port';
import { asyncExecShell, getEngine, removeContainer } from '$lib/common';
export async function listDatabases(teamId) {
return await prisma.database.findMany({ where: { teams: { some: { id: teamId } } } });
if (teamId === '0') {
return await prisma.database.findMany({ include: { teams: true } });
} else {
return await prisma.database.findMany({
where: { teams: { some: { id: teamId } } },
include: { teams: true }
});
}
}
export async function newDatabase({ name, teamId }) {
const dbUser = cuid();
@@ -31,10 +38,18 @@ export async function newDatabase({ name, teamId }) {
}
export async function getDatabase({ id, teamId }) {
const body = await prisma.database.findFirst({
where: { id, teams: { some: { id: teamId } } },
include: { destinationDocker: true, settings: true }
});
let body = {};
if (teamId === '0') {
body = await prisma.database.findFirst({
where: { id },
include: { destinationDocker: true, settings: true }
});
} else {
body = await prisma.database.findFirst({
where: { id, teams: { some: { id: teamId } } },
include: { destinationDocker: true, settings: true }
});
}
if (body.dbUserPassword) body.dbUserPassword = decrypt(body.dbUserPassword);
if (body.rootUserPassword) body.rootUserPassword = decrypt(body.rootUserPassword);
@@ -122,3 +137,43 @@ export async function stopDatabase(database) {
}
return everStarted;
}
export async function updatePasswordInDb(database, user, newPassword, isRoot) {
const {
id,
type,
rootUser,
rootUserPassword,
dbUser,
dbUserPassword,
defaultDatabase,
destinationDockerId,
destinationDocker: { engine }
} = database;
if (destinationDockerId) {
const host = getEngine(engine);
if (type === 'mysql') {
await asyncExecShell(
`DOCKER_HOST=${host} docker exec ${id} mysql -u ${rootUser} -p${rootUserPassword} -e \"ALTER USER '${user}'@'%' IDENTIFIED WITH caching_sha2_password BY '${newPassword}';\"`
);
} else if (type === 'postgresql') {
if (isRoot) {
await asyncExecShell(
`DOCKER_HOST=${host} docker exec ${id} psql postgresql://postgres:${rootUserPassword}@${id}:5432/${defaultDatabase} -c "ALTER role postgres WITH PASSWORD '${newPassword}'"`
);
} else {
await asyncExecShell(
`DOCKER_HOST=${host} docker exec ${id} psql postgresql://${dbUser}:${dbUserPassword}@${id}:5432/${defaultDatabase} -c "ALTER role ${user} WITH PASSWORD '${newPassword}'"`
);
}
} else if (type === 'mongodb') {
await asyncExecShell(
`DOCKER_HOST=${host} docker exec ${id} mongo 'mongodb://${rootUser}:${rootUserPassword}@${id}:27017/admin?readPreference=primary&ssl=false' --eval "db.changeUserPassword('${user}','${newPassword}')"`
);
} else if (type === 'redis') {
await asyncExecShell(
`DOCKER_HOST=${host} docker exec ${id} redis-cli -u redis://${dbUserPassword}@${id}:6379 --raw CONFIG SET requirepass ${newPassword}`
);
}
}
}

View File

@@ -6,7 +6,13 @@ import { getDatabaseImage } from '.';
import { prisma } from './common';
export async function listDestinations(teamId) {
return await prisma.destinationDocker.findMany({ where: { teams: { some: { id: teamId } } } });
if (teamId === '0') {
return await prisma.destinationDocker.findMany({ include: { teams: true } });
}
return await prisma.destinationDocker.findMany({
where: { teams: { some: { id: teamId } } },
include: { teams: true }
});
}
export async function configureDestinationForService({ id, destinationId }) {
@@ -38,9 +44,7 @@ export async function configureDestinationForDatabase({ id, destinationId }) {
const host = getEngine(engine);
if (type && version) {
const baseImage = getDatabaseImage(type);
asyncExecShell(
`DOCKER_HOST=${host} docker pull ${baseImage}:${version} && echo "FROM ${baseImage}:${version}" | docker build --label coolify.image="true" -t "${baseImage}:${version}" -`
);
asyncExecShell(`DOCKER_HOST=${host} docker pull ${baseImage}:${version}`);
}
}
}
@@ -124,12 +128,17 @@ export async function removeDestination({ id }) {
}
export async function getDestination({ id, teamId }) {
let destination = await prisma.destinationDocker.findFirst({
where: { id, teams: { some: { id: teamId } } }
});
if (destination.remoteEngine) {
destination.sshPrivateKey = decrypt(destination.sshPrivateKey);
let destination = {};
if (teamId === '0') {
destination = await prisma.destinationDocker.findFirst({
where: { id }
});
} else {
destination = await prisma.destinationDocker.findFirst({
where: { id, teams: { some: { id: teamId } } }
});
}
return destination;
}
export async function getDestinationByApplicationId({ id, teamId }) {

View File

@@ -2,26 +2,26 @@ import { decrypt, encrypt } from '$lib/crypto';
import { prisma } from './common';
export async function listSources(teamId) {
if (teamId === '0') {
return await prisma.gitSource.findMany({
include: { githubApp: true, gitlabApp: true, teams: true }
});
}
return await prisma.gitSource.findMany({
where: { teams: { some: { id: teamId } } },
include: { githubApp: true, gitlabApp: true }
include: { githubApp: true, gitlabApp: true, teams: true }
});
}
export async function newSource({ name, teamId, type, htmlUrl, apiUrl, organization }) {
export async function newSource({ teamId, name }) {
return await prisma.gitSource.create({
data: {
teams: { connect: { id: teamId } },
name,
type,
htmlUrl,
apiUrl,
organization
teams: { connect: { id: teamId } }
}
});
}
export async function removeSource({ id }) {
// TODO: Disconnect application with this sourceId! Maybe not needed?
const source = await prisma.gitSource.delete({
where: { id },
include: { githubApp: true, gitlabApp: true }
@@ -31,10 +31,18 @@ export async function removeSource({ id }) {
}
export async function getSource({ id, teamId }) {
let body = await prisma.gitSource.findFirst({
where: { id, teams: { some: { id: teamId } } },
include: { githubApp: true, gitlabApp: true }
});
let body = {};
if (teamId === '0') {
body = await prisma.gitSource.findFirst({
where: { id },
include: { githubApp: true, gitlabApp: true }
});
} else {
body = await prisma.gitSource.findFirst({
where: { id, teams: { some: { id: teamId } } },
include: { githubApp: true, gitlabApp: true }
});
}
if (body?.githubApp?.clientSecret)
body.githubApp.clientSecret = decrypt(body.githubApp.clientSecret);
if (body?.githubApp?.webhookSecret)
@@ -43,8 +51,29 @@ export async function getSource({ id, teamId }) {
if (body?.gitlabApp?.appSecret) body.gitlabApp.appSecret = decrypt(body.gitlabApp.appSecret);
return body;
}
export async function addSource({ id, appId, teamId, oauthId, groupName, appSecret }) {
export async function addGitHubSource({ id, teamId, type, name, htmlUrl, apiUrl }) {
await prisma.gitSource.update({ where: { id }, data: { type, name, htmlUrl, apiUrl } });
return await prisma.githubApp.create({
data: {
teams: { connect: { id: teamId } },
gitSource: { connect: { id } }
}
});
}
export async function addGitLabSource({
id,
teamId,
type,
name,
htmlUrl,
apiUrl,
oauthId,
appId,
appSecret,
groupName
}) {
const encrptedAppSecret = encrypt(appSecret);
await prisma.gitSource.update({ where: { id }, data: { type, apiUrl, htmlUrl, name } });
return await prisma.gitlabApp.create({
data: {
teams: { connect: { id: teamId } },
@@ -63,9 +92,9 @@ export async function configureGitsource({ id, gitSourceId }) {
data: { gitSource: { connect: { id: gitSourceId } } }
});
}
export async function updateGitsource({ id, name }) {
export async function updateGitsource({ id, name, htmlUrl, apiUrl }) {
return await prisma.gitSource.update({
where: { id },
data: { name }
data: { name, htmlUrl, apiUrl }
});
}

View File

@@ -5,7 +5,14 @@ import { generatePassword } from '.';
import { prisma } from './common';
export async function listServices(teamId) {
return await prisma.service.findMany({ where: { teams: { some: { id: teamId } } } });
if (teamId === '0') {
return await prisma.service.findMany({ include: { teams: true } });
} else {
return await prisma.service.findMany({
where: { teams: { some: { id: teamId } } },
include: { teams: true }
});
}
}
export async function newService({ name, teamId }) {
@@ -13,18 +20,28 @@ export async function newService({ name, teamId }) {
}
export async function getService({ id, teamId }) {
const body = await prisma.service.findFirst({
where: { id, teams: { some: { id: teamId } } },
include: {
destinationDocker: true,
plausibleAnalytics: true,
minio: true,
vscodeserver: true,
wordpress: true,
ghost: true,
serviceSecret: true
}
});
let body = {};
const include = {
destinationDocker: true,
plausibleAnalytics: true,
minio: true,
vscodeserver: true,
wordpress: true,
ghost: true,
serviceSecret: true,
meiliSearch: true
};
if (teamId === '0') {
body = await prisma.service.findFirst({
where: { id },
include
});
} else {
body = await prisma.service.findFirst({
where: { id, teams: { some: { id: teamId } } },
include
});
}
if (body.plausibleAnalytics?.postgresqlPassword)
body.plausibleAnalytics.postgresqlPassword = decrypt(
@@ -50,14 +67,20 @@ export async function getService({ id, teamId }) {
body.ghost.mariadbRootUserPassword = decrypt(body.ghost.mariadbRootUserPassword);
if (body.ghost?.defaultPassword) body.ghost.defaultPassword = decrypt(body.ghost.defaultPassword);
if (body.meiliSearch?.masterKey) body.meiliSearch.masterKey = decrypt(body.meiliSearch.masterKey);
if (body?.serviceSecret.length > 0) {
body.serviceSecret = body.serviceSecret.map((s) => {
s.value = decrypt(s.value);
return s;
});
}
if (body.wordpress?.ftpPassword) {
body.wordpress.ftpPassword = decrypt(body.wordpress.ftpPassword);
}
const settings = await prisma.setting.findFirst();
return { ...body };
return { ...body, settings };
}
export async function configureServiceType({ id, type }) {
@@ -142,7 +165,7 @@ export async function configureServiceType({ id, type }) {
}
});
} else if (type === 'ghost') {
const defaultEmail = `${cuid()}@coolify.io`;
const defaultEmail = `${cuid()}@example.com`;
const defaultPassword = encrypt(generatePassword());
const mariadbUser = cuid();
const mariadbPassword = encrypt(generatePassword());
@@ -165,6 +188,15 @@ export async function configureServiceType({ id, type }) {
}
}
});
} else if (type === 'meilisearch') {
const masterKey = encrypt(generatePassword(32));
await prisma.service.update({
where: { id },
data: {
type,
meiliSearch: { create: { masterKey } }
}
});
}
}
export async function setServiceVersion({ id, version }) {
@@ -188,15 +220,6 @@ export async function updatePlausibleAnalyticsService({ id, fqdn, email, usernam
export async function updateService({ id, fqdn, name }) {
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
}
export async function updateLanguageToolService({ id, fqdn, name }) {
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
}
export async function updateVaultWardenService({ id, fqdn, name }) {
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
}
export async function updateVsCodeServer({ id, fqdn, name }) {
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
}
export async function updateWordpress({ id, fqdn, name, mysqlDatabase, extraConfig }) {
return await prisma.service.update({
where: { id },
@@ -214,6 +237,7 @@ export async function updateGhostService({ id, fqdn, name, mariadbDatabase }) {
}
export async function removeService({ id }) {
await prisma.meiliSearch.deleteMany({ where: { serviceId: id } });
await prisma.ghost.deleteMany({ where: { serviceId: id } });
await prisma.plausibleAnalytics.deleteMany({ where: { serviceId: id } });
await prisma.minio.deleteMany({ where: { serviceId: id } });

View File

@@ -32,26 +32,42 @@ export async function login({ email, password, isLogin }) {
if (users === 0) {
await prisma.setting.update({ where: { id }, data: { isRegistrationEnabled: false } });
// Create default network & start Coolify Proxy
asyncExecShell(`docker network create --attachable coolify`)
.then(() => {
console.log('Network created');
})
.catch(() => {
console.log('Network already exists.');
});
startCoolifyProxy('/var/run/docker.sock')
.then(() => {
console.log('Coolify Proxy started.');
})
.catch((err) => {
console.log(err);
});
await asyncExecShell(`docker network create --attachable coolify`);
await startCoolifyProxy('/var/run/docker.sock');
uid = '0';
}
if (userFound) {
if (userFound.type === 'email') {
if (userFound.password === 'RESETME') {
const hashedPassword = await hashPassword(password);
if (userFound.updatedAt < new Date(Date.now() - 1000 * 60 * 10)) {
await prisma.user.update({
where: { email: userFound.email },
data: { password: 'RESETTIMEOUT' }
});
throw {
error: 'Password reset link has expired. Please request a new one.'
};
} else {
await prisma.user.update({
where: { email: userFound.email },
data: { password: hashedPassword }
});
return {
status: 200,
headers: {
'Set-Cookie': `teamId=${uid}; HttpOnly; Path=/; Max-Age=15778800;`
},
body: {
userId: userFound.id,
teamId: userFound.id,
permission: userFound.permission,
isAdmin: true
}
};
}
}
const passwordMatch = await bcrypt.compare(password, userFound.password);
if (!passwordMatch) {
throw {