Merge remote-tracking branch 'upstream/next' into feature/glitchtip-service
# Conflicts: # apps/api/prisma/schema.prisma # apps/api/src/lib/common.ts # apps/api/src/lib/serviceFields.ts # apps/api/src/routes/api/v1/services/handlers.ts
This commit is contained in:
commit
ce2c887469
2
.gitpod.Dockerfile
vendored
Normal file
2
.gitpod.Dockerfile
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
FROM gitpod/workspace-node:2022-06-20-19-54-55
|
||||||
|
RUN (curl -sSL "https://github.com/buildpacks/pack/releases/download/v0.27.0/pack-v0.27.0-linux.tgz" | tar -C /usr/local/bin/ --no-same-owner -xzv pack)
|
@ -1,7 +1,8 @@
|
|||||||
# This configuration file was automatically generated by Gitpod.
|
# This configuration file was automatically generated by Gitpod.
|
||||||
# Please adjust to your needs (see https://www.gitpod.io/docs/config-gitpod-file)
|
# Please adjust to your needs (see https://www.gitpod.io/docs/config-gitpod-file)
|
||||||
# and commit this file to your remote git repository to share the goodness with others.
|
# and commit this file to your remote git repository to share the goodness with others.
|
||||||
image: gitpod/workspace-node:2022-06-20-19-54-55
|
image:
|
||||||
|
file: .gitpod.Dockerfile
|
||||||
tasks:
|
tasks:
|
||||||
- init: pnpm install && pnpm db:push && pnpm db:seed
|
- init: pnpm install && pnpm db:push && pnpm db:seed
|
||||||
command: pnpm dev
|
command: pnpm dev
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Appwrite" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"serviceId" TEXT NOT NULL,
|
||||||
|
"opensslKeyV1" TEXT NOT NULL,
|
||||||
|
"executorSecret" TEXT NOT NULL,
|
||||||
|
"redisPassword" TEXT NOT NULL,
|
||||||
|
"mariadbHost" TEXT,
|
||||||
|
"mariadbPort" INTEGER NOT NULL DEFAULT 3306,
|
||||||
|
"mariadbUser" TEXT NOT NULL,
|
||||||
|
"mariadbPassword" TEXT NOT NULL,
|
||||||
|
"mariadbRootUser" TEXT NOT NULL,
|
||||||
|
"mariadbRootUserPassword" TEXT NOT NULL,
|
||||||
|
"mariadbDatabase" TEXT NOT NULL,
|
||||||
|
"mariadbPublicPort" INTEGER,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
CONSTRAINT "Appwrite_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "Appwrite_serviceId_key" ON "Appwrite"("serviceId");
|
@ -323,6 +323,7 @@ model Service {
|
|||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
destinationDocker DestinationDocker? @relation(fields: [destinationDockerId], references: [id])
|
destinationDocker DestinationDocker? @relation(fields: [destinationDockerId], references: [id])
|
||||||
|
|
||||||
fider Fider?
|
fider Fider?
|
||||||
ghost Ghost?
|
ghost Ghost?
|
||||||
glitchTip GlitchTip?
|
glitchTip GlitchTip?
|
||||||
@ -336,7 +337,9 @@ model Service {
|
|||||||
umami Umami?
|
umami Umami?
|
||||||
vscodeserver Vscodeserver?
|
vscodeserver Vscodeserver?
|
||||||
wordpress Wordpress?
|
wordpress Wordpress?
|
||||||
teams Team[]
|
appwrite Appwrite?
|
||||||
|
|
||||||
|
teams Team[]
|
||||||
}
|
}
|
||||||
|
|
||||||
model PlausibleAnalytics {
|
model PlausibleAnalytics {
|
||||||
@ -493,6 +496,25 @@ model Moodle {
|
|||||||
service Service @relation(fields: [serviceId], references: [id])
|
service Service @relation(fields: [serviceId], references: [id])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model Appwrite {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
serviceId String @unique
|
||||||
|
opensslKeyV1 String
|
||||||
|
executorSecret String
|
||||||
|
redisPassword String
|
||||||
|
mariadbHost String?
|
||||||
|
mariadbPort Int @default(3306)
|
||||||
|
mariadbUser String
|
||||||
|
mariadbPassword String
|
||||||
|
mariadbRootUser String
|
||||||
|
mariadbRootUserPassword String
|
||||||
|
mariadbDatabase String
|
||||||
|
mariadbPublicPort Int?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
service Service @relation(fields: [serviceId], references: [id])
|
||||||
|
}
|
||||||
|
|
||||||
model GlitchTip {
|
model GlitchTip {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
postgresqlUser String
|
postgresqlUser String
|
||||||
|
@ -2,14 +2,14 @@ import { executeDockerCmd, prisma } from "../common"
|
|||||||
import { saveBuildLog } from "./common";
|
import { saveBuildLog } from "./common";
|
||||||
|
|
||||||
export default async function (data: any): Promise<void> {
|
export default async function (data: any): Promise<void> {
|
||||||
|
const { buildId, applicationId, tag, dockerId, debug, workdir } = data
|
||||||
try {
|
try {
|
||||||
const { buildId, applicationId, tag, dockerId, debug, workdir } = data
|
|
||||||
await saveBuildLog({ line: `Building image started.`, buildId, applicationId });
|
await saveBuildLog({ line: `Building image started.`, buildId, applicationId });
|
||||||
const { stdout } = await executeDockerCmd({
|
const { stdout } = await executeDockerCmd({
|
||||||
dockerId,
|
dockerId,
|
||||||
command: `pack build -p ${workdir} ${applicationId}:${tag} --builder heroku/buildpacks:20`
|
command: `pack build -p ${workdir} ${applicationId}:${tag} --builder heroku/buildpacks:20`
|
||||||
})
|
})
|
||||||
|
|
||||||
if (debug) {
|
if (debug) {
|
||||||
const array = stdout.split('\n')
|
const array = stdout.split('\n')
|
||||||
for (const line of array) {
|
for (const line of array) {
|
||||||
@ -24,6 +24,16 @@ export default async function (data: any): Promise<void> {
|
|||||||
}
|
}
|
||||||
await saveBuildLog({ line: `Building image successful.`, buildId, applicationId });
|
await saveBuildLog({ line: `Building image successful.`, buildId, applicationId });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
const array = error.stdout.split('\n')
|
||||||
|
for (const line of array) {
|
||||||
|
if (line !== '\n') {
|
||||||
|
await saveBuildLog({
|
||||||
|
line: `${line.replace('\n', '')}`,
|
||||||
|
buildId,
|
||||||
|
applicationId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ import { checkContainer, removeContainer } from './docker';
|
|||||||
import { day } from './dayjs';
|
import { day } from './dayjs';
|
||||||
import * as serviceFields from './serviceFields'
|
import * as serviceFields from './serviceFields'
|
||||||
|
|
||||||
export const version = '3.3.2';
|
export const version = '3.4.0';
|
||||||
export const isDev = process.env.NODE_ENV === 'development';
|
export const isDev = process.env.NODE_ENV === 'development';
|
||||||
|
|
||||||
const algorithm = 'aes-256-ctr';
|
const algorithm = 'aes-256-ctr';
|
||||||
@ -78,6 +78,8 @@ export const include: any = {
|
|||||||
umami: true,
|
umami: true,
|
||||||
hasura: true,
|
hasura: true,
|
||||||
fider: true,
|
fider: true,
|
||||||
|
moodle: true,
|
||||||
|
appwrite: true,
|
||||||
glitchTip: true,
|
glitchTip: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -97,17 +99,23 @@ export const base64Decode = (text: string): string => {
|
|||||||
};
|
};
|
||||||
export const decrypt = (hashString: string) => {
|
export const decrypt = (hashString: string) => {
|
||||||
if (hashString) {
|
if (hashString) {
|
||||||
const hash = JSON.parse(hashString);
|
try {
|
||||||
const decipher = crypto.createDecipheriv(
|
const hash = JSON.parse(hashString);
|
||||||
algorithm,
|
const decipher = crypto.createDecipheriv(
|
||||||
process.env['COOLIFY_SECRET_KEY'],
|
algorithm,
|
||||||
Buffer.from(hash.iv, 'hex')
|
process.env['COOLIFY_SECRET_KEY'],
|
||||||
);
|
Buffer.from(hash.iv, 'hex')
|
||||||
const decrpyted = Buffer.concat([
|
);
|
||||||
decipher.update(Buffer.from(hash.content, 'hex')),
|
const decrpyted = Buffer.concat([
|
||||||
decipher.final()
|
decipher.update(Buffer.from(hash.content, 'hex')),
|
||||||
]);
|
decipher.final()
|
||||||
return decrpyted.toString();
|
]);
|
||||||
|
return decrpyted.toString();
|
||||||
|
} catch (error) {
|
||||||
|
console.log({ decryptionError: error.message })
|
||||||
|
return hashString
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
export const encrypt = (text: string) => {
|
export const encrypt = (text: string) => {
|
||||||
@ -270,6 +278,17 @@ export const supportedServiceTypesAndVersions = [
|
|||||||
main: 3000
|
main: 3000
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'appwrite',
|
||||||
|
fancyName: 'Appwrite',
|
||||||
|
baseImage: 'appwrite/appwrite',
|
||||||
|
images: ['mariadb:10.7', 'redis:6.2-alpine', 'appwrite/telegraf:1.4.0'],
|
||||||
|
versions: ['latest', '0.15.3'],
|
||||||
|
recommendedVersion: '0.15.3',
|
||||||
|
ports: {
|
||||||
|
main: 80
|
||||||
|
}
|
||||||
|
}
|
||||||
// {
|
// {
|
||||||
// name: 'moodle',
|
// name: 'moodle',
|
||||||
// fancyName: 'Moodle',
|
// fancyName: 'Moodle',
|
||||||
@ -585,6 +604,11 @@ export async function executeDockerCmd({ dockerId, command }: { dockerId: string
|
|||||||
} else {
|
} else {
|
||||||
engine = 'unix:///var/run/docker.sock'
|
engine = 'unix:///var/run/docker.sock'
|
||||||
}
|
}
|
||||||
|
if (process.env.CODESANDBOX_HOST) {
|
||||||
|
if (command.startsWith('docker compose')) {
|
||||||
|
command = command.replace(/docker compose/gi, 'docker-compose')
|
||||||
|
}
|
||||||
|
}
|
||||||
return await asyncExecShell(
|
return await asyncExecShell(
|
||||||
`DOCKER_BUILDKIT=1 DOCKER_HOST="${engine}" ${command}`
|
`DOCKER_BUILDKIT=1 DOCKER_HOST="${engine}" ${command}`
|
||||||
);
|
);
|
||||||
@ -596,6 +620,11 @@ export async function startTraefikProxy(id: string): Promise<void> {
|
|||||||
const { id: settingsId, ipv4, ipv6 } = await listSettings();
|
const { id: settingsId, ipv4, ipv6 } = await listSettings();
|
||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
|
const { stdout: coolifyNetwork } = await executeDockerCmd({ dockerId: id, command: `docker network ls --filter 'name=coolify-infra' --no-trunc --format "{{json .}}"` })
|
||||||
|
|
||||||
|
if (!coolifyNetwork) {
|
||||||
|
await executeDockerCmd({ dockerId: id, command: `docker network create --attachable coolify-infra` })
|
||||||
|
}
|
||||||
const { stdout: Config } = await executeDockerCmd({ dockerId: id, command: `docker network inspect ${network} --format '{{json .IPAM.Config }}'` })
|
const { stdout: Config } = await executeDockerCmd({ dockerId: id, command: `docker network inspect ${network} --format '{{json .IPAM.Config }}'` })
|
||||||
const ip = JSON.parse(Config)[0].Gateway;
|
const ip = JSON.parse(Config)[0].Gateway;
|
||||||
let traefikUrl = mainTraefikEndpoint
|
let traefikUrl = mainTraefikEndpoint
|
||||||
@ -879,6 +908,11 @@ export function generateDatabaseConfiguration(database: any, arch: string):
|
|||||||
}
|
}
|
||||||
if (isARM(arch)) {
|
if (isARM(arch)) {
|
||||||
configuration.volume = `${id}-${type}-data:/var/lib/postgresql`;
|
configuration.volume = `${id}-${type}-data:/var/lib/postgresql`;
|
||||||
|
configuration.environmentVariables = {
|
||||||
|
POSTGRES_PASSWORD: dbUserPassword,
|
||||||
|
POSTGRES_USER: dbUser,
|
||||||
|
POSTGRES_DB: defaultDatabase
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return configuration
|
return configuration
|
||||||
} else if (type === 'redis') {
|
} else if (type === 'redis') {
|
||||||
@ -915,7 +949,7 @@ export function generateDatabaseConfiguration(database: any, arch: string):
|
|||||||
return configuration
|
return configuration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export function isARM(arch) {
|
export function isARM(arch: string) {
|
||||||
if (arch === 'arm' || arch === 'arm64') {
|
if (arch === 'arm' || arch === 'arm64') {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -1238,7 +1272,6 @@ export async function startTraefikTCPProxy(
|
|||||||
}
|
}
|
||||||
traefikUrl = `${ip}/webhooks/traefik/other.json`
|
traefikUrl = `${ip}/webhooks/traefik/other.json`
|
||||||
}
|
}
|
||||||
console.log(traefikUrl)
|
|
||||||
const tcpProxy = {
|
const tcpProxy = {
|
||||||
version: '3.8',
|
version: '3.8',
|
||||||
services: {
|
services: {
|
||||||
@ -1303,6 +1336,7 @@ export async function getServiceFromDB({ id, teamId }: { id: string; teamId: str
|
|||||||
return s;
|
return s;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
body[type] = { ...body[type], ...getUpdateableFields(type, body[type]) }
|
body[type] = { ...body[type], ...getUpdateableFields(type, body[type]) }
|
||||||
return { ...body, settings };
|
return { ...body, settings };
|
||||||
}
|
}
|
||||||
@ -1529,6 +1563,35 @@ export async function configureServiceType({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else if (type === 'appwrite') {
|
||||||
|
const opensslKeyV1 = encrypt(generatePassword());
|
||||||
|
const executorSecret = encrypt(generatePassword());
|
||||||
|
const redisPassword = encrypt(generatePassword());
|
||||||
|
const mariadbHost = `${id}-mariadb`
|
||||||
|
const mariadbUser = cuid();
|
||||||
|
const mariadbPassword = encrypt(generatePassword());
|
||||||
|
const mariadbDatabase = 'appwrite';
|
||||||
|
const mariadbRootUser = cuid();
|
||||||
|
const mariadbRootUserPassword = encrypt(generatePassword());
|
||||||
|
await prisma.service.update({
|
||||||
|
where: { id },
|
||||||
|
data: {
|
||||||
|
type,
|
||||||
|
appwrite: {
|
||||||
|
create: {
|
||||||
|
opensslKeyV1,
|
||||||
|
executorSecret,
|
||||||
|
redisPassword,
|
||||||
|
mariadbHost,
|
||||||
|
mariadbUser,
|
||||||
|
mariadbPassword,
|
||||||
|
mariadbDatabase,
|
||||||
|
mariadbRootUser,
|
||||||
|
mariadbRootUserPassword
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
} else if (type === 'glitchTip') {
|
} else if (type === 'glitchTip') {
|
||||||
const defaultUsername = cuid();
|
const defaultUsername = cuid();
|
||||||
const defaultEmail = `${defaultUsername}@example.com`;
|
const defaultEmail = `${defaultUsername}@example.com`;
|
||||||
@ -1566,6 +1629,7 @@ export async function configureServiceType({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function removeService({ id }: { id: string }): Promise<void> {
|
export async function removeService({ id }: { id: string }): Promise<void> {
|
||||||
|
await prisma.serviceSecret.deleteMany({ where: { serviceId: id } });
|
||||||
await prisma.servicePersistentStorage.deleteMany({ where: { serviceId: id } });
|
await prisma.servicePersistentStorage.deleteMany({ where: { serviceId: id } });
|
||||||
await prisma.meiliSearch.deleteMany({ where: { serviceId: id } });
|
await prisma.meiliSearch.deleteMany({ where: { serviceId: id } });
|
||||||
await prisma.fider.deleteMany({ where: { serviceId: id } });
|
await prisma.fider.deleteMany({ where: { serviceId: id } });
|
||||||
@ -1577,8 +1641,8 @@ export async function removeService({ id }: { id: string }): Promise<void> {
|
|||||||
await prisma.vscodeserver.deleteMany({ where: { serviceId: id } });
|
await prisma.vscodeserver.deleteMany({ where: { serviceId: id } });
|
||||||
await prisma.wordpress.deleteMany({ where: { serviceId: id } });
|
await prisma.wordpress.deleteMany({ where: { serviceId: id } });
|
||||||
await prisma.glitchTip.deleteMany({ where: { serviceId: id } });
|
await prisma.glitchTip.deleteMany({ where: { serviceId: id } });
|
||||||
await prisma.serviceSecret.deleteMany({ where: { serviceId: id } });
|
await prisma.moodle.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.appwrite.deleteMany({ where: { serviceId: id } });
|
||||||
await prisma.service.delete({ where: { id } });
|
await prisma.service.delete({ where: { id } });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1643,9 +1707,9 @@ export const getServiceMainPort = (service: string) => {
|
|||||||
export function makeLabelForServices(type) {
|
export function makeLabelForServices(type) {
|
||||||
return [
|
return [
|
||||||
'coolify.managed=true',
|
'coolify.managed=true',
|
||||||
`coolify.version = ${version} `,
|
`coolify.version = ${version}`,
|
||||||
`coolify.type = service`,
|
`coolify.type = service`,
|
||||||
`coolify.service.type = ${type} `
|
`coolify.service.type = ${type}`
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
export function errorHandler({ status = 500, message = 'Unknown error.' }: { status: number, message: string | any }) {
|
export function errorHandler({ status = 500, message = 'Unknown error.' }: { status: number, message: string | any }) {
|
||||||
@ -1717,7 +1781,7 @@ export function convertTolOldVolumeNames(type) {
|
|||||||
// export async function getAvailableServices(): Promise<any> {
|
// export async function getAvailableServices(): Promise<any> {
|
||||||
// const { data } = await axios.get(`https://gist.githubusercontent.com/andrasbacsai/4aac36d8d6214dbfc34fa78110554a50/raw/5b27e6c37d78aaeedc1148d797112c827a2f43cf/availableServices.json`)
|
// const { data } = await axios.get(`https://gist.githubusercontent.com/andrasbacsai/4aac36d8d6214dbfc34fa78110554a50/raw/5b27e6c37d78aaeedc1148d797112c827a2f43cf/availableServices.json`)
|
||||||
// return data
|
// return data
|
||||||
//
|
//
|
||||||
export async function cleanupDockerStorage(dockerId, lowDiskSpace, force) {
|
export async function cleanupDockerStorage(dockerId, lowDiskSpace, force) {
|
||||||
// Cleanup old coolify images
|
// Cleanup old coolify images
|
||||||
try {
|
try {
|
||||||
@ -1780,4 +1844,4 @@ export function persistentVolumes(id, persistentStorage, config) {
|
|||||||
...composeVolumes
|
...composeVolumes
|
||||||
) || {}
|
) || {}
|
||||||
return { volumes, volumeMounts }
|
return { volumes, volumeMounts }
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ export function formatLabelsOnDocker(data) {
|
|||||||
export async function checkContainer({ dockerId, container, remove = false }: { dockerId: string, container: string, remove?: boolean }): Promise<boolean> {
|
export async function checkContainer({ dockerId, container, remove = false }: { dockerId: string, container: string, remove?: boolean }): Promise<boolean> {
|
||||||
let containerFound = false;
|
let containerFound = false;
|
||||||
try {
|
try {
|
||||||
|
console.log('checking ', container)
|
||||||
const { stdout } = await executeDockerCmd({
|
const { stdout } = await executeDockerCmd({
|
||||||
dockerId,
|
dockerId,
|
||||||
command:
|
command:
|
||||||
@ -71,7 +72,7 @@ export async function removeContainer({
|
|||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const { stdout } = await executeDockerCmd({ dockerId, command: `docker inspect --format '{{json .State}}' ${id}` })
|
const { stdout } = await executeDockerCmd({ dockerId, command: `docker inspect --format '{{json .State}}' ${id}` })
|
||||||
|
console.log(id)
|
||||||
if (JSON.parse(stdout).Running) {
|
if (JSON.parse(stdout).Running) {
|
||||||
await executeDockerCmd({ dockerId, command: `docker stop -t 0 ${id}` })
|
await executeDockerCmd({ dockerId, command: `docker stop -t 0 ${id}` })
|
||||||
await executeDockerCmd({ dockerId, command: `docker rm ${id}` })
|
await executeDockerCmd({ dockerId, command: `docker rm ${id}` })
|
||||||
|
@ -326,7 +326,7 @@ export const fider = [{
|
|||||||
isBoolean: false,
|
isBoolean: false,
|
||||||
isEncrypted: true
|
isEncrypted: true
|
||||||
}, {
|
}, {
|
||||||
name: 'postgreslUser',
|
name: 'postgresqlUser',
|
||||||
isEditable: false,
|
isEditable: false,
|
||||||
isLowerCase: false,
|
isLowerCase: false,
|
||||||
isNumber: false,
|
isNumber: false,
|
||||||
@ -477,6 +477,88 @@ export const moodle = [{
|
|||||||
isBoolean: false,
|
isBoolean: false,
|
||||||
isEncrypted: false
|
isEncrypted: false
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
export const appwrite = [{
|
||||||
|
name: 'opensslKeyV1',
|
||||||
|
isEditable: false,
|
||||||
|
isLowerCase: false,
|
||||||
|
isNumber: false,
|
||||||
|
isBoolean: false,
|
||||||
|
isEncrypted: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'executorSecret',
|
||||||
|
isEditable: false,
|
||||||
|
isLowerCase: false,
|
||||||
|
isNumber: false,
|
||||||
|
isBoolean: false,
|
||||||
|
isEncrypted: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'redisPassword',
|
||||||
|
isEditable: false,
|
||||||
|
isLowerCase: false,
|
||||||
|
isNumber: false,
|
||||||
|
isBoolean: false,
|
||||||
|
isEncrypted: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'mariadbHost',
|
||||||
|
isEditable: false,
|
||||||
|
isLowerCase: false,
|
||||||
|
isNumber: false,
|
||||||
|
isBoolean: false,
|
||||||
|
isEncrypted: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'mariadbPort',
|
||||||
|
isEditable: false,
|
||||||
|
isLowerCase: false,
|
||||||
|
isNumber: false,
|
||||||
|
isBoolean: false,
|
||||||
|
isEncrypted: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'mariadbUser',
|
||||||
|
isEditable: false,
|
||||||
|
isLowerCase: false,
|
||||||
|
isNumber: false,
|
||||||
|
isBoolean: false,
|
||||||
|
isEncrypted: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'mariadbPassword',
|
||||||
|
isEditable: false,
|
||||||
|
isLowerCase: false,
|
||||||
|
isNumber: false,
|
||||||
|
isBoolean: false,
|
||||||
|
isEncrypted: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'mariadbRootUser',
|
||||||
|
isEditable: false,
|
||||||
|
isLowerCase: false,
|
||||||
|
isNumber: false,
|
||||||
|
isBoolean: false,
|
||||||
|
isEncrypted: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'mariadbRootUserPassword',
|
||||||
|
isEditable: false,
|
||||||
|
isLowerCase: false,
|
||||||
|
isNumber: false,
|
||||||
|
isBoolean: false,
|
||||||
|
isEncrypted: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'mariadbDatabase',
|
||||||
|
isEditable: true,
|
||||||
|
isLowerCase: false,
|
||||||
|
isNumber: false,
|
||||||
|
isBoolean: false,
|
||||||
|
isEncrypted: false
|
||||||
|
}]
|
||||||
|
|
||||||
export const glitchTip = [{
|
export const glitchTip = [{
|
||||||
name: 'postgresqlUser',
|
name: 'postgresqlUser',
|
||||||
isEditable: false,
|
isEditable: false,
|
||||||
@ -588,4 +670,4 @@ export const glitchTip = [{
|
|||||||
isNumber: false,
|
isNumber: false,
|
||||||
isBoolean: true,
|
isBoolean: true,
|
||||||
isEncrypted: false
|
isEncrypted: false
|
||||||
}]
|
}]
|
||||||
|
35
apps/api/src/lib/services.ts
Normal file
35
apps/api/src/lib/services.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { createDirectories, getServiceFromDB, getServiceImage, getServiceMainPort, makeLabelForServices } from "./common";
|
||||||
|
|
||||||
|
export async function defaultServiceConfigurations({ id, teamId }) {
|
||||||
|
const service = await getServiceFromDB({ id, teamId });
|
||||||
|
const { destinationDockerId, destinationDocker, type, serviceSecret } = service;
|
||||||
|
|
||||||
|
const network = destinationDockerId && destinationDocker.network;
|
||||||
|
const port = getServiceMainPort(type);
|
||||||
|
|
||||||
|
const { workdir } = await createDirectories({ repository: type, buildId: id });
|
||||||
|
|
||||||
|
const image = getServiceImage(type);
|
||||||
|
let secrets = [];
|
||||||
|
if (serviceSecret.length > 0) {
|
||||||
|
serviceSecret.forEach((secret) => {
|
||||||
|
secrets.push([secret.name]=secret.value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return { ...service, network, port, workdir, image, secrets }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function defaultServiceComposeConfiguration(network: string) {
|
||||||
|
return {
|
||||||
|
networks: [network],
|
||||||
|
restart: 'always',
|
||||||
|
deploy: {
|
||||||
|
restart_policy: {
|
||||||
|
condition: 'on-failure',
|
||||||
|
delay: '10s',
|
||||||
|
max_attempts: 10,
|
||||||
|
window: '120s'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,7 @@ import cuid from 'cuid';
|
|||||||
|
|
||||||
import type { OnlyId } from '../../../../types';
|
import type { OnlyId } from '../../../../types';
|
||||||
import type { ActivateWordpressFtp, CheckService, CheckServiceDomain, DeleteServiceSecret, DeleteServiceStorage, GetServiceLogs, SaveService, SaveServiceDestination, SaveServiceSecret, SaveServiceSettings, SaveServiceStorage, SaveServiceType, SaveServiceVersion, ServiceStartStop, SetWordpressSettings } from './types';
|
import type { ActivateWordpressFtp, CheckService, CheckServiceDomain, DeleteServiceSecret, DeleteServiceStorage, GetServiceLogs, SaveService, SaveServiceDestination, SaveServiceSecret, SaveServiceSettings, SaveServiceStorage, SaveServiceType, SaveServiceVersion, ServiceStartStop, SetWordpressSettings } from './types';
|
||||||
|
import { defaultServiceComposeConfiguration, defaultServiceConfigurations } from '../../../../lib/services';
|
||||||
|
|
||||||
// async function startServiceNew(request: FastifyRequest<OnlyId>) {
|
// async function startServiceNew(request: FastifyRequest<OnlyId>) {
|
||||||
// try {
|
// try {
|
||||||
@ -29,7 +30,7 @@ import type { ActivateWordpressFtp, CheckService, CheckServiceDomain, DeleteServ
|
|||||||
// serviceSecret.forEach((secret) => {
|
// serviceSecret.forEach((secret) => {
|
||||||
// environmentVariables[secret.name] = secret.value;
|
// environmentVariables[secret.name] = secret.value;
|
||||||
// });
|
// });
|
||||||
// }
|
// }
|
||||||
// config.newVolumes = {}
|
// config.newVolumes = {}
|
||||||
// for (const service of Object.entries(config.services)) {
|
// for (const service of Object.entries(config.services)) {
|
||||||
// const name = service[0]
|
// const name = service[0]
|
||||||
@ -97,7 +98,7 @@ import type { ActivateWordpressFtp, CheckService, CheckServiceDomain, DeleteServ
|
|||||||
|
|
||||||
// }
|
// }
|
||||||
// console.log(config.services)
|
// console.log(config.services)
|
||||||
// console.log(config.volumes)
|
// console.log(config.volumes)
|
||||||
|
|
||||||
// // config.services[id] = JSON.parse(JSON.stringify(config.services[type]))
|
// // config.services[id] = JSON.parse(JSON.stringify(config.services[type]))
|
||||||
// // config.services[id].container_name = id
|
// // config.services[id].container_name = id
|
||||||
@ -197,13 +198,11 @@ export async function getService(request: FastifyRequest<OnlyId>) {
|
|||||||
const teamId = request.user.teamId;
|
const teamId = request.user.teamId;
|
||||||
const { id } = request.params;
|
const { id } = request.params;
|
||||||
const service = await getServiceFromDB({ id, teamId });
|
const service = await getServiceFromDB({ id, teamId });
|
||||||
const settings = await listSettings()
|
|
||||||
if (!service) {
|
if (!service) {
|
||||||
throw { status: 404, message: 'Service not found.' }
|
throw { status: 404, message: 'Service not found.' }
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
service,
|
service
|
||||||
settings
|
|
||||||
}
|
}
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message })
|
||||||
@ -590,6 +589,9 @@ export async function startService(request: FastifyRequest<ServiceStartStop>) {
|
|||||||
if (type === 'moodle') {
|
if (type === 'moodle') {
|
||||||
return await startMoodleService(request)
|
return await startMoodleService(request)
|
||||||
}
|
}
|
||||||
|
if (type === 'appwrite') {
|
||||||
|
return await startAppWriteService(request)
|
||||||
|
}
|
||||||
if (type === 'glitchTip') {
|
if (type === 'glitchTip') {
|
||||||
return await startGlitchTipService(request)
|
return await startGlitchTipService(request)
|
||||||
}
|
}
|
||||||
@ -643,6 +645,9 @@ export async function stopService(request: FastifyRequest<ServiceStartStop>) {
|
|||||||
if (type === 'fider') {
|
if (type === 'fider') {
|
||||||
return await stopFiderService(request)
|
return await stopFiderService(request)
|
||||||
}
|
}
|
||||||
|
if (type === 'appwrite') {
|
||||||
|
return await stopAppWriteService(request)
|
||||||
|
}
|
||||||
if (type === 'moodle') {
|
if (type === 'moodle') {
|
||||||
return await stopMoodleService(request)
|
return await stopMoodleService(request)
|
||||||
}
|
}
|
||||||
@ -2480,7 +2485,511 @@ async function stopFiderService(request: FastifyRequest<ServiceStartStop>) {
|
|||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async function startAppWriteService(request: FastifyRequest<ServiceStartStop>) {
|
||||||
|
try {
|
||||||
|
const { id } = request.params;
|
||||||
|
const teamId = request.user.teamId;
|
||||||
|
const { version, fqdn, destinationDocker, secrets, exposePort, network, port, workdir, image, appwrite } = await defaultServiceConfigurations({ id, teamId })
|
||||||
|
|
||||||
|
let isStatsEnabled = false
|
||||||
|
if (secrets._APP_USAGE_STATS) {
|
||||||
|
isStatsEnabled = true
|
||||||
|
}
|
||||||
|
const {
|
||||||
|
opensslKeyV1,
|
||||||
|
executorSecret,
|
||||||
|
mariadbHost,
|
||||||
|
mariadbPort,
|
||||||
|
mariadbUser,
|
||||||
|
mariadbPassword,
|
||||||
|
mariadbRootUser,
|
||||||
|
mariadbRootUserPassword,
|
||||||
|
mariadbDatabase
|
||||||
|
} = appwrite;
|
||||||
|
|
||||||
|
const dockerCompose = {
|
||||||
|
[id]: {
|
||||||
|
...defaultServiceComposeConfiguration(network),
|
||||||
|
image: `${image}:${version}`,
|
||||||
|
container_name: id,
|
||||||
|
labels: makeLabelForServices('appwrite'),
|
||||||
|
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||||
|
"volumes": [
|
||||||
|
`${id}-uploads:/storage/uploads:rw`,
|
||||||
|
`${id}-cache:/storage/cache:rw`,
|
||||||
|
`${id}-config:/storage/config:rw`,
|
||||||
|
`${id}-certificates:/storage/certificates:rw`,
|
||||||
|
`${id}-functions:/storage/functions:rw`
|
||||||
|
],
|
||||||
|
"depends_on": [
|
||||||
|
`${id}-mariadb`,
|
||||||
|
`${id}-redis`,
|
||||||
|
],
|
||||||
|
"environment": [
|
||||||
|
"_APP_ENV=production",
|
||||||
|
"_APP_LOCALE=en",
|
||||||
|
`_APP_OPENSSL_KEY_V1=${opensslKeyV1}`,
|
||||||
|
`_APP_DOMAIN=${fqdn}`,
|
||||||
|
`_APP_DOMAIN_TARGET=${fqdn}`,
|
||||||
|
`_APP_REDIS_HOST=${id}-redis`,
|
||||||
|
"_APP_REDIS_PORT=6379",
|
||||||
|
`_APP_DB_HOST=${mariadbHost}`,
|
||||||
|
`_APP_DB_PORT=${mariadbPort}`,
|
||||||
|
`_APP_DB_SCHEMA=${mariadbDatabase}`,
|
||||||
|
`_APP_DB_USER=${mariadbUser}`,
|
||||||
|
`_APP_DB_PASS=${mariadbPassword}`,
|
||||||
|
`_APP_INFLUXDB_HOST=${id}-influxdb`,
|
||||||
|
"_APP_INFLUXDB_PORT=8806",
|
||||||
|
`_APP_EXECUTOR_SECRET=${executorSecret}`,
|
||||||
|
`_APP_EXECUTOR_HOST=http://${id}-executor/v1`,
|
||||||
|
`_APP_STATSD_HOST=${id}-telegraf`,
|
||||||
|
"_APP_STATSD_PORT=8125",
|
||||||
|
...secrets
|
||||||
|
]
|
||||||
|
},
|
||||||
|
[`${id}-realtime`]: {
|
||||||
|
...defaultServiceComposeConfiguration(network),
|
||||||
|
image: `${image}:${version}`,
|
||||||
|
container_name: `${id}-realtime`,
|
||||||
|
entrypoint: "realtime",
|
||||||
|
labels: makeLabelForServices('appwrite'),
|
||||||
|
"depends_on": [
|
||||||
|
`${id}-mariadb`,
|
||||||
|
`${id}-redis`,
|
||||||
|
],
|
||||||
|
"environment": [
|
||||||
|
"_APP_ENV=production",
|
||||||
|
`_APP_OPENSSL_KEY_V1=${opensslKeyV1}`,
|
||||||
|
`_APP_REDIS_HOST=${id}-redis`,
|
||||||
|
"_APP_REDIS_PORT=6379",
|
||||||
|
`_APP_DB_HOST=${mariadbHost}`,
|
||||||
|
`_APP_DB_PORT=${mariadbPort}`,
|
||||||
|
`_APP_DB_SCHEMA=${mariadbDatabase}`,
|
||||||
|
`_APP_DB_USER=${mariadbUser}`,
|
||||||
|
`_APP_DB_PASS=${mariadbPassword}`,
|
||||||
|
...secrets
|
||||||
|
]
|
||||||
|
},
|
||||||
|
[`${id}-worker-audits`]: {
|
||||||
|
...defaultServiceComposeConfiguration(network),
|
||||||
|
image: `${image}:${version}`,
|
||||||
|
container_name: `${id}-worker-audits`,
|
||||||
|
labels: makeLabelForServices('appwrite'),
|
||||||
|
"entrypoint": "worker-audits",
|
||||||
|
"depends_on": [
|
||||||
|
`${id}-mariadb`,
|
||||||
|
`${id}-redis`,
|
||||||
|
],
|
||||||
|
"environment": [
|
||||||
|
"_APP_ENV=production",
|
||||||
|
`_APP_OPENSSL_KEY_V1=${opensslKeyV1}`,
|
||||||
|
`_APP_REDIS_HOST=${id}-redis`,
|
||||||
|
"_APP_REDIS_PORT=6379",
|
||||||
|
`_APP_DB_HOST=${mariadbHost}`,
|
||||||
|
`_APP_DB_PORT=${mariadbPort}`,
|
||||||
|
`_APP_DB_SCHEMA=${mariadbDatabase}`,
|
||||||
|
`_APP_DB_USER=${mariadbUser}`,
|
||||||
|
`_APP_DB_PASS=${mariadbPassword}`,
|
||||||
|
...secrets
|
||||||
|
]
|
||||||
|
},
|
||||||
|
[`${id}-worker-webhooks`]: {
|
||||||
|
...defaultServiceComposeConfiguration(network),
|
||||||
|
image: `${image}:${version}`,
|
||||||
|
container_name: `${id}-worker-webhooks`,
|
||||||
|
labels: makeLabelForServices('appwrite'),
|
||||||
|
"entrypoint": "worker-webhooks",
|
||||||
|
"depends_on": [
|
||||||
|
`${id}-mariadb`,
|
||||||
|
`${id}-redis`,
|
||||||
|
],
|
||||||
|
"environment": [
|
||||||
|
"_APP_ENV=production",
|
||||||
|
`_APP_OPENSSL_KEY_V1=${opensslKeyV1}`,
|
||||||
|
`_APP_REDIS_HOST=${id}-redis`,
|
||||||
|
"_APP_REDIS_PORT=6379",
|
||||||
|
...secrets
|
||||||
|
]
|
||||||
|
},
|
||||||
|
[`${id}-worker-deletes`]: {
|
||||||
|
...defaultServiceComposeConfiguration(network),
|
||||||
|
image: `${image}:${version}`,
|
||||||
|
container_name: `${id}-worker-deletes`,
|
||||||
|
labels: makeLabelForServices('appwrite'),
|
||||||
|
"entrypoint": "worker-deletes",
|
||||||
|
"depends_on": [
|
||||||
|
`${id}-mariadb`,
|
||||||
|
`${id}-redis`,
|
||||||
|
],
|
||||||
|
"volumes": [
|
||||||
|
`${id}-uploads:/storage/uploads:rw`,
|
||||||
|
`${id}-cache:/storage/cache:rw`,
|
||||||
|
`${id}-config:/storage/config:rw`,
|
||||||
|
`${id}-certificates:/storage/certificates:rw`,
|
||||||
|
`${id}-functions:/storage/functions:rw`,
|
||||||
|
`${id}-builds:/storage/builds:rw`,
|
||||||
|
],
|
||||||
|
"environment": [
|
||||||
|
"_APP_ENV=production",
|
||||||
|
`_APP_OPENSSL_KEY_V1=${opensslKeyV1}`,
|
||||||
|
`_APP_REDIS_HOST=${id}-redis`,
|
||||||
|
"_APP_REDIS_PORT=6379",
|
||||||
|
`_APP_DB_HOST=${mariadbHost}`,
|
||||||
|
`_APP_DB_PORT=${mariadbPort}`,
|
||||||
|
`_APP_DB_SCHEMA=${mariadbDatabase}`,
|
||||||
|
`_APP_DB_USER=${mariadbUser}`,
|
||||||
|
`_APP_DB_PASS=${mariadbPassword}`,
|
||||||
|
`_APP_EXECUTOR_SECRET=${executorSecret}`,
|
||||||
|
`_APP_EXECUTOR_HOST=http://${id}-executor/v1`,
|
||||||
|
...secrets
|
||||||
|
]
|
||||||
|
},
|
||||||
|
[`${id}-worker-databases`]: {
|
||||||
|
...defaultServiceComposeConfiguration(network),
|
||||||
|
image: `${image}:${version}`,
|
||||||
|
container_name: `${id}-worker-databases`,
|
||||||
|
labels: makeLabelForServices('appwrite'),
|
||||||
|
"entrypoint": "worker-databases",
|
||||||
|
"depends_on": [
|
||||||
|
`${id}-mariadb`,
|
||||||
|
`${id}-redis`,
|
||||||
|
],
|
||||||
|
"environment": [
|
||||||
|
"_APP_ENV=production",
|
||||||
|
`_APP_OPENSSL_KEY_V1=${opensslKeyV1}`,
|
||||||
|
`_APP_REDIS_HOST=${id}-redis`,
|
||||||
|
"_APP_REDIS_PORT=6379",
|
||||||
|
`_APP_DB_HOST=${mariadbHost}`,
|
||||||
|
`_APP_DB_PORT=${mariadbPort}`,
|
||||||
|
`_APP_DB_SCHEMA=${mariadbDatabase}`,
|
||||||
|
`_APP_DB_USER=${mariadbUser}`,
|
||||||
|
`_APP_DB_PASS=${mariadbPassword}`,
|
||||||
|
...secrets
|
||||||
|
]
|
||||||
|
},
|
||||||
|
[`${id}-worker-builds`]: {
|
||||||
|
...defaultServiceComposeConfiguration(network),
|
||||||
|
image: `${image}:${version}`,
|
||||||
|
container_name: `${id}-worker-builds`,
|
||||||
|
labels: makeLabelForServices('appwrite'),
|
||||||
|
"entrypoint": "worker-builds",
|
||||||
|
"depends_on": [
|
||||||
|
`${id}-mariadb`,
|
||||||
|
`${id}-redis`,
|
||||||
|
],
|
||||||
|
"environment": [
|
||||||
|
"_APP_ENV=production",
|
||||||
|
`_APP_OPENSSL_KEY_V1=${opensslKeyV1}`,
|
||||||
|
`_APP_EXECUTOR_SECRET=${executorSecret}`,
|
||||||
|
`_APP_EXECUTOR_HOST=http://${id}-executor/v1`,
|
||||||
|
`_APP_REDIS_HOST=${id}-redis`,
|
||||||
|
"_APP_REDIS_PORT=6379",
|
||||||
|
`_APP_DB_HOST=${mariadbHost}`,
|
||||||
|
`_APP_DB_PORT=${mariadbPort}`,
|
||||||
|
`_APP_DB_SCHEMA=${mariadbDatabase}`,
|
||||||
|
`_APP_DB_USER=${mariadbUser}`,
|
||||||
|
`_APP_DB_PASS=${mariadbPassword}`,
|
||||||
|
...secrets
|
||||||
|
]
|
||||||
|
},
|
||||||
|
[`${id}-worker-certificates`]: {
|
||||||
|
...defaultServiceComposeConfiguration(network),
|
||||||
|
image: `${image}:${version}`,
|
||||||
|
container_name: `${id}-worker-certificates`,
|
||||||
|
labels: makeLabelForServices('appwrite'),
|
||||||
|
"entrypoint": "worker-certificates",
|
||||||
|
"depends_on": [
|
||||||
|
`${id}-mariadb`,
|
||||||
|
`${id}-redis`,
|
||||||
|
],
|
||||||
|
"volumes": [
|
||||||
|
`${id}-config:/storage/config:rw`,
|
||||||
|
`${id}-certificates:/storage/certificates:rw`,
|
||||||
|
],
|
||||||
|
"environment": [
|
||||||
|
"_APP_ENV=production",
|
||||||
|
`_APP_OPENSSL_KEY_V1=${opensslKeyV1}`,
|
||||||
|
`_APP_DOMAIN=${fqdn}`,
|
||||||
|
`_APP_DOMAIN_TARGET=${fqdn}`,
|
||||||
|
`_APP_REDIS_HOST=${id}-redis`,
|
||||||
|
"_APP_REDIS_PORT=6379",
|
||||||
|
`_APP_DB_HOST=${mariadbHost}`,
|
||||||
|
`_APP_DB_PORT=${mariadbPort}`,
|
||||||
|
`_APP_DB_SCHEMA=${mariadbDatabase}`,
|
||||||
|
`_APP_DB_USER=${mariadbUser}`,
|
||||||
|
`_APP_DB_PASS=${mariadbPassword}`,
|
||||||
|
...secrets
|
||||||
|
]
|
||||||
|
},
|
||||||
|
[`${id}-worker-functions`]: {
|
||||||
|
...defaultServiceComposeConfiguration(network),
|
||||||
|
image: `${image}:${version}`,
|
||||||
|
container_name: `${id}-worker-functions`,
|
||||||
|
labels: makeLabelForServices('appwrite'),
|
||||||
|
"entrypoint": "worker-functions",
|
||||||
|
"depends_on": [
|
||||||
|
`${id}-mariadb`,
|
||||||
|
`${id}-redis`,
|
||||||
|
`${id}-executor`
|
||||||
|
],
|
||||||
|
"environment": [
|
||||||
|
"_APP_ENV=production",
|
||||||
|
`_APP_OPENSSL_KEY_V1=${opensslKeyV1}`,
|
||||||
|
`_APP_REDIS_HOST=${id}-redis`,
|
||||||
|
"_APP_REDIS_PORT=6379",
|
||||||
|
`_APP_DB_HOST=${mariadbHost}`,
|
||||||
|
`_APP_DB_PORT=${mariadbPort}`,
|
||||||
|
`_APP_DB_SCHEMA=${mariadbDatabase}`,
|
||||||
|
`_APP_DB_USER=${mariadbUser}`,
|
||||||
|
`_APP_DB_PASS=${mariadbPassword}`,
|
||||||
|
`_APP_EXECUTOR_SECRET=${executorSecret}`,
|
||||||
|
`_APP_EXECUTOR_HOST=http://${id}-executor/v1`,
|
||||||
|
...secrets
|
||||||
|
]
|
||||||
|
},
|
||||||
|
[`${id}-executor`]: {
|
||||||
|
...defaultServiceComposeConfiguration(network),
|
||||||
|
image: `${image}:${version}`,
|
||||||
|
container_name: `${id}-executor`,
|
||||||
|
labels: makeLabelForServices('appwrite'),
|
||||||
|
"entrypoint": "executor",
|
||||||
|
"stop_signal": "SIGINT",
|
||||||
|
"volumes": [
|
||||||
|
`${id}-functions:/storage/functions:rw`,
|
||||||
|
`${id}-builds:/storage/builds:rw`,
|
||||||
|
"/var/run/docker.sock:/var/run/docker.sock",
|
||||||
|
"/tmp:/tmp:rw"
|
||||||
|
],
|
||||||
|
"depends_on": [
|
||||||
|
`${id}-mariadb`,
|
||||||
|
`${id}-redis`,
|
||||||
|
`${id}`
|
||||||
|
],
|
||||||
|
"environment": [
|
||||||
|
"_APP_ENV=production",
|
||||||
|
`_APP_EXECUTOR_SECRET=${executorSecret}`,
|
||||||
|
...secrets
|
||||||
|
]
|
||||||
|
},
|
||||||
|
[`${id}-worker-mails`]: {
|
||||||
|
...defaultServiceComposeConfiguration(network),
|
||||||
|
image: `${image}:${version}`,
|
||||||
|
container_name: `${id}-worker-mails`,
|
||||||
|
labels: makeLabelForServices('appwrite'),
|
||||||
|
"entrypoint": "worker-mails",
|
||||||
|
"depends_on": [
|
||||||
|
`${id}-redis`,
|
||||||
|
],
|
||||||
|
"environment": [
|
||||||
|
"_APP_ENV=production",
|
||||||
|
`_APP_OPENSSL_KEY_V1=${opensslKeyV1}`,
|
||||||
|
`_APP_REDIS_HOST=${id}-redis`,
|
||||||
|
"_APP_REDIS_PORT=6379",
|
||||||
|
...secrets
|
||||||
|
]
|
||||||
|
},
|
||||||
|
[`${id}-worker-messaging`]: {
|
||||||
|
...defaultServiceComposeConfiguration(network),
|
||||||
|
image: `${image}:${version}`,
|
||||||
|
container_name: `${id}-worker-messaging`,
|
||||||
|
labels: makeLabelForServices('appwrite'),
|
||||||
|
"entrypoint": "worker-messaging",
|
||||||
|
"depends_on": [
|
||||||
|
`${id}-redis`,
|
||||||
|
],
|
||||||
|
"environment": [
|
||||||
|
"_APP_ENV=production",
|
||||||
|
`_APP_REDIS_HOST=${id}-redis`,
|
||||||
|
"_APP_REDIS_PORT=6379",
|
||||||
|
...secrets
|
||||||
|
]
|
||||||
|
},
|
||||||
|
[`${id}-maintenance`]: {
|
||||||
|
...defaultServiceComposeConfiguration(network),
|
||||||
|
image: `${image}:${version}`,
|
||||||
|
container_name: `${id}-maintenance`,
|
||||||
|
labels: makeLabelForServices('appwrite'),
|
||||||
|
"entrypoint": "maintenance",
|
||||||
|
"depends_on": [
|
||||||
|
`${id}-redis`,
|
||||||
|
],
|
||||||
|
"environment": [
|
||||||
|
"_APP_ENV=production",
|
||||||
|
`_APP_OPENSSL_KEY_V1=${opensslKeyV1}`,
|
||||||
|
`_APP_DOMAIN=${fqdn}`,
|
||||||
|
`_APP_DOMAIN_TARGET=${fqdn}`,
|
||||||
|
`_APP_REDIS_HOST=${id}-redis`,
|
||||||
|
"_APP_REDIS_PORT=6379",
|
||||||
|
`_APP_DB_HOST=${mariadbHost}`,
|
||||||
|
`_APP_DB_PORT=${mariadbPort}`,
|
||||||
|
`_APP_DB_SCHEMA=${mariadbDatabase}`,
|
||||||
|
`_APP_DB_USER=${mariadbUser}`,
|
||||||
|
`_APP_DB_PASS=${mariadbPassword}`,
|
||||||
|
...secrets
|
||||||
|
]
|
||||||
|
},
|
||||||
|
[`${id}-schedule`]: {
|
||||||
|
...defaultServiceComposeConfiguration(network),
|
||||||
|
image: `${image}:${version}`,
|
||||||
|
container_name: `${id}-schedule`,
|
||||||
|
labels: makeLabelForServices('appwrite'),
|
||||||
|
"entrypoint": "schedule",
|
||||||
|
"depends_on": [
|
||||||
|
`${id}-redis`,
|
||||||
|
],
|
||||||
|
"environment": [
|
||||||
|
"_APP_ENV=production",
|
||||||
|
`_APP_REDIS_HOST=${id}-redis`,
|
||||||
|
"_APP_REDIS_PORT=6379",
|
||||||
|
...secrets
|
||||||
|
]
|
||||||
|
},
|
||||||
|
[`${id}-mariadb`]: {
|
||||||
|
...defaultServiceComposeConfiguration(network),
|
||||||
|
"image": "mariadb:10.7",
|
||||||
|
container_name: `${id}-mariadb`,
|
||||||
|
labels: makeLabelForServices('appwrite'),
|
||||||
|
"volumes": [
|
||||||
|
`${id}-mariadb:/var/lib/mysql:rw`
|
||||||
|
],
|
||||||
|
"environment": [
|
||||||
|
`MYSQL_ROOT_USER=${mariadbRootUser}`,
|
||||||
|
`MYSQL_ROOT_PASSWORD=${mariadbRootUserPassword}`,
|
||||||
|
`MYSQL_USER=${mariadbUser}`,
|
||||||
|
`MYSQL_PASSWORD=${mariadbPassword}`,
|
||||||
|
`MYSQL_DATABASE=${mariadbDatabase}`
|
||||||
|
],
|
||||||
|
"command": "mysqld --innodb-flush-method=fsync"
|
||||||
|
},
|
||||||
|
[`${id}-redis`]: {
|
||||||
|
...defaultServiceComposeConfiguration(network),
|
||||||
|
"image": "redis:6.2-alpine",
|
||||||
|
container_name: `${id}-redis`,
|
||||||
|
"command": `redis-server --maxmemory 512mb --maxmemory-policy allkeys-lru --maxmemory-samples 5\n`,
|
||||||
|
"volumes": [
|
||||||
|
`${id}-redis:/data:rw`
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
if (isStatsEnabled) {
|
||||||
|
dockerCompose.id.depends_on.push(`${id}-influxdb`);
|
||||||
|
dockerCompose[`${id}-usage`] = {
|
||||||
|
...defaultServiceComposeConfiguration(network),
|
||||||
|
image: `${image}:${version}`,
|
||||||
|
container_name: `${id}-usage`,
|
||||||
|
labels: makeLabelForServices('appwrite'),
|
||||||
|
"entrypoint": "usage",
|
||||||
|
"depends_on": [
|
||||||
|
`${id}-mariadb`,
|
||||||
|
`${id}-influxdb`,
|
||||||
|
],
|
||||||
|
"environment": [
|
||||||
|
"_APP_ENV=production",
|
||||||
|
`_APP_OPENSSL_KEY_V1=${opensslKeyV1}`,
|
||||||
|
`_APP_DB_HOST=${mariadbHost}`,
|
||||||
|
`_APP_DB_PORT=${mariadbPort}`,
|
||||||
|
`_APP_DB_SCHEMA=${mariadbDatabase}`,
|
||||||
|
`_APP_DB_USER=${mariadbUser}`,
|
||||||
|
`_APP_DB_PASS=${mariadbPassword}`,
|
||||||
|
`_APP_INFLUXDB_HOST=${id}-influxdb`,
|
||||||
|
"_APP_INFLUXDB_PORT=8806",
|
||||||
|
`_APP_REDIS_HOST=${id}-redis`,
|
||||||
|
"_APP_REDIS_PORT=6379",
|
||||||
|
...secrets
|
||||||
|
]
|
||||||
|
}
|
||||||
|
dockerCompose[`${id}-influxdb`] = {
|
||||||
|
...defaultServiceComposeConfiguration(network),
|
||||||
|
"image": "appwrite/influxdb:1.5.0",
|
||||||
|
container_name: `${id}-influxdb`,
|
||||||
|
"volumes": [
|
||||||
|
`${id}-influxdb:/var/lib/influxdb:rw`
|
||||||
|
]
|
||||||
|
}
|
||||||
|
dockerCompose[`${id}-telegraf`] = {
|
||||||
|
...defaultServiceComposeConfiguration(network),
|
||||||
|
"image": "appwrite/telegraf:1.4.0",
|
||||||
|
container_name: `${id}-telegraf`,
|
||||||
|
"environment": [
|
||||||
|
`_APP_INFLUXDB_HOST=${id}-influxdb`,
|
||||||
|
"_APP_INFLUXDB_PORT=8806",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const composeFile: any = {
|
||||||
|
version: '3.8',
|
||||||
|
services: dockerCompose,
|
||||||
|
networks: {
|
||||||
|
[network]: {
|
||||||
|
external: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
volumes: {
|
||||||
|
[`${id}-uploads`]: {
|
||||||
|
name: `${id}-uploads`
|
||||||
|
},
|
||||||
|
[`${id}-cache`]: {
|
||||||
|
name: `${id}-cache`
|
||||||
|
},
|
||||||
|
[`${id}-config`]: {
|
||||||
|
name: `${id}-config`
|
||||||
|
},
|
||||||
|
[`${id}-certificates`]: {
|
||||||
|
name: `${id}-certificates`
|
||||||
|
},
|
||||||
|
[`${id}-functions`]: {
|
||||||
|
name: `${id}-functions`
|
||||||
|
},
|
||||||
|
[`${id}-builds`]: {
|
||||||
|
name: `${id}-builds`
|
||||||
|
},
|
||||||
|
[`${id}-mariadb`]: {
|
||||||
|
name: `${id}-mariadb`
|
||||||
|
},
|
||||||
|
[`${id}-redis`]: {
|
||||||
|
name: `${id}-redis`
|
||||||
|
},
|
||||||
|
[`${id}-influxdb`]: {
|
||||||
|
name: `${id}-influxdb`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
const composeFileDestination = `${workdir}/docker-compose.yaml`;
|
||||||
|
await fs.writeFile(composeFileDestination, yaml.dump(composeFile));
|
||||||
|
|
||||||
|
await executeDockerCmd({ dockerId: destinationDocker.id, command: `docker compose -f ${composeFileDestination} pull` })
|
||||||
|
await executeDockerCmd({ dockerId: destinationDocker.id, command: `docker compose -f ${composeFileDestination} up --build -d` })
|
||||||
|
|
||||||
|
return {}
|
||||||
|
} catch ({ status, message }) {
|
||||||
|
return errorHandler({ status, message })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function stopAppWriteService(request: FastifyRequest<ServiceStartStop>) {
|
||||||
|
try {
|
||||||
|
// TODO: Fix async for of
|
||||||
|
const { id } = request.params;
|
||||||
|
const teamId = request.user.teamId;
|
||||||
|
const service = await getServiceFromDB({ id, teamId });
|
||||||
|
const { destinationDockerId, destinationDocker } = service;
|
||||||
|
const containers = [`${id}-mariadb`, `${id}-redis`, `${id}-influxdb`, `${id}-telegraf`, id, `${id}-realtime`, `${id}-worker-audits`, `${id}worker-webhooks`, `${id}-worker-deletes`, `${id}-worker-databases`, `${id}-worker-builds`, `${id}-worker-certificates`, `${id}-worker-functions`, `${id}-worker-mails`, `${id}-worker-messaging`, `${id}-maintenance`, `${id}-schedule`, `${id}-executor`, `${id}-usage`]
|
||||||
|
if (destinationDockerId) {
|
||||||
|
for (const container of containers) {
|
||||||
|
const found = await checkContainer({ dockerId: destinationDocker.id, container });
|
||||||
|
if (found) {
|
||||||
|
await removeContainer({ id, dockerId: destinationDocker.id });
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {}
|
||||||
|
} catch ({ status, message }) {
|
||||||
|
return errorHandler({ status, message })
|
||||||
|
}
|
||||||
|
}
|
||||||
async function startMoodleService(request: FastifyRequest<ServiceStartStop>) {
|
async function startMoodleService(request: FastifyRequest<ServiceStartStop>) {
|
||||||
try {
|
try {
|
||||||
const { id } = request.params;
|
const { id } = request.params;
|
||||||
|
@ -148,6 +148,17 @@ export const supportedServiceTypesAndVersions = [
|
|||||||
main: 3000
|
main: 3000
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'appwrite',
|
||||||
|
fancyName: 'Appwrite',
|
||||||
|
baseImage: 'appwrite/appwrite',
|
||||||
|
images: ['mariadb:10.7', 'redis:6.2-alpine', 'appwrite/telegraf:1.4.0'],
|
||||||
|
versions: ['latest', '0.15.3'],
|
||||||
|
recommendedVersion: '0.15.3',
|
||||||
|
ports: {
|
||||||
|
main: 80
|
||||||
|
}
|
||||||
|
}
|
||||||
// {
|
// {
|
||||||
// name: 'moodle',
|
// name: 'moodle',
|
||||||
// fancyName: 'Moodle',
|
// fancyName: 'Moodle',
|
||||||
|
@ -13,5 +13,6 @@ export { default as MeiliSearch } from './MeiliSearch.svelte';
|
|||||||
export { default as Umami } from './Umami.svelte';
|
export { default as Umami } from './Umami.svelte';
|
||||||
export { default as Hasura } from './Hasura.svelte';
|
export { default as Hasura } from './Hasura.svelte';
|
||||||
export { default as Fider } from './Fider.svelte';
|
export { default as Fider } from './Fider.svelte';
|
||||||
|
export { default as Appwrite } from './Moodle.svelte';
|
||||||
export { default as Moodle } from './Moodle.svelte';
|
export { default as Moodle } from './Moodle.svelte';
|
||||||
export { default as GlitchTip } from './GlitchTip.svelte';
|
export { default as GlitchTip } from './GlitchTip.svelte';
|
||||||
|
@ -292,8 +292,7 @@ export const buildPacks = [
|
|||||||
fancyName: 'Deno',
|
fancyName: 'Deno',
|
||||||
hoverColor: 'hover:bg-green-700',
|
hoverColor: 'hover:bg-green-700',
|
||||||
color: 'bg-green-700'
|
color: 'bg-green-700'
|
||||||
}
|
},
|
||||||
// },
|
|
||||||
// {
|
// {
|
||||||
// name: 'heroku',
|
// name: 'heroku',
|
||||||
// fancyName: 'Heroku Buildpack',
|
// fancyName: 'Heroku Buildpack',
|
||||||
|
@ -59,7 +59,7 @@
|
|||||||
|
|
||||||
async function changeSettings(name: any) {
|
async function changeSettings(name: any) {
|
||||||
if (name !== 'appendOnly') {
|
if (name !== 'appendOnly') {
|
||||||
if (publicLoading || !$status.database.isRunning || name !== 'appendOnly') return;
|
if (publicLoading || !$status.database.isRunning) return;
|
||||||
}
|
}
|
||||||
publicLoading = true;
|
publicLoading = true;
|
||||||
let data = {
|
let data = {
|
||||||
@ -247,6 +247,7 @@
|
|||||||
{#if database.type === 'redis'}
|
{#if database.type === 'redis'}
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center">
|
||||||
<Setting
|
<Setting
|
||||||
|
loading={publicLoading}
|
||||||
bind:setting={appendOnly}
|
bind:setting={appendOnly}
|
||||||
on:click={() => changeSettings('appendOnly')}
|
on:click={() => changeSettings('appendOnly')}
|
||||||
title={$t('database.change_append_only_mode')}
|
title={$t('database.change_append_only_mode')}
|
||||||
|
@ -97,6 +97,7 @@
|
|||||||
<div class="mt-10 pb-12 tracking-tight sm:pb-16">
|
<div class="mt-10 pb-12 tracking-tight sm:pb-16">
|
||||||
<div class="mx-auto px-10">
|
<div class="mx-auto px-10">
|
||||||
<div class="flex flex-col justify-center xl:flex-row">
|
<div class="flex flex-col justify-center xl:flex-row">
|
||||||
|
{#if applications.length > 0}
|
||||||
<div>
|
<div>
|
||||||
<div class="title">Resources</div>
|
<div class="title">Resources</div>
|
||||||
<div class="flex items-start justify-center p-8">
|
<div class="flex items-start justify-center p-8">
|
||||||
@ -308,6 +309,7 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
{#if $appSession.teamId === '0'}
|
{#if $appSession.teamId === '0'}
|
||||||
<Usage />
|
<Usage />
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -55,6 +55,10 @@
|
|||||||
<a href="https://fider.io" target="_blank">
|
<a href="https://fider.io" target="_blank">
|
||||||
<Icons.Fider />
|
<Icons.Fider />
|
||||||
</a>
|
</a>
|
||||||
|
{:else if service.type === 'appwrote'}
|
||||||
|
<a href="https://appwrite.io" target="_blank">
|
||||||
|
<Icons.Appwrite/>
|
||||||
|
</a>
|
||||||
{:else if service.type === 'moodle'}
|
{:else if service.type === 'moodle'}
|
||||||
<a href="https://moodle.org" target="_blank">
|
<a href="https://moodle.org" target="_blank">
|
||||||
<Icons.Moodle />
|
<Icons.Moodle />
|
||||||
|
126
apps/ui/src/routes/services/[id]/_Services/_Appwrite.svelte
Normal file
126
apps/ui/src/routes/services/[id]/_Services/_Appwrite.svelte
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
|
||||||
|
import { t } from '$lib/translations';
|
||||||
|
import Select from 'svelte-select';
|
||||||
|
export let service: any;
|
||||||
|
export let readOnly: any;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex space-x-1 py-5 font-bold">
|
||||||
|
<div class="title">Appwrite</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="opensslKeyV1">Encryption Key</label>
|
||||||
|
<CopyPasswordField
|
||||||
|
name="opensslKeyV1"
|
||||||
|
id="opensslKeyV1"
|
||||||
|
isPasswordField
|
||||||
|
value={service.appwrite.opensslKeyV1}
|
||||||
|
readonly
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="executorSecret">Executor Secret</label>
|
||||||
|
<CopyPasswordField
|
||||||
|
name="executorSecret"
|
||||||
|
id="executorSecret"
|
||||||
|
isPasswordField
|
||||||
|
value={service.appwrite.executorSecret}
|
||||||
|
readonly
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <div class="flex space-x-1 py-5 font-bold">
|
||||||
|
<div class="title">Redis</div>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="redisPassword">Password</label>
|
||||||
|
<CopyPasswordField
|
||||||
|
name="redisPassword"
|
||||||
|
id="redisPassword"
|
||||||
|
isPasswordField
|
||||||
|
value={service.appwrite.redisPassword}
|
||||||
|
readonly
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</div> -->
|
||||||
|
|
||||||
|
<div class="flex space-x-1 py-5 font-bold">
|
||||||
|
<div class="title">MariaDB</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="mariadbHost">MariaDB Host</label>
|
||||||
|
<CopyPasswordField
|
||||||
|
name="mariadbHost"
|
||||||
|
id="mariadbHost"
|
||||||
|
value={service.appwrite.mariadbHost}
|
||||||
|
readonly
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="mariadbPort">MariaDB Port</label>
|
||||||
|
<CopyPasswordField
|
||||||
|
name="mariadbPort"
|
||||||
|
id="mariadbPort"
|
||||||
|
value={service.appwrite.mariadbPort}
|
||||||
|
readonly
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</div> -->
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="mariadbUser">{$t('forms.username')}</label>
|
||||||
|
<CopyPasswordField
|
||||||
|
name="mariadbUser"
|
||||||
|
id="mariadbUser"
|
||||||
|
value={service.appwrite.mariadbUser}
|
||||||
|
readonly
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="mariadbPassword">{$t('forms.password')}</label>
|
||||||
|
<CopyPasswordField
|
||||||
|
id="mariadbPassword"
|
||||||
|
isPasswordField
|
||||||
|
readonly
|
||||||
|
disabled
|
||||||
|
name="mariadbPassword"
|
||||||
|
value={service.appwrite.mariadbPassword}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="mariadbRootUser">Root User</label>
|
||||||
|
<CopyPasswordField
|
||||||
|
name="mariadbRootUser"
|
||||||
|
id="mariadbRootUser"
|
||||||
|
value={service.appwrite.mariadbRootUser}
|
||||||
|
readonly
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="mariadbRootUserPassword">Root Password</label>
|
||||||
|
<CopyPasswordField
|
||||||
|
id="mariadbRootUserPassword"
|
||||||
|
isPasswordField
|
||||||
|
readonly
|
||||||
|
disabled
|
||||||
|
name="mariadbRootUserPassword"
|
||||||
|
value={service.appwrite.mariadbRootUserPassword}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="mariadbDatabase">{$t('index.database')}</label>
|
||||||
|
<CopyPasswordField
|
||||||
|
name="mariadbDatabase"
|
||||||
|
id="mariadbDatabase"
|
||||||
|
value={service.appwrite.mariadbDatabase}
|
||||||
|
readonly
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</div>
|
@ -27,6 +27,7 @@
|
|||||||
import Umami from './_Umami.svelte';
|
import Umami from './_Umami.svelte';
|
||||||
import VsCodeServer from './_VSCodeServer.svelte';
|
import VsCodeServer from './_VSCodeServer.svelte';
|
||||||
import Wordpress from './_Wordpress.svelte';
|
import Wordpress from './_Wordpress.svelte';
|
||||||
|
import Appwrite from './_Appwrite.svelte';
|
||||||
import Moodle from './_Moodle.svelte';
|
import Moodle from './_Moodle.svelte';
|
||||||
|
|
||||||
const { id } = $page.params;
|
const { id } = $page.params;
|
||||||
@ -395,6 +396,8 @@
|
|||||||
<Hasura bind:service />
|
<Hasura bind:service />
|
||||||
{:else if service.type === 'fider'}
|
{:else if service.type === 'fider'}
|
||||||
<Fider bind:service {readOnly} />
|
<Fider bind:service {readOnly} />
|
||||||
|
{:else if service.type === 'appwrite'}
|
||||||
|
<Appwrite bind:service {readOnly} />
|
||||||
{:else if service.type === 'moodle'}
|
{:else if service.type === 'moodle'}
|
||||||
<Moodle bind:service {readOnly} />
|
<Moodle bind:service {readOnly} />
|
||||||
{:else if service.type === 'glitchTip'}
|
{:else if service.type === 'glitchTip'}
|
||||||
|
@ -35,7 +35,6 @@
|
|||||||
|
|
||||||
const { id } = $page.params;
|
const { id } = $page.params;
|
||||||
const from = $page.url.searchParams.get('from');
|
const from = $page.url.searchParams.get('from');
|
||||||
|
|
||||||
let recommendedVersion = supportedServiceTypesAndVersions.find(
|
let recommendedVersion = supportedServiceTypesAndVersions.find(
|
||||||
({ name }) => name === type
|
({ name }) => name === type
|
||||||
)?.recommendedVersion;
|
)?.recommendedVersion;
|
||||||
|
3
csb.nix
3
csb.nix
@ -1,9 +1,10 @@
|
|||||||
with import <nixpkgs> {};
|
with import <nixpkgs> {};
|
||||||
|
|
||||||
stdenv.mkDerivation {
|
stdenv.mkDerivation {
|
||||||
name = "git";
|
name = "environment";
|
||||||
buildInputs = [
|
buildInputs = [
|
||||||
git
|
git
|
||||||
git-lfs
|
git-lfs
|
||||||
|
docker-compose
|
||||||
];
|
];
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "coolify",
|
"name": "coolify",
|
||||||
"description": "An open-source & self-hostable Heroku / Netlify alternative.",
|
"description": "An open-source & self-hostable Heroku / Netlify alternative.",
|
||||||
"version": "3.3.2",
|
"version": "3.4.0",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"repository": "github:coollabsio/coolify",
|
"repository": "github:coollabsio/coolify",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user