commit
d93506a18c
@ -0,0 +1,20 @@
|
|||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_ApplicationSettings" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"applicationId" TEXT NOT NULL,
|
||||||
|
"dualCerts" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"debug" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"previews" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"autodeploy" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"isBot" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
CONSTRAINT "ApplicationSettings_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
INSERT INTO "new_ApplicationSettings" ("applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "previews", "updatedAt") SELECT "applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "previews", "updatedAt" FROM "ApplicationSettings";
|
||||||
|
DROP TABLE "ApplicationSettings";
|
||||||
|
ALTER TABLE "new_ApplicationSettings" RENAME TO "ApplicationSettings";
|
||||||
|
CREATE UNIQUE INDEX "ApplicationSettings_applicationId_key" ON "ApplicationSettings"("applicationId");
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON;
|
@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Setting" ADD COLUMN "DNSServers" TEXT;
|
@ -20,6 +20,7 @@ model Setting {
|
|||||||
proxyHash String?
|
proxyHash String?
|
||||||
isAutoUpdateEnabled Boolean @default(false)
|
isAutoUpdateEnabled Boolean @default(false)
|
||||||
isDNSCheckEnabled Boolean @default(true)
|
isDNSCheckEnabled Boolean @default(true)
|
||||||
|
DNSServers String?
|
||||||
isTraefikUsed Boolean @default(true)
|
isTraefikUsed Boolean @default(true)
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
@ -124,6 +125,7 @@ model ApplicationSettings {
|
|||||||
debug Boolean @default(false)
|
debug Boolean @default(false)
|
||||||
previews Boolean @default(false)
|
previews Boolean @default(false)
|
||||||
autodeploy Boolean @default(true)
|
autodeploy Boolean @default(true)
|
||||||
|
isBot Boolean @default(false)
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
application Application @relation(fields: [applicationId], references: [id])
|
application Application @relation(fields: [applicationId], references: [id])
|
||||||
|
@ -5,8 +5,10 @@ import env from '@fastify/env';
|
|||||||
import cookie from '@fastify/cookie';
|
import cookie from '@fastify/cookie';
|
||||||
import path, { join } from 'path';
|
import path, { join } from 'path';
|
||||||
import autoLoad from '@fastify/autoload';
|
import autoLoad from '@fastify/autoload';
|
||||||
import { asyncExecShell, isDev, listSettings, prisma } from './lib/common';
|
import { asyncExecShell, isDev, listSettings, prisma, version } from './lib/common';
|
||||||
import { scheduler } from './lib/scheduler';
|
import { scheduler } from './lib/scheduler';
|
||||||
|
import axios from 'axios';
|
||||||
|
import compareVersions from 'compare-versions';
|
||||||
|
|
||||||
declare module 'fastify' {
|
declare module 'fastify' {
|
||||||
interface FastifyInstance {
|
interface FastifyInstance {
|
||||||
@ -113,8 +115,22 @@ fastify.listen({ port, host }, async (err: any, address: any) => {
|
|||||||
setInterval(async () => {
|
setInterval(async () => {
|
||||||
const { isAutoUpdateEnabled } = await prisma.setting.findFirst();
|
const { isAutoUpdateEnabled } = await prisma.setting.findFirst();
|
||||||
if (isAutoUpdateEnabled) {
|
if (isAutoUpdateEnabled) {
|
||||||
if (scheduler.workers.has('deployApplication')) {
|
const currentVersion = version;
|
||||||
scheduler.workers.get('deployApplication').postMessage("status:autoUpdater");
|
const { data: versions } = await axios
|
||||||
|
.get(
|
||||||
|
`https://get.coollabs.io/versions.json`
|
||||||
|
, {
|
||||||
|
params: {
|
||||||
|
appId: process.env['COOLIFY_APP_ID'] || undefined,
|
||||||
|
version: currentVersion
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const latestVersion = versions['coolify'].main.version;
|
||||||
|
const isUpdateAvailable = compareVersions(latestVersion, currentVersion);
|
||||||
|
if (isUpdateAvailable === 1) {
|
||||||
|
if (scheduler.workers.has('deployApplication')) {
|
||||||
|
scheduler.workers.get('deployApplication').postMessage("status:autoUpdater");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, isDev ? 5000 : 60000 * 15)
|
}, isDev ? 5000 : 60000 * 15)
|
||||||
|
@ -298,7 +298,6 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
console.log({port})
|
|
||||||
const composeFile = {
|
const composeFile = {
|
||||||
version: '3.8',
|
version: '3.8',
|
||||||
services: {
|
services: {
|
||||||
|
@ -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.4.0';
|
export const version = '3.5.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';
|
||||||
@ -307,6 +307,10 @@ export async function checkDoubleBranch(branch: string, projectId: number): Prom
|
|||||||
}
|
}
|
||||||
export async function isDNSValid(hostname: any, domain: string): Promise<any> {
|
export async function isDNSValid(hostname: any, domain: string): Promise<any> {
|
||||||
const { isIP } = await import('is-ip');
|
const { isIP } = await import('is-ip');
|
||||||
|
const { DNSServers } = await listSettings();
|
||||||
|
if (DNSServers) {
|
||||||
|
dns.setServers([DNSServers]);
|
||||||
|
}
|
||||||
let resolves = [];
|
let resolves = [];
|
||||||
try {
|
try {
|
||||||
if (isIP(hostname)) {
|
if (isIP(hostname)) {
|
||||||
@ -320,7 +324,6 @@ export async function isDNSValid(hostname: any, domain: string): Promise<any> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
let ipDomainFound = false;
|
let ipDomainFound = false;
|
||||||
dns.setServers(['1.1.1.1', '8.8.8.8']);
|
|
||||||
const dnsResolve = await dns.resolve4(domain);
|
const dnsResolve = await dns.resolve4(domain);
|
||||||
if (dnsResolve.length > 0) {
|
if (dnsResolve.length > 0) {
|
||||||
for (const ip of dnsResolve) {
|
for (const ip of dnsResolve) {
|
||||||
@ -412,7 +415,12 @@ export async function checkDomainsIsValidInDNS({ hostname, fqdn, dualCerts }): P
|
|||||||
const { isIP } = await import('is-ip');
|
const { isIP } = await import('is-ip');
|
||||||
const domain = getDomain(fqdn);
|
const domain = getDomain(fqdn);
|
||||||
const domainDualCert = domain.includes('www.') ? domain.replace('www.', '') : `www.${domain}`;
|
const domainDualCert = domain.includes('www.') ? domain.replace('www.', '') : `www.${domain}`;
|
||||||
dns.setServers(['1.1.1.1', '8.8.8.8']);
|
|
||||||
|
const { DNSServers } = await listSettings();
|
||||||
|
if (DNSServers) {
|
||||||
|
dns.setServers([DNSServers]);
|
||||||
|
}
|
||||||
|
|
||||||
let resolves = [];
|
let resolves = [];
|
||||||
try {
|
try {
|
||||||
if (isIP(hostname)) {
|
if (isIP(hostname)) {
|
||||||
@ -1553,7 +1561,7 @@ export async function configureServiceType({
|
|||||||
});
|
});
|
||||||
} else if (type === 'appwrite') {
|
} else if (type === 'appwrite') {
|
||||||
const opensslKeyV1 = encrypt(generatePassword());
|
const opensslKeyV1 = encrypt(generatePassword());
|
||||||
const executorSecret = encrypt(generatePassword());
|
const executorSecret = encrypt(generatePassword());
|
||||||
const redisPassword = encrypt(generatePassword());
|
const redisPassword = encrypt(generatePassword());
|
||||||
const mariadbHost = `${id}-mariadb`
|
const mariadbHost = `${id}-mariadb`
|
||||||
const mariadbUser = cuid();
|
const mariadbUser = cuid();
|
||||||
|
@ -20,7 +20,6 @@ const options: any = {
|
|||||||
}
|
}
|
||||||
if (message.caller === 'cleanupStorage') {
|
if (message.caller === 'cleanupStorage') {
|
||||||
if (!scheduler.workers.has('cleanupStorage')) {
|
if (!scheduler.workers.has('cleanupStorage')) {
|
||||||
await scheduler.stop('deployApplication');
|
|
||||||
await scheduler.run('cleanupStorage')
|
await scheduler.run('cleanupStorage')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import axios from 'axios';
|
|||||||
import { FastifyReply } from 'fastify';
|
import { FastifyReply } from 'fastify';
|
||||||
import { day } from '../../../../lib/dayjs';
|
import { day } from '../../../../lib/dayjs';
|
||||||
import { setDefaultBaseImage, setDefaultConfiguration } from '../../../../lib/buildPacks/common';
|
import { setDefaultBaseImage, setDefaultConfiguration } from '../../../../lib/buildPacks/common';
|
||||||
import { checkDomainsIsValidInDNS, checkDoubleBranch, decrypt, encrypt, errorHandler, executeDockerCmd, generateSshKeyPair, getContainerUsage, getDomain, getFreeExposedPort, isDev, isDomainConfigured, prisma, stopBuild, uniqueName } from '../../../../lib/common';
|
import { checkDomainsIsValidInDNS, checkDoubleBranch, decrypt, encrypt, errorHandler, executeDockerCmd, generateSshKeyPair, getContainerUsage, getDomain, getFreeExposedPort, isDev, isDomainConfigured, listSettings, prisma, stopBuild, uniqueName } from '../../../../lib/common';
|
||||||
import { checkContainer, formatLabelsOnDocker, isContainerExited, removeContainer } from '../../../../lib/docker';
|
import { checkContainer, formatLabelsOnDocker, isContainerExited, removeContainer } from '../../../../lib/docker';
|
||||||
import { scheduler } from '../../../../lib/scheduler';
|
import { scheduler } from '../../../../lib/scheduler';
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ export async function listApplications(request: FastifyRequest) {
|
|||||||
const { teamId } = request.user
|
const { teamId } = request.user
|
||||||
const applications = await prisma.application.findMany({
|
const applications = await prisma.application.findMany({
|
||||||
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } },
|
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } },
|
||||||
include: { teams: true, destinationDocker: true }
|
include: { teams: true, destinationDocker: true, settings: true }
|
||||||
});
|
});
|
||||||
const settings = await prisma.setting.findFirst()
|
const settings = await prisma.setting.findFirst()
|
||||||
return {
|
return {
|
||||||
@ -90,10 +90,11 @@ export async function getApplication(request: FastifyRequest<OnlyId>) {
|
|||||||
const { teamId } = request.user
|
const { teamId } = request.user
|
||||||
const appId = process.env['COOLIFY_APP_ID'];
|
const appId = process.env['COOLIFY_APP_ID'];
|
||||||
const application: any = await getApplicationFromDB(id, teamId);
|
const application: any = await getApplicationFromDB(id, teamId);
|
||||||
|
const settings = await listSettings();
|
||||||
return {
|
return {
|
||||||
application,
|
application,
|
||||||
appId
|
appId,
|
||||||
|
settings
|
||||||
};
|
};
|
||||||
|
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
@ -275,7 +276,7 @@ export async function saveApplication(request: FastifyRequest<SaveApplication>,
|
|||||||
export async function saveApplicationSettings(request: FastifyRequest<SaveApplicationSettings>, reply: FastifyReply) {
|
export async function saveApplicationSettings(request: FastifyRequest<SaveApplicationSettings>, reply: FastifyReply) {
|
||||||
try {
|
try {
|
||||||
const { id } = request.params
|
const { id } = request.params
|
||||||
const { debug, previews, dualCerts, autodeploy, branch, projectId } = request.body
|
const { debug, previews, dualCerts, autodeploy, branch, projectId, isBot } = request.body
|
||||||
const isDouble = await checkDoubleBranch(branch, projectId);
|
const isDouble = await checkDoubleBranch(branch, projectId);
|
||||||
if (isDouble && autodeploy) {
|
if (isDouble && autodeploy) {
|
||||||
await prisma.applicationSettings.updateMany({ where: { application: { branch, projectId } }, data: { autodeploy: false } })
|
await prisma.applicationSettings.updateMany({ where: { application: { branch, projectId } }, data: { autodeploy: false } })
|
||||||
@ -283,7 +284,7 @@ export async function saveApplicationSettings(request: FastifyRequest<SaveApplic
|
|||||||
}
|
}
|
||||||
await prisma.application.update({
|
await prisma.application.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: { settings: { update: { debug, previews, dualCerts, autodeploy } } },
|
data: { fqdn: isBot ? null : undefined, settings: { update: { debug, previews, dualCerts, autodeploy, isBot } } },
|
||||||
include: { destinationDocker: true }
|
include: { destinationDocker: true }
|
||||||
});
|
});
|
||||||
return reply.code(201).send();
|
return reply.code(201).send();
|
||||||
|
@ -25,7 +25,7 @@ export interface SaveApplication extends OnlyId {
|
|||||||
}
|
}
|
||||||
export interface SaveApplicationSettings extends OnlyId {
|
export interface SaveApplicationSettings extends OnlyId {
|
||||||
Querystring: { domain: string; };
|
Querystring: { domain: string; };
|
||||||
Body: { debug: boolean; previews: boolean; dualCerts: boolean; autodeploy: boolean; branch: string; projectId: number; };
|
Body: { debug: boolean; previews: boolean; dualCerts: boolean; autodeploy: boolean; branch: string; projectId: number; isBot: boolean; };
|
||||||
}
|
}
|
||||||
export interface DeleteApplication extends OnlyId {
|
export interface DeleteApplication extends OnlyId {
|
||||||
Querystring: { domain: string; };
|
Querystring: { domain: string; };
|
||||||
|
@ -4,7 +4,7 @@ import axios from 'axios';
|
|||||||
import compare from 'compare-versions';
|
import compare from 'compare-versions';
|
||||||
import cuid from 'cuid';
|
import cuid from 'cuid';
|
||||||
import bcrypt from 'bcryptjs';
|
import bcrypt from 'bcryptjs';
|
||||||
import { asyncExecShell, asyncSleep, cleanupDockerStorage, errorHandler, isDev, prisma, uniqueName, version } from '../../../lib/common';
|
import { asyncExecShell, asyncSleep, cleanupDockerStorage, errorHandler, isDev, listSettings, prisma, uniqueName, version } from '../../../lib/common';
|
||||||
|
|
||||||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||||
import type { Login, Update } from '.';
|
import type { Login, Update } from '.';
|
||||||
@ -97,7 +97,8 @@ export async function showDashboard(request: FastifyRequest) {
|
|||||||
const userId = request.user.userId;
|
const userId = request.user.userId;
|
||||||
const teamId = request.user.teamId;
|
const teamId = request.user.teamId;
|
||||||
const applications = await prisma.application.findMany({
|
const applications = await prisma.application.findMany({
|
||||||
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }
|
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } },
|
||||||
|
include: { settings: true }
|
||||||
});
|
});
|
||||||
const databases = await prisma.database.findMany({
|
const databases = await prisma.database.findMany({
|
||||||
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }
|
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }
|
||||||
@ -105,10 +106,12 @@ export async function showDashboard(request: FastifyRequest) {
|
|||||||
const services = await prisma.service.findMany({
|
const services = await prisma.service.findMany({
|
||||||
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }
|
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }
|
||||||
});
|
});
|
||||||
|
const settings = await listSettings();
|
||||||
return {
|
return {
|
||||||
applications,
|
applications,
|
||||||
databases,
|
databases,
|
||||||
services,
|
services,
|
||||||
|
settings,
|
||||||
};
|
};
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message })
|
||||||
|
@ -33,12 +33,13 @@ export async function saveSettings(request: FastifyRequest<SaveSettings>, reply:
|
|||||||
minPort,
|
minPort,
|
||||||
maxPort,
|
maxPort,
|
||||||
isAutoUpdateEnabled,
|
isAutoUpdateEnabled,
|
||||||
isDNSCheckEnabled
|
isDNSCheckEnabled,
|
||||||
|
DNSServers
|
||||||
} = request.body
|
} = request.body
|
||||||
const { id } = await listSettings();
|
const { id } = await listSettings();
|
||||||
await prisma.setting.update({
|
await prisma.setting.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: { isRegistrationEnabled, dualCerts, isAutoUpdateEnabled, isDNSCheckEnabled }
|
data: { isRegistrationEnabled, dualCerts, isAutoUpdateEnabled, isDNSCheckEnabled, DNSServers }
|
||||||
});
|
});
|
||||||
if (fqdn) {
|
if (fqdn) {
|
||||||
await prisma.setting.update({ where: { id }, data: { fqdn } });
|
await prisma.setting.update({ where: { id }, data: { fqdn } });
|
||||||
@ -54,6 +55,10 @@ export async function saveSettings(request: FastifyRequest<SaveSettings>, reply:
|
|||||||
export async function deleteDomain(request: FastifyRequest<DeleteDomain>, reply: FastifyReply) {
|
export async function deleteDomain(request: FastifyRequest<DeleteDomain>, reply: FastifyReply) {
|
||||||
try {
|
try {
|
||||||
const { fqdn } = request.body
|
const { fqdn } = request.body
|
||||||
|
const { DNSServers } = await listSettings();
|
||||||
|
if (DNSServers) {
|
||||||
|
dns.setServers([DNSServers]);
|
||||||
|
}
|
||||||
let ip;
|
let ip;
|
||||||
try {
|
try {
|
||||||
ip = await dns.resolve(fqdn);
|
ip = await dns.resolve(fqdn);
|
||||||
|
@ -8,7 +8,8 @@ export interface SaveSettings {
|
|||||||
minPort: number,
|
minPort: number,
|
||||||
maxPort: number,
|
maxPort: number,
|
||||||
isAutoUpdateEnabled: boolean,
|
isAutoUpdateEnabled: boolean,
|
||||||
isDNSCheckEnabled: boolean
|
isDNSCheckEnabled: boolean,
|
||||||
|
DNSServers: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export interface DeleteDomain {
|
export interface DeleteDomain {
|
||||||
|
@ -83,7 +83,7 @@
|
|||||||
disabled={updateStatus.success === false}
|
disabled={updateStatus.success === false}
|
||||||
on:click={update}
|
on:click={update}
|
||||||
class="icons tooltip tooltip-right tooltip-primary bg-gradient-to-r from-purple-500 via-pink-500 to-red-500 text-white duration-75 hover:scale-105"
|
class="icons tooltip tooltip-right tooltip-primary bg-gradient-to-r from-purple-500 via-pink-500 to-red-500 text-white duration-75 hover:scale-105"
|
||||||
data-tip="Update available!"
|
data-tip="Update Available!"
|
||||||
>
|
>
|
||||||
{#if updateStatus.loading}
|
{#if updateStatus.loading}
|
||||||
<svg
|
<svg
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
"wait_new_version_startup": "Waiting for the new version to start...",
|
"wait_new_version_startup": "Waiting for the new version to start...",
|
||||||
"new_version": "New version reachable. Reloading...",
|
"new_version": "New version reachable. Reloading...",
|
||||||
"switch_to_a_different_team": "Switch to a different team...",
|
"switch_to_a_different_team": "Switch to a different team...",
|
||||||
"update_available": "Update available"
|
"update_available": "Update Available"
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"you_can_find_your_way_back": "You can find your way back",
|
"you_can_find_your_way_back": "You can find your way back",
|
||||||
@ -144,8 +144,8 @@
|
|||||||
},
|
},
|
||||||
"preview": {
|
"preview": {
|
||||||
"need_during_buildtime": "Need during buildtime?",
|
"need_during_buildtime": "Need during buildtime?",
|
||||||
"setup_secret_app_first": "You can add secrets to PR/MR deployments. Please add secrets to the application first. <br>Useful for creating <span class='text-applications font-bold'>staging</span> environments.",
|
"setup_secret_app_first": "You can add secrets to PR/MR deployments. Please add secrets to the application first. <br>Useful for creating <span class='text-green-500 font-bold'>staging</span> environments.",
|
||||||
"values_overwriting_app_secrets": "These values overwrite application secrets in PR/MR deployments. Useful for creating <span class='text-applications font-bold'>staging</span> environments.",
|
"values_overwriting_app_secrets": "These values overwrite application secrets in PR/MR deployments. Useful for creating <span class='text-green-500 font-bold'>staging</span> environments.",
|
||||||
"redeploy": "Redeploy",
|
"redeploy": "Redeploy",
|
||||||
"no_previews_available": "No previews available"
|
"no_previews_available": "No previews available"
|
||||||
},
|
},
|
||||||
@ -163,9 +163,9 @@
|
|||||||
},
|
},
|
||||||
"deployment_queued": "Deployment queued.",
|
"deployment_queued": "Deployment queued.",
|
||||||
"confirm_to_delete": "Are you sure you would like to delete '{{name}}'?",
|
"confirm_to_delete": "Are you sure you would like to delete '{{name}}'?",
|
||||||
"stop_application": "Stop application",
|
"stop_application": "Stop Application",
|
||||||
"permission_denied_stop_application": "You do not have permission to stop the application.",
|
"permission_denied_stop_application": "You do not have permission to stop the application.",
|
||||||
"rebuild_application": "Rebuild application",
|
"rebuild_application": "Rebuild Application",
|
||||||
"permission_denied_rebuild_application": "You do not have permission to rebuild application.",
|
"permission_denied_rebuild_application": "You do not have permission to rebuild application.",
|
||||||
"build_and_start_application": "Deploy",
|
"build_and_start_application": "Deploy",
|
||||||
"permission_denied_build_and_start_application": "You do not have permission to deploy application.",
|
"permission_denied_build_and_start_application": "You do not have permission to deploy application.",
|
||||||
@ -194,14 +194,14 @@
|
|||||||
"application": "Application",
|
"application": "Application",
|
||||||
"url_fqdn": "URL (FQDN)",
|
"url_fqdn": "URL (FQDN)",
|
||||||
"domain_fqdn": "Domain (FQDN)",
|
"domain_fqdn": "Domain (FQDN)",
|
||||||
"https_explainer": "If you specify <span class='text-applications font-bold'>https</span>, the application will be accessible only over https. SSL certificate will be generated for you.<br>If you specify <span class='text-applications font-bold'>www</span>, the application will be redirected (302) from non-www and vice versa.<br><br>To modify the domain, you must first stop the application.<br><br><span class='text-white font-bold'>You must set your DNS to point to the server IP in advance.</span>",
|
"https_explainer": "If you specify <span class='text-green-500 font-bold'>https</span>, the application will be accessible only over https. SSL certificate will be generated for you.<br>If you specify <span class='text-green-500 font-bold'>www</span>, the application will be redirected (302) from non-www and vice versa.<br><br>To modify the domain, you must first stop the application.<br><br><span class='text-white font-bold'>You must set your DNS to point to the server IP in advance.</span>",
|
||||||
"ssl_www_and_non_www": "Generate SSL for www and non-www?",
|
"ssl_www_and_non_www": "Generate SSL for www and non-www?",
|
||||||
"ssl_explainer": "It will generate certificates for both www and non-www. <br>You need to have <span class='font-bold text-applications'>both DNS entries</span> set in advance.<br><br>Useful if you expect to have visitors on both.",
|
"ssl_explainer": "It will generate certificates for both www and non-www. <br>You need to have <span class='font-bold text-green-500'>both DNS entries</span> set in advance.<br><br>Useful if you expect to have visitors on both.",
|
||||||
"install_command": "Install Command",
|
"install_command": "Install Command",
|
||||||
"build_command": "Build Command",
|
"build_command": "Build Command",
|
||||||
"start_command": "Start Command",
|
"start_command": "Start Command",
|
||||||
"directory_to_use_explainer": "Directory to use as the base for all commands.<br>Could be useful with <span class='text-applications font-bold'>monorepos</span>.",
|
"directory_to_use_explainer": "Directory to use as the base for all commands.<br>Could be useful with <span class='text-green-500 font-bold'>monorepos</span>.",
|
||||||
"publish_directory_explainer": "Directory containing all the assets for deployment. <br> For example: <span class='text-applications font-bold'>dist</span>,<span class='text-applications font-bold'>_site</span> or <span class='text-applications font-bold'>public</span>.",
|
"publish_directory_explainer": "Directory containing all the assets for deployment. <br> For example: <span class='text-green-500 font-bold'>dist</span>,<span class='text-green-500 font-bold'>_site</span> or <span class='text-green-500 font-bold'>public</span>.",
|
||||||
"features": "Features",
|
"features": "Features",
|
||||||
"enable_automatic_deployment": "Enable Automatic Deployment",
|
"enable_automatic_deployment": "Enable Automatic Deployment",
|
||||||
"enable_auto_deploy_webhooks": "Enable automatic deployment through webhooks.",
|
"enable_auto_deploy_webhooks": "Enable automatic deployment through webhooks.",
|
||||||
|
@ -65,7 +65,7 @@
|
|||||||
"features": "Caractéristiques",
|
"features": "Caractéristiques",
|
||||||
"git_repository": "Dépôt Git",
|
"git_repository": "Dépôt Git",
|
||||||
"git_source": "Source Git",
|
"git_source": "Source Git",
|
||||||
"https_explainer": "Si vous spécifiez <span class='text-applications font-bold'>https</span>, l'application sera accessible uniquement via https. \nUn certificat SSL sera généré pour vous.<br>Si vous spécifiez <span class='text-applications font-bold'>www</span>, l'application sera redirigée (302) à partir de non-www et vice versa \n.<br><br>Pour modifier le domaine, vous devez d'abord arrêter l'application.<br><br><span class='text-white font-bold'>Vous devez configurer, en avance, votre DNS pour pointer vers l'IP du serveur.</span>",
|
"https_explainer": "Si vous spécifiez <span class='text-green-500 font-bold'>https</span>, l'application sera accessible uniquement via https. \nUn certificat SSL sera généré pour vous.<br>Si vous spécifiez <span class='text-green-500 font-bold'>www</span>, l'application sera redirigée (302) à partir de non-www et vice versa \n.<br><br>Pour modifier le domaine, vous devez d'abord arrêter l'application.<br><br><span class='text-white font-bold'>Vous devez configurer, en avance, votre DNS pour pointer vers l'IP du serveur.</span>",
|
||||||
"install_command": "Commande d'installation",
|
"install_command": "Commande d'installation",
|
||||||
"logs": "Journaux des applications",
|
"logs": "Journaux des applications",
|
||||||
"no_applications_found": "Aucune application trouvée",
|
"no_applications_found": "Aucune application trouvée",
|
||||||
@ -78,11 +78,11 @@
|
|||||||
"need_during_buildtime": "Besoin pendant la build ?",
|
"need_during_buildtime": "Besoin pendant la build ?",
|
||||||
"no_previews_available": "Aucun aperçu disponible",
|
"no_previews_available": "Aucun aperçu disponible",
|
||||||
"redeploy": "Redéployer",
|
"redeploy": "Redéployer",
|
||||||
"setup_secret_app_first": "Vous pouvez ajouter des secrets aux déploiements PR/MR. \nVeuillez d'abord ajouter des secrets à l'application. \n<br>Utile pour créer des environnements <span class='text-applications font-bold'>de mise en scène</span>.",
|
"setup_secret_app_first": "Vous pouvez ajouter des secrets aux déploiements PR/MR. \nVeuillez d'abord ajouter des secrets à l'application. \n<br>Utile pour créer des environnements <span class='text-green-500 font-bold'>de mise en scène</span>.",
|
||||||
"values_overwriting_app_secrets": "Ces valeurs remplacent les secrets d'application dans les déploiements PR/MR. \nUtile pour créer des environnements <span class='text-applications font-bold'>de mise en scène</span>."
|
"values_overwriting_app_secrets": "Ces valeurs remplacent les secrets d'application dans les déploiements PR/MR. \nUtile pour créer des environnements <span class='text-green-500 font-bold'>de mise en scène</span>."
|
||||||
},
|
},
|
||||||
"previews": "Aperçus",
|
"previews": "Aperçus",
|
||||||
"publish_directory_explainer": "Répertoire contenant tous les actifs à déployer. \n<br> Par exemple : <span class='text-applications font-bold'>dist</span>,<span class='text-applications font-bold'>_site</span> ou <span \nclass='text-applications font-bold'>public</span>.",
|
"publish_directory_explainer": "Répertoire contenant tous les actifs à déployer. \n<br> Par exemple : <span class='text-green-500 font-bold'>dist</span>,<span class='text-green-500 font-bold'>_site</span> ou <span \nclass='text-green-500 font-bold'>public</span>.",
|
||||||
"rebuild_application": "Re-build l'application",
|
"rebuild_application": "Re-build l'application",
|
||||||
"secret": "secrets",
|
"secret": "secrets",
|
||||||
"secrets": {
|
"secrets": {
|
||||||
@ -91,7 +91,7 @@
|
|||||||
"use_isbuildsecret": "Utiliser isBuildSecret"
|
"use_isbuildsecret": "Utiliser isBuildSecret"
|
||||||
},
|
},
|
||||||
"settings_saved": "Paramètres sauvegardés.",
|
"settings_saved": "Paramètres sauvegardés.",
|
||||||
"ssl_explainer": "Il générera des certificats pour www et non-www. \n<br>Vous devez avoir <span class='font-bold text-applications'>les deux entrées DNS</span> définies à l'avance.<br><br>Utile si vous prévoyez d'avoir des visiteurs sur les deux.",
|
"ssl_explainer": "Il générera des certificats pour www et non-www. \n<br>Vous devez avoir <span class='font-bold text-green-500'>les deux entrées DNS</span> définies à l'avance.<br><br>Utile si vous prévoyez d'avoir des visiteurs sur les deux.",
|
||||||
"ssl_www_and_non_www": "Générer SSL pour www et non-www ?",
|
"ssl_www_and_non_www": "Générer SSL pour www et non-www ?",
|
||||||
"start_command": "Démarrer la commande",
|
"start_command": "Démarrer la commande",
|
||||||
"stop_application": "Arrêter l'application",
|
"stop_application": "Arrêter l'application",
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { dev } from '$app/env';
|
||||||
import cuid from 'cuid';
|
import cuid from 'cuid';
|
||||||
import { writable, readable, type Writable } from 'svelte/store';
|
import { writable, readable, type Writable } from 'svelte/store';
|
||||||
|
|
||||||
@ -70,7 +71,11 @@ export const features = readable({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const location: Writable<null | string> = writable(null)
|
export const location: Writable<null | string> = writable(null)
|
||||||
export const setLocation = (resource: any) => {
|
export const setLocation = (resource: any, settings?: any) => {
|
||||||
|
if (resource.settings.isBot && resource.exposePort) {
|
||||||
|
disabledButton.set(false);
|
||||||
|
return location.set(`http://${dev ? 'localhost' : settings.ipv4}:${resource.exposePort}`)
|
||||||
|
}
|
||||||
if (GITPOD_WORKSPACE_URL && resource.exposePort) {
|
if (GITPOD_WORKSPACE_URL && resource.exposePort) {
|
||||||
const { href } = new URL(GITPOD_WORKSPACE_URL);
|
const { href } = new URL(GITPOD_WORKSPACE_URL);
|
||||||
const newURL = href
|
const newURL = href
|
||||||
@ -81,7 +86,12 @@ export const setLocation = (resource: any) => {
|
|||||||
const newURL = `https://${CODESANDBOX_HOST.replace(/\$PORT/, resource.exposePort)}`
|
const newURL = `https://${CODESANDBOX_HOST.replace(/\$PORT/, resource.exposePort)}`
|
||||||
return location.set(newURL)
|
return location.set(newURL)
|
||||||
}
|
}
|
||||||
return location.set(resource.fqdn)
|
if (resource.fqdn) {
|
||||||
|
return location.set(resource.fqdn)
|
||||||
|
} else {
|
||||||
|
location.set(null);
|
||||||
|
disabledButton.set(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const toasts: any = writable([])
|
export const toasts: any = writable([])
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
export const load: Load = async ({ fetch, url, params }) => {
|
export const load: Load = async ({ fetch, url, params }) => {
|
||||||
try {
|
try {
|
||||||
const response = await get(`/applications/${params.id}`);
|
const response = await get(`/applications/${params.id}`);
|
||||||
let { application, appId, settings, isQueueActive } = response;
|
let { application, appId, settings } = response;
|
||||||
if (!application || Object.entries(application).length === 0) {
|
if (!application || Object.entries(application).length === 0) {
|
||||||
return {
|
return {
|
||||||
status: 302,
|
status: 302,
|
||||||
@ -36,7 +36,8 @@
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
application
|
application,
|
||||||
|
settings
|
||||||
},
|
},
|
||||||
stuff: {
|
stuff: {
|
||||||
application,
|
application,
|
||||||
@ -52,7 +53,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let application: any;
|
export let application: any;
|
||||||
|
export let settings: any;
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import DeleteIcon from '$lib/components/DeleteIcon.svelte';
|
import DeleteIcon from '$lib/components/DeleteIcon.svelte';
|
||||||
import { del, get, post } from '$lib/api';
|
import { del, get, post } from '$lib/api';
|
||||||
@ -65,10 +66,10 @@
|
|||||||
|
|
||||||
let loading = false;
|
let loading = false;
|
||||||
let statusInterval: any;
|
let statusInterval: any;
|
||||||
let isQueueActive= false;
|
let isQueueActive = false;
|
||||||
$disabledButton =
|
$disabledButton =
|
||||||
!$appSession.isAdmin ||
|
!$appSession.isAdmin ||
|
||||||
!application.fqdn ||
|
(!application.fqdn && !application.settings.isBot) ||
|
||||||
!application.gitSource ||
|
!application.gitSource ||
|
||||||
!application.repository ||
|
!application.repository ||
|
||||||
!application.destinationDocker ||
|
!application.destinationDocker ||
|
||||||
@ -80,9 +81,9 @@
|
|||||||
try {
|
try {
|
||||||
const { buildId } = await post(`/applications/${id}/deploy`, { ...application });
|
const { buildId } = await post(`/applications/${id}/deploy`, { ...application });
|
||||||
addToast({
|
addToast({
|
||||||
message: $t('application.deployment_queued'),
|
message: $t('application.deployment_queued'),
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
if ($page.url.pathname.startsWith(`/applications/${id}/logs/build`)) {
|
if ($page.url.pathname.startsWith(`/applications/${id}/logs/build`)) {
|
||||||
return window.location.assign(`/applications/${id}/logs/build?buildId=${buildId}`);
|
return window.location.assign(`/applications/${id}/logs/build?buildId=${buildId}`);
|
||||||
} else {
|
} else {
|
||||||
@ -126,18 +127,23 @@
|
|||||||
$status.application.loading = false;
|
$status.application.loading = false;
|
||||||
$status.application.initialLoading = false;
|
$status.application.initialLoading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
$status.application.initialLoading = true;
|
$status.application.initialLoading = true;
|
||||||
$location = null;
|
$location = null;
|
||||||
clearInterval(statusInterval);
|
clearInterval(statusInterval);
|
||||||
});
|
});
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
setLocation(application);
|
setLocation(application, settings);
|
||||||
|
|
||||||
$status.application.isRunning = false;
|
$status.application.isRunning = false;
|
||||||
$status.application.isExited = false;
|
$status.application.isExited = false;
|
||||||
$status.application.loading = false;
|
$status.application.loading = false;
|
||||||
if (application.gitSourceId && application.destinationDockerId && application.fqdn) {
|
if (
|
||||||
|
application.gitSourceId &&
|
||||||
|
application.destinationDockerId &&
|
||||||
|
(application.fqdn ||
|
||||||
|
application.settings.isBot)
|
||||||
|
) {
|
||||||
await getStatus();
|
await getStatus();
|
||||||
statusInterval = setInterval(async () => {
|
statusInterval = setInterval(async () => {
|
||||||
await getStatus();
|
await getStatus();
|
||||||
@ -258,7 +264,7 @@
|
|||||||
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm flex items-center space-x-2"
|
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm flex items-center space-x-2"
|
||||||
data-tip={$appSession.isAdmin
|
data-tip={$appSession.isAdmin
|
||||||
? isQueueActive
|
? isQueueActive
|
||||||
? 'Rebuild application'
|
? 'Rebuild Application'
|
||||||
: 'Autoupdate inprogress. Cannot rebuild application.'
|
: 'Autoupdate inprogress. Cannot rebuild application.'
|
||||||
: 'You do not have permission to rebuild application.'}
|
: 'You do not have permission to rebuild application.'}
|
||||||
>
|
>
|
||||||
@ -403,37 +409,39 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</button></a
|
</button></a
|
||||||
>
|
>
|
||||||
<a
|
{#if !application.settings.isBot}
|
||||||
href={!$disabledButton ? `/applications/${id}/previews` : null}
|
<a
|
||||||
sveltekit:prefetch
|
href={!$disabledButton ? `/applications/${id}/previews` : null}
|
||||||
class="hover:text-orange-500 rounded"
|
sveltekit:prefetch
|
||||||
class:text-orange-500={$page.url.pathname === `/applications/${id}/previews`}
|
class="hover:text-orange-500 rounded"
|
||||||
class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/previews`}
|
class:text-orange-500={$page.url.pathname === `/applications/${id}/previews`}
|
||||||
>
|
class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/previews`}
|
||||||
<button
|
|
||||||
disabled={$disabledButton}
|
|
||||||
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm"
|
|
||||||
data-tip="Previews"
|
|
||||||
>
|
>
|
||||||
<svg
|
<button
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
disabled={$disabledButton}
|
||||||
class="w-6 h-6"
|
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm"
|
||||||
viewBox="0 0 24 24"
|
data-tip="Previews"
|
||||||
stroke-width="1.5"
|
|
||||||
stroke="currentColor"
|
|
||||||
fill="none"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
>
|
>
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
<svg
|
||||||
<circle cx="7" cy="18" r="2" />
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<circle cx="7" cy="6" r="2" />
|
class="w-6 h-6"
|
||||||
<circle cx="17" cy="12" r="2" />
|
viewBox="0 0 24 24"
|
||||||
<line x1="7" y1="8" x2="7" y2="16" />
|
stroke-width="1.5"
|
||||||
<path d="M7 8a4 4 0 0 0 4 4h4" />
|
stroke="currentColor"
|
||||||
</svg></button
|
fill="none"
|
||||||
></a
|
stroke-linecap="round"
|
||||||
>
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
|
<circle cx="7" cy="18" r="2" />
|
||||||
|
<circle cx="7" cy="6" r="2" />
|
||||||
|
<circle cx="17" cy="12" r="2" />
|
||||||
|
<line x1="7" y1="8" x2="7" y2="16" />
|
||||||
|
<path d="M7 8a4 4 0 0 0 4 4h4" />
|
||||||
|
</svg></button
|
||||||
|
></a
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
<div class="border border-coolgray-500 h-8" />
|
<div class="border border-coolgray-500 h-8" />
|
||||||
<a
|
<a
|
||||||
href={!$disabledButton && $status.application.isRunning ? `/applications/${id}/logs` : null}
|
href={!$disabledButton && $status.application.isRunning ? `/applications/${id}/logs` : null}
|
||||||
|
@ -5,7 +5,8 @@
|
|||||||
if (stuff?.application?.id) {
|
if (stuff?.application?.id) {
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
application: stuff.application
|
application: stuff.application,
|
||||||
|
settings: stuff.settings
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -26,6 +27,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let application: any;
|
export let application: any;
|
||||||
|
export let settings: any;
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onDestroy, onMount } from 'svelte';
|
||||||
import Select from 'svelte-select';
|
import Select from 'svelte-select';
|
||||||
@ -60,6 +62,7 @@
|
|||||||
let previews = application.settings.previews;
|
let previews = application.settings.previews;
|
||||||
let dualCerts = application.settings.dualCerts;
|
let dualCerts = application.settings.dualCerts;
|
||||||
let autodeploy = application.settings.autodeploy;
|
let autodeploy = application.settings.autodeploy;
|
||||||
|
let isBot = application.settings.isBot;
|
||||||
|
|
||||||
let nonWWWDomain = application.fqdn && getDomain(application.fqdn).replace(/^www\./, '');
|
let nonWWWDomain = application.fqdn && getDomain(application.fqdn).replace(/^www\./, '');
|
||||||
let isNonWWWDomainOK = false;
|
let isNonWWWDomainOK = false;
|
||||||
@ -99,7 +102,7 @@
|
|||||||
application.fqdn = `http://${cuid()}.demo.coolify.io`;
|
application.fqdn = `http://${cuid()}.demo.coolify.io`;
|
||||||
await handleSubmit();
|
await handleSubmit();
|
||||||
}
|
}
|
||||||
domainEl.focus();
|
// !isBot && domainEl.focus();
|
||||||
await getUsage();
|
await getUsage();
|
||||||
usageInterval = setInterval(async () => {
|
usageInterval = setInterval(async () => {
|
||||||
await getUsage();
|
await getUsage();
|
||||||
@ -129,11 +132,17 @@
|
|||||||
if (name === 'autodeploy') {
|
if (name === 'autodeploy') {
|
||||||
autodeploy = !autodeploy;
|
autodeploy = !autodeploy;
|
||||||
}
|
}
|
||||||
|
if (name === 'isBot') {
|
||||||
|
isBot = !isBot;
|
||||||
|
application.settings.isBot = isBot;
|
||||||
|
setLocation(application, settings);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
await post(`/applications/${id}/settings`, {
|
await post(`/applications/${id}/settings`, {
|
||||||
previews,
|
previews,
|
||||||
debug,
|
debug,
|
||||||
dualCerts,
|
dualCerts,
|
||||||
|
isBot,
|
||||||
autodeploy,
|
autodeploy,
|
||||||
branch: application.branch,
|
branch: application.branch,
|
||||||
projectId: application.projectId
|
projectId: application.projectId
|
||||||
@ -155,24 +164,28 @@
|
|||||||
if (name === 'autodeploy') {
|
if (name === 'autodeploy') {
|
||||||
autodeploy = !autodeploy;
|
autodeploy = !autodeploy;
|
||||||
}
|
}
|
||||||
|
if (name === 'isBot') {
|
||||||
|
isBot = !isBot;
|
||||||
|
}
|
||||||
return errorNotification(error);
|
return errorNotification(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function handleSubmit() {
|
async function handleSubmit() {
|
||||||
if (loading || !application.fqdn) return;
|
if (loading || (!application.fqdn && !isBot)) return;
|
||||||
loading = true;
|
loading = true;
|
||||||
try {
|
try {
|
||||||
nonWWWDomain = application.fqdn && getDomain(application.fqdn).replace(/^www\./, '');
|
nonWWWDomain = application.fqdn && getDomain(application.fqdn).replace(/^www\./, '');
|
||||||
if (application.deploymentType)
|
if (application.deploymentType)
|
||||||
application.deploymentType = application.deploymentType.toLowerCase();
|
application.deploymentType = application.deploymentType.toLowerCase();
|
||||||
await post(`/applications/${id}/check`, {
|
!isBot &&
|
||||||
fqdn: application.fqdn,
|
(await post(`/applications/${id}/check`, {
|
||||||
forceSave,
|
fqdn: application.fqdn,
|
||||||
dualCerts,
|
forceSave,
|
||||||
exposePort: application.exposePort
|
dualCerts,
|
||||||
});
|
exposePort: application.exposePort
|
||||||
|
}));
|
||||||
await post(`/applications/${id}`, { ...application });
|
await post(`/applications/${id}`, { ...application });
|
||||||
setLocation(application);
|
setLocation(application, settings);
|
||||||
$disabledButton = false;
|
$disabledButton = false;
|
||||||
forceSave = false;
|
forceSave = false;
|
||||||
addToast({
|
addToast({
|
||||||
@ -371,16 +384,16 @@
|
|||||||
{#if isDisabled}
|
{#if isDisabled}
|
||||||
<input class="capitalize" disabled={isDisabled} value={application.buildPack} />
|
<input class="capitalize" disabled={isDisabled} value={application.buildPack} />
|
||||||
{:else}
|
{:else}
|
||||||
<a
|
<a
|
||||||
href={`/applications/${id}/configuration/buildpack?from=/applications/${id}`}
|
href={`/applications/${id}/configuration/buildpack?from=/applications/${id}`}
|
||||||
class="no-underline "
|
class="no-underline "
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
value={application.buildPack}
|
value={application.buildPack}
|
||||||
id="buildPack"
|
id="buildPack"
|
||||||
class="cursor-pointer hover:bg-coolgray-500 capitalize"
|
class="cursor-pointer hover:bg-coolgray-500 capitalize"
|
||||||
/></a
|
/></a
|
||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-2 items-center pb-8">
|
<div class="grid grid-cols-2 items-center pb-8">
|
||||||
@ -468,77 +481,88 @@
|
|||||||
<div class="title">{$t('application.application')}</div>
|
<div class="title">{$t('application.application')}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-flow-row gap-2 px-10">
|
<div class="grid grid-flow-row gap-2 px-10">
|
||||||
<div class="grid grid-cols-2">
|
<div class="grid grid-cols-2 items-center">
|
||||||
<div class="flex-col">
|
<Setting
|
||||||
<label for="fqdn" class="pt-2 text-base font-bold text-stone-100"
|
isCenter={false}
|
||||||
>{$t('application.url_fqdn')}</label
|
bind:setting={isBot}
|
||||||
>
|
on:click={() => changeSettings('isBot')}
|
||||||
{#if browser && window.location.hostname === 'demo.coolify.io'}
|
title="Is your application a bot?"
|
||||||
<Explainer
|
description="You can deploy applications without domains. <br>They will listen on <span class='text-green-500 font-bold'>IP:EXPOSEDPORT</span> instead.<br></Setting><br>Useful to host <span class='text-green-500 font-bold'>Twitch bots.</span>"
|
||||||
text="<span class='text-white font-bold'>You can use the predefined random url name or enter your own domain name.</span>"
|
/>
|
||||||
|
</div>
|
||||||
|
{#if !isBot}
|
||||||
|
<div class="grid grid-cols-2">
|
||||||
|
<div class="flex-col">
|
||||||
|
<label for="fqdn" class="pt-2 text-base font-bold text-stone-100"
|
||||||
|
>{$t('application.url_fqdn')}</label
|
||||||
|
>
|
||||||
|
{#if browser && window.location.hostname === 'demo.coolify.io'}
|
||||||
|
<Explainer
|
||||||
|
text="<span class='text-white font-bold'>You can use the predefined random url name or enter your own domain name.</span>"
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
<Explainer text={$t('application.https_explainer')} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
readonly={isDisabled}
|
||||||
|
disabled={isDisabled}
|
||||||
|
bind:this={domainEl}
|
||||||
|
name="fqdn"
|
||||||
|
id="fqdn"
|
||||||
|
required
|
||||||
|
bind:value={application.fqdn}
|
||||||
|
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
|
||||||
|
placeholder="eg: https://coollabs.io"
|
||||||
/>
|
/>
|
||||||
{/if}
|
{#if forceSave}
|
||||||
<Explainer text={$t('application.https_explainer')} />
|
<div class="flex-col space-y-2 pt-4 text-center">
|
||||||
</div>
|
{#if isNonWWWDomainOK}
|
||||||
<div>
|
|
||||||
<input
|
|
||||||
readonly={isDisabled}
|
|
||||||
disabled={isDisabled}
|
|
||||||
bind:this={domainEl}
|
|
||||||
name="fqdn"
|
|
||||||
id="fqdn"
|
|
||||||
required
|
|
||||||
bind:value={application.fqdn}
|
|
||||||
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
|
|
||||||
placeholder="eg: https://coollabs.io"
|
|
||||||
/>
|
|
||||||
{#if forceSave}
|
|
||||||
<div class="flex-col space-y-2 pt-4 text-center">
|
|
||||||
{#if isNonWWWDomainOK}
|
|
||||||
<button
|
|
||||||
class="bg-green-600 hover:bg-green-500"
|
|
||||||
on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
|
|
||||||
>DNS settings for {nonWWWDomain} is OK, click to recheck.</button
|
|
||||||
>
|
|
||||||
{:else}
|
|
||||||
<button
|
|
||||||
class="bg-red-600 hover:bg-red-500"
|
|
||||||
on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
|
|
||||||
>DNS settings for {nonWWWDomain} is invalid, click to recheck.</button
|
|
||||||
>
|
|
||||||
{/if}
|
|
||||||
{#if dualCerts}
|
|
||||||
{#if isWWWDomainOK}
|
|
||||||
<button
|
<button
|
||||||
class="bg-green-600 hover:bg-green-500"
|
class="btn btn-sm bg-green-600 hover:bg-green-500"
|
||||||
on:click|preventDefault={() =>
|
on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
|
||||||
isDNSValid(getDomain(`www.${nonWWWDomain}`), true)}
|
>DNS settings for {nonWWWDomain} is OK, click to recheck.</button
|
||||||
>DNS settings for www.{nonWWWDomain} is OK, click to recheck.</button
|
|
||||||
>
|
>
|
||||||
{:else}
|
{:else}
|
||||||
<button
|
<button
|
||||||
class="bg-red-600 hover:bg-red-500"
|
class="btn btn-sm bg-red-600 hover:bg-red-500"
|
||||||
on:click|preventDefault={() =>
|
on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
|
||||||
isDNSValid(getDomain(`www.${nonWWWDomain}`), true)}
|
>DNS settings for {nonWWWDomain} is invalid, click to recheck.</button
|
||||||
>DNS settings for www.{nonWWWDomain} is invalid, click to recheck.</button
|
|
||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{#if dualCerts}
|
||||||
</div>
|
{#if isWWWDomainOK}
|
||||||
{/if}
|
<button
|
||||||
|
class="btn btn-sm bg-green-600 hover:bg-green-500"
|
||||||
|
on:click|preventDefault={() =>
|
||||||
|
isDNSValid(getDomain(`www.${nonWWWDomain}`), true)}
|
||||||
|
>DNS settings for www.{nonWWWDomain} is OK, click to recheck.</button
|
||||||
|
>
|
||||||
|
{:else}
|
||||||
|
<button
|
||||||
|
class="btn btn-sm bg-red-600 hover:bg-red-500"
|
||||||
|
on:click|preventDefault={() =>
|
||||||
|
isDNSValid(getDomain(`www.${nonWWWDomain}`), true)}
|
||||||
|
>DNS settings for www.{nonWWWDomain} is invalid, click to recheck.</button
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="grid grid-cols-2 items-center pb-8">
|
||||||
<div class="grid grid-cols-2 items-center pb-8">
|
<Setting
|
||||||
<Setting
|
dataTooltip={$t('forms.must_be_stopped_to_modify')}
|
||||||
dataTooltip={$t('forms.must_be_stopped_to_modify')}
|
disabled={$status.application.isRunning}
|
||||||
disabled={$status.application.isRunning}
|
isCenter={false}
|
||||||
isCenter={false}
|
bind:setting={dualCerts}
|
||||||
bind:setting={dualCerts}
|
title={$t('application.ssl_www_and_non_www')}
|
||||||
title={$t('application.ssl_www_and_non_www')}
|
description={$t('application.ssl_explainer')}
|
||||||
description={$t('application.ssl_explainer')}
|
on:click={() => !$status.application.isRunning && changeSettings('dualCerts')}
|
||||||
on:click={() => !$status.application.isRunning && changeSettings('dualCerts')}
|
/>
|
||||||
/>
|
</div>
|
||||||
</div>
|
{/if}
|
||||||
{#if application.buildPack === 'python'}
|
{#if application.buildPack === 'python'}
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center">
|
||||||
<label for="pythonModule" class="text-base font-bold text-stone-100">WSGI / ASGI</label>
|
<label for="pythonModule" class="text-base font-bold text-stone-100">WSGI / ASGI</label>
|
||||||
@ -588,7 +612,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
{#if !staticDeployments.includes(application.buildPack)}
|
{#if !staticDeployments.includes(application.buildPack) && !isBot}
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center">
|
||||||
<label for="port" class="text-base font-bold text-stone-100">{$t('forms.port')}</label>
|
<label for="port" class="text-base font-bold text-stone-100">{$t('forms.port')}</label>
|
||||||
<input
|
<input
|
||||||
@ -609,6 +633,7 @@
|
|||||||
name="exposePort"
|
name="exposePort"
|
||||||
id="exposePort"
|
id="exposePort"
|
||||||
bind:value={application.exposePort}
|
bind:value={application.exposePort}
|
||||||
|
required={isBot}
|
||||||
placeholder="12345"
|
placeholder="12345"
|
||||||
/>
|
/>
|
||||||
<Explainer
|
<Explainer
|
||||||
@ -754,15 +779,17 @@
|
|||||||
description={$t('application.enable_auto_deploy_webhooks')}
|
description={$t('application.enable_auto_deploy_webhooks')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-2 items-center">
|
{#if !application.settings.isBot}
|
||||||
<Setting
|
<div class="grid grid-cols-2 items-center">
|
||||||
isCenter={false}
|
<Setting
|
||||||
bind:setting={previews}
|
isCenter={false}
|
||||||
on:click={() => changeSettings('previews')}
|
bind:setting={previews}
|
||||||
title={$t('application.enable_mr_pr_previews')}
|
on:click={() => changeSettings('previews')}
|
||||||
description={$t('application.enable_preview_deploy_mr_pr_requests')}
|
title={$t('application.enable_mr_pr_previews')}
|
||||||
/>
|
description={$t('application.enable_preview_deploy_mr_pr_requests')}
|
||||||
</div>
|
/>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center">
|
||||||
<Setting
|
<Setting
|
||||||
isCenter={false}
|
isCenter={false}
|
||||||
|
@ -87,6 +87,9 @@
|
|||||||
{#if application.fqdn}
|
{#if application.fqdn}
|
||||||
<div class="truncate text-center">{getDomain(application.fqdn) || ''}</div>
|
<div class="truncate text-center">{getDomain(application.fqdn) || ''}</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if application.settings.isBot}
|
||||||
|
<div class="truncate text-center">BOT</div>
|
||||||
|
{/if}
|
||||||
{#if application.destinationDocker?.name}
|
{#if application.destinationDocker?.name}
|
||||||
<div class="truncate text-center">{application.destinationDocker.name}</div>
|
<div class="truncate text-center">{application.destinationDocker.name}</div>
|
||||||
{/if}
|
{/if}
|
||||||
@ -98,7 +101,7 @@
|
|||||||
<div class="truncate text-center font-bold text-red-500 group-hover:text-white">
|
<div class="truncate text-center font-bold text-red-500 group-hover:text-white">
|
||||||
Destination Missing
|
Destination Missing
|
||||||
</div>
|
</div>
|
||||||
{:else if !application.fqdn}
|
{:else if !application.fqdn && !application.settings.isBot}
|
||||||
<div class="truncate text-center font-bold text-red-500 group-hover:text-white">
|
<div class="truncate text-center font-bold text-red-500 group-hover:text-white">
|
||||||
URL Missing
|
URL Missing
|
||||||
</div>
|
</div>
|
||||||
|
@ -20,6 +20,11 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
export let applications: any;
|
||||||
|
export let databases: any;
|
||||||
|
export let services: any;
|
||||||
|
export let settings: any;
|
||||||
|
|
||||||
import { get, post } from '$lib/api';
|
import { get, post } from '$lib/api';
|
||||||
import Usage from '$lib/components/Usage.svelte';
|
import Usage from '$lib/components/Usage.svelte';
|
||||||
import { t } from '$lib/translations';
|
import { t } from '$lib/translations';
|
||||||
@ -29,26 +34,24 @@
|
|||||||
import ApplicationsIcons from '$lib/components/svg/applications/ApplicationIcons.svelte';
|
import ApplicationsIcons from '$lib/components/svg/applications/ApplicationIcons.svelte';
|
||||||
import DatabaseIcons from '$lib/components/svg/databases/DatabaseIcons.svelte';
|
import DatabaseIcons from '$lib/components/svg/databases/DatabaseIcons.svelte';
|
||||||
import ServiceIcons from '$lib/components/svg/services/ServiceIcons.svelte';
|
import ServiceIcons from '$lib/components/svg/services/ServiceIcons.svelte';
|
||||||
|
import { dev } from '$app/env';
|
||||||
|
|
||||||
let loading = {
|
let loading = {
|
||||||
cleanup: false
|
cleanup: false
|
||||||
};
|
};
|
||||||
|
|
||||||
export let applications: any;
|
let numberOfGetStatus = 0;
|
||||||
export let databases: any;
|
|
||||||
export let services: any;
|
|
||||||
let numberOfGetStatus = 0;
|
|
||||||
|
|
||||||
function getRndInteger(min: number, max: number) {
|
function getRndInteger(min: number, max: number) {
|
||||||
return Math.floor(Math.random() * (max - min + 1) ) + min;
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getStatus(resources: any) {
|
async function getStatus(resources: any) {
|
||||||
while (numberOfGetStatus > 1){
|
while (numberOfGetStatus > 1) {
|
||||||
await asyncSleep(getRndInteger(100,200));
|
await asyncSleep(getRndInteger(100, 200));
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
numberOfGetStatus++;
|
numberOfGetStatus++;
|
||||||
const { id, buildPack, dualCerts } = resources;
|
const { id, buildPack, dualCerts } = resources;
|
||||||
let isRunning = false;
|
let isRunning = false;
|
||||||
if (buildPack) {
|
if (buildPack) {
|
||||||
@ -69,8 +72,8 @@
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
return 'Error';
|
return 'Error';
|
||||||
} finally {
|
} finally {
|
||||||
numberOfGetStatus--;
|
numberOfGetStatus--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function manuallyCleanupStorage() {
|
async function manuallyCleanupStorage() {
|
||||||
try {
|
try {
|
||||||
@ -98,40 +101,90 @@
|
|||||||
<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}
|
{#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">
|
||||||
<table class="rounded-none text-base">
|
<table class="rounded-none text-base">
|
||||||
<tbody>
|
<tbody>
|
||||||
{#each applications as application}
|
{#each applications as application}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="space-x-2 items-center tracking-tight font-bold">
|
<td class="space-x-2 items-center tracking-tight font-bold">
|
||||||
{#await getStatus(application)}
|
{#await getStatus(application)}
|
||||||
<div class="inline-flex w-2 h-2 bg-yellow-500 rounded-full" />
|
<div class="inline-flex w-2 h-2 bg-yellow-500 rounded-full" />
|
||||||
{:then status}
|
{:then status}
|
||||||
{#if status === 'Running'}
|
{#if status === 'Running'}
|
||||||
<div class="inline-flex w-2 h-2 bg-success rounded-full" />
|
<div class="inline-flex w-2 h-2 bg-success rounded-full" />
|
||||||
{:else}
|
{:else}
|
||||||
<div class="inline-flex w-2 h-2 bg-error rounded-full" />
|
<div class="inline-flex w-2 h-2 bg-error rounded-full" />
|
||||||
|
{/if}
|
||||||
|
{/await}
|
||||||
|
<div class="inline-flex">{application.name}</div>
|
||||||
|
</td>
|
||||||
|
<td class="px-10 inline-flex">
|
||||||
|
<ApplicationsIcons {application} isAbsolute={false} />
|
||||||
|
</td>
|
||||||
|
<td class="px-10">
|
||||||
|
<div
|
||||||
|
class="badge badge-outline text-xs border-applications rounded text-white"
|
||||||
|
>
|
||||||
|
Application
|
||||||
|
{#if application.settings.isBot}
|
||||||
|
| BOT
|
||||||
|
{/if}
|
||||||
|
</div></td
|
||||||
|
>
|
||||||
|
<td class="flex justify-end">
|
||||||
|
{#if application.fqdn}
|
||||||
|
<a
|
||||||
|
href={application.fqdn}
|
||||||
|
target="_blank"
|
||||||
|
class="icons bg-transparent text-sm inline-flex"
|
||||||
|
><svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-6 w-6"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
fill="none"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
|
<path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" />
|
||||||
|
<line x1="10" y1="14" x2="20" y2="4" />
|
||||||
|
<polyline points="15 4 20 4 20 9" />
|
||||||
|
</svg></a
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
|
{#if application.settings.isBot && application.exposePort}
|
||||||
|
<a
|
||||||
|
href={`http://${dev ? 'localhost' : settings.ipv4}:${
|
||||||
|
application.exposePort
|
||||||
|
}`}
|
||||||
|
target="_blank"
|
||||||
|
class="icons bg-transparent text-sm inline-flex"
|
||||||
|
><svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-6 w-6"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
fill="none"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
|
<path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" />
|
||||||
|
<line x1="10" y1="14" x2="20" y2="4" />
|
||||||
|
<polyline points="15 4 20 4 20 9" />
|
||||||
|
</svg></a
|
||||||
|
>
|
||||||
{/if}
|
{/if}
|
||||||
{/await}
|
|
||||||
<div class="inline-flex">{application.name}</div>
|
|
||||||
</td>
|
|
||||||
<td class="px-10 inline-flex">
|
|
||||||
<ApplicationsIcons {application} isAbsolute={false} />
|
|
||||||
</td>
|
|
||||||
<td class="px-10">
|
|
||||||
<div class="badge badge-outline text-xs border-applications rounded text-white">
|
|
||||||
Application
|
|
||||||
</div></td
|
|
||||||
>
|
|
||||||
<td class="flex justify-end">
|
|
||||||
{#if application.fqdn}
|
|
||||||
<a
|
<a
|
||||||
href={application.fqdn}
|
href={`/applications/${application.id}`}
|
||||||
target="_blank"
|
|
||||||
class="icons bg-transparent text-sm inline-flex"
|
class="icons bg-transparent text-sm inline-flex"
|
||||||
><svg
|
>
|
||||||
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="h-6 w-6"
|
class="h-6 w-6"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
@ -142,72 +195,72 @@
|
|||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
>
|
>
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
<path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" />
|
<rect x="4" y="8" width="4" height="4" />
|
||||||
<line x1="10" y1="14" x2="20" y2="4" />
|
<line x1="6" y1="4" x2="6" y2="8" />
|
||||||
<polyline points="15 4 20 4 20 9" />
|
<line x1="6" y1="12" x2="6" y2="20" />
|
||||||
</svg></a
|
<rect x="10" y="14" width="4" height="4" />
|
||||||
>
|
<line x1="12" y1="4" x2="12" y2="14" />
|
||||||
{/if}
|
<line x1="12" y1="18" x2="12" y2="20" />
|
||||||
<a
|
<rect x="16" y="5" width="4" height="4" />
|
||||||
href={`/applications/${application.id}`}
|
<line x1="18" y1="4" x2="18" y2="5" />
|
||||||
class="icons bg-transparent text-sm inline-flex"
|
<line x1="18" y1="9" x2="18" y2="20" />
|
||||||
>
|
</svg>
|
||||||
<svg
|
</a>
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
</td>
|
||||||
class="h-6 w-6"
|
</tr>
|
||||||
viewBox="0 0 24 24"
|
{/each}
|
||||||
stroke-width="1.5"
|
|
||||||
stroke="currentColor"
|
|
||||||
fill="none"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
>
|
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
||||||
<rect x="4" y="8" width="4" height="4" />
|
|
||||||
<line x1="6" y1="4" x2="6" y2="8" />
|
|
||||||
<line x1="6" y1="12" x2="6" y2="20" />
|
|
||||||
<rect x="10" y="14" width="4" height="4" />
|
|
||||||
<line x1="12" y1="4" x2="12" y2="14" />
|
|
||||||
<line x1="12" y1="18" x2="12" y2="20" />
|
|
||||||
<rect x="16" y="5" width="4" height="4" />
|
|
||||||
<line x1="18" y1="4" x2="18" y2="5" />
|
|
||||||
<line x1="18" y1="9" x2="18" y2="20" />
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{/each}
|
|
||||||
|
|
||||||
{#each services as service}
|
{#each services as service}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="space-x-2 items-center tracking-tight font-bold">
|
<td class="space-x-2 items-center tracking-tight font-bold">
|
||||||
{#await getStatus(service)}
|
{#await getStatus(service)}
|
||||||
<div class="inline-flex w-2 h-2 bg-yellow-500 rounded-full" />
|
<div class="inline-flex w-2 h-2 bg-yellow-500 rounded-full" />
|
||||||
{:then status}
|
{:then status}
|
||||||
{#if status === 'Running'}
|
{#if status === 'Running'}
|
||||||
<div class="inline-flex w-2 h-2 bg-success rounded-full" />
|
<div class="inline-flex w-2 h-2 bg-success rounded-full" />
|
||||||
{:else}
|
{:else}
|
||||||
<div class="inline-flex w-2 h-2 bg-error rounded-full" />
|
<div class="inline-flex w-2 h-2 bg-error rounded-full" />
|
||||||
|
{/if}
|
||||||
|
{/await}
|
||||||
|
<div class="inline-flex">{service.name}</div>
|
||||||
|
</td>
|
||||||
|
<td class="px-10 inline-flex">
|
||||||
|
<ServiceIcons type={service.type} isAbsolute={false} />
|
||||||
|
</td>
|
||||||
|
<td class="px-10"
|
||||||
|
><div class="badge badge-outline text-xs border-services rounded text-white">
|
||||||
|
Service
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="flex justify-end">
|
||||||
|
{#if service.fqdn}
|
||||||
|
<a
|
||||||
|
href={service.fqdn}
|
||||||
|
target="_blank"
|
||||||
|
class="icons bg-transparent text-sm inline-flex"
|
||||||
|
><svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-6 w-6"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
fill="none"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
|
<path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" />
|
||||||
|
<line x1="10" y1="14" x2="20" y2="4" />
|
||||||
|
<polyline points="15 4 20 4 20 9" />
|
||||||
|
</svg></a
|
||||||
|
>
|
||||||
{/if}
|
{/if}
|
||||||
{/await}
|
|
||||||
<div class="inline-flex">{service.name}</div>
|
|
||||||
</td>
|
|
||||||
<td class="px-10 inline-flex">
|
|
||||||
<ServiceIcons type={service.type} isAbsolute={false} />
|
|
||||||
</td>
|
|
||||||
<td class="px-10"
|
|
||||||
><div class="badge badge-outline text-xs border-services rounded text-white">
|
|
||||||
Service
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td class="flex justify-end">
|
|
||||||
{#if service.fqdn}
|
|
||||||
<a
|
<a
|
||||||
href={service.fqdn}
|
href={`/services/${service.id}`}
|
||||||
target="_blank"
|
|
||||||
class="icons bg-transparent text-sm inline-flex"
|
class="icons bg-transparent text-sm inline-flex"
|
||||||
><svg
|
>
|
||||||
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="h-6 w-6"
|
class="h-6 w-6"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
@ -218,97 +271,76 @@
|
|||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
>
|
>
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
<path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" />
|
<rect x="4" y="8" width="4" height="4" />
|
||||||
<line x1="10" y1="14" x2="20" y2="4" />
|
<line x1="6" y1="4" x2="6" y2="8" />
|
||||||
<polyline points="15 4 20 4 20 9" />
|
<line x1="6" y1="12" x2="6" y2="20" />
|
||||||
</svg></a
|
<rect x="10" y="14" width="4" height="4" />
|
||||||
|
<line x1="12" y1="4" x2="12" y2="14" />
|
||||||
|
<line x1="12" y1="18" x2="12" y2="20" />
|
||||||
|
<rect x="16" y="5" width="4" height="4" />
|
||||||
|
<line x1="18" y1="4" x2="18" y2="5" />
|
||||||
|
<line x1="18" y1="9" x2="18" y2="20" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
{#each databases as database}
|
||||||
|
<tr>
|
||||||
|
<td class="space-x-2 items-center tracking-tight font-bold">
|
||||||
|
{#await getStatus(database)}
|
||||||
|
<div class="inline-flex w-2 h-2 bg-yellow-500 rounded-full" />
|
||||||
|
{:then status}
|
||||||
|
{#if status === 'Running'}
|
||||||
|
<div class="inline-flex w-2 h-2 bg-success rounded-full" />
|
||||||
|
{:else}
|
||||||
|
<div class="inline-flex w-2 h-2 bg-error rounded-full" />
|
||||||
|
{/if}
|
||||||
|
{/await}
|
||||||
|
<div class="inline-flex">{database.name}</div>
|
||||||
|
</td>
|
||||||
|
<td class="px-10 inline-flex">
|
||||||
|
<DatabaseIcons type={database.type} />
|
||||||
|
</td>
|
||||||
|
<td class="px-10">
|
||||||
|
<div class="badge badge-outline text-xs border-databases rounded text-white">
|
||||||
|
Database
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="flex justify-end">
|
||||||
|
<a
|
||||||
|
href={`/databases/${database.id}`}
|
||||||
|
class="icons bg-transparent text-sm inline-flex ml-11"
|
||||||
>
|
>
|
||||||
{/if}
|
<svg
|
||||||
<a
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
href={`/services/${service.id}`}
|
class="h-6 w-6"
|
||||||
class="icons bg-transparent text-sm inline-flex"
|
viewBox="0 0 24 24"
|
||||||
>
|
stroke-width="1.5"
|
||||||
<svg
|
stroke="currentColor"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
fill="none"
|
||||||
class="h-6 w-6"
|
stroke-linecap="round"
|
||||||
viewBox="0 0 24 24"
|
stroke-linejoin="round"
|
||||||
stroke-width="1.5"
|
>
|
||||||
stroke="currentColor"
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
fill="none"
|
<rect x="4" y="8" width="4" height="4" />
|
||||||
stroke-linecap="round"
|
<line x1="6" y1="4" x2="6" y2="8" />
|
||||||
stroke-linejoin="round"
|
<line x1="6" y1="12" x2="6" y2="20" />
|
||||||
>
|
<rect x="10" y="14" width="4" height="4" />
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
<line x1="12" y1="4" x2="12" y2="14" />
|
||||||
<rect x="4" y="8" width="4" height="4" />
|
<line x1="12" y1="18" x2="12" y2="20" />
|
||||||
<line x1="6" y1="4" x2="6" y2="8" />
|
<rect x="16" y="5" width="4" height="4" />
|
||||||
<line x1="6" y1="12" x2="6" y2="20" />
|
<line x1="18" y1="4" x2="18" y2="5" />
|
||||||
<rect x="10" y="14" width="4" height="4" />
|
<line x1="18" y1="9" x2="18" y2="20" />
|
||||||
<line x1="12" y1="4" x2="12" y2="14" />
|
</svg>
|
||||||
<line x1="12" y1="18" x2="12" y2="20" />
|
</a>
|
||||||
<rect x="16" y="5" width="4" height="4" />
|
</td>
|
||||||
<line x1="18" y1="4" x2="18" y2="5" />
|
</tr>
|
||||||
<line x1="18" y1="9" x2="18" y2="20" />
|
{/each}
|
||||||
</svg>
|
</tbody>
|
||||||
</a>
|
</table>
|
||||||
</td>
|
</div>
|
||||||
</tr>
|
|
||||||
{/each}
|
|
||||||
{#each databases as database}
|
|
||||||
<tr>
|
|
||||||
<td class="space-x-2 items-center tracking-tight font-bold">
|
|
||||||
{#await getStatus(database)}
|
|
||||||
<div class="inline-flex w-2 h-2 bg-yellow-500 rounded-full" />
|
|
||||||
{:then status}
|
|
||||||
{#if status === 'Running'}
|
|
||||||
<div class="inline-flex w-2 h-2 bg-success rounded-full" />
|
|
||||||
{:else}
|
|
||||||
<div class="inline-flex w-2 h-2 bg-error rounded-full" />
|
|
||||||
{/if}
|
|
||||||
{/await}
|
|
||||||
<div class="inline-flex">{database.name}</div>
|
|
||||||
</td>
|
|
||||||
<td class="px-10 inline-flex">
|
|
||||||
<DatabaseIcons type={database.type} />
|
|
||||||
</td>
|
|
||||||
<td class="px-10">
|
|
||||||
<div class="badge badge-outline text-xs border-databases rounded text-white">
|
|
||||||
Database
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="flex justify-end">
|
|
||||||
<a
|
|
||||||
href={`/databases/${database.id}`}
|
|
||||||
class="icons bg-transparent text-sm inline-flex ml-11"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
class="h-6 w-6"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke-width="1.5"
|
|
||||||
stroke="currentColor"
|
|
||||||
fill="none"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
>
|
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
||||||
<rect x="4" y="8" width="4" height="4" />
|
|
||||||
<line x1="6" y1="4" x2="6" y2="8" />
|
|
||||||
<line x1="6" y1="12" x2="6" y2="20" />
|
|
||||||
<rect x="10" y="14" width="4" height="4" />
|
|
||||||
<line x1="12" y1="4" x2="12" y2="14" />
|
|
||||||
<line x1="12" y1="18" x2="12" y2="20" />
|
|
||||||
<rect x="16" y="5" width="4" height="4" />
|
|
||||||
<line x1="18" y1="4" x2="18" y2="5" />
|
|
||||||
<line x1="18" y1="9" x2="18" y2="20" />
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{/each}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
{#if $appSession.teamId === '0'}
|
{#if $appSession.teamId === '0'}
|
||||||
<Usage />
|
<Usage />
|
||||||
|
@ -322,13 +322,13 @@
|
|||||||
<div class="flex-col space-y-2 pt-4 text-center">
|
<div class="flex-col space-y-2 pt-4 text-center">
|
||||||
{#if isNonWWWDomainOK}
|
{#if isNonWWWDomainOK}
|
||||||
<button
|
<button
|
||||||
class="bg-green-600 hover:bg-green-500"
|
class="btn btn-sm bg-green-600 hover:bg-green-500"
|
||||||
on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
|
on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
|
||||||
>DNS settings for {nonWWWDomain} is OK, click to recheck.</button
|
>DNS settings for {nonWWWDomain} is OK, click to recheck.</button
|
||||||
>
|
>
|
||||||
{:else}
|
{:else}
|
||||||
<button
|
<button
|
||||||
class="bg-red-600 hover:bg-red-500"
|
class="btn btn-sm bg-red-600 hover:bg-red-500"
|
||||||
on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
|
on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
|
||||||
>DNS settings for {nonWWWDomain} is invalid, click to recheck.</button
|
>DNS settings for {nonWWWDomain} is invalid, click to recheck.</button
|
||||||
>
|
>
|
||||||
@ -336,13 +336,13 @@
|
|||||||
{#if dualCerts}
|
{#if dualCerts}
|
||||||
{#if isWWWDomainOK}
|
{#if isWWWDomainOK}
|
||||||
<button
|
<button
|
||||||
class="bg-green-600 hover:bg-green-500"
|
class="btn btn-sm bg-green-600 hover:bg-green-500"
|
||||||
on:click|preventDefault={() => isDNSValid(getDomain(`www.${nonWWWDomain}`), true)}
|
on:click|preventDefault={() => isDNSValid(getDomain(`www.${nonWWWDomain}`), true)}
|
||||||
>DNS settings for www.{nonWWWDomain} is OK, click to recheck.</button
|
>DNS settings for www.{nonWWWDomain} is OK, click to recheck.</button
|
||||||
>
|
>
|
||||||
{:else}
|
{:else}
|
||||||
<button
|
<button
|
||||||
class="bg-red-600 hover:bg-red-500"
|
class="btn btn-sm bg-red-600 hover:bg-red-500"
|
||||||
on:click|preventDefault={() => isDNSValid(getDomain(`www.${nonWWWDomain}`), true)}
|
on:click|preventDefault={() => isDNSValid(getDomain(`www.${nonWWWDomain}`), true)}
|
||||||
>DNS settings for www.{nonWWWDomain} is invalid, click to recheck.</button
|
>DNS settings for www.{nonWWWDomain} is invalid, click to recheck.</button
|
||||||
>
|
>
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
let dualCerts = settings.dualCerts;
|
let dualCerts = settings.dualCerts;
|
||||||
let isAutoUpdateEnabled = settings.isAutoUpdateEnabled;
|
let isAutoUpdateEnabled = settings.isAutoUpdateEnabled;
|
||||||
let isDNSCheckEnabled = settings.isDNSCheckEnabled;
|
let isDNSCheckEnabled = settings.isDNSCheckEnabled;
|
||||||
|
let DNSServers = settings.DNSServers;
|
||||||
let minPort = settings.minPort;
|
let minPort = settings.minPort;
|
||||||
let maxPort = settings.maxPort;
|
let maxPort = settings.maxPort;
|
||||||
|
|
||||||
@ -105,6 +105,10 @@
|
|||||||
settings.minPort = minPort;
|
settings.minPort = minPort;
|
||||||
settings.maxPort = maxPort;
|
settings.maxPort = maxPort;
|
||||||
}
|
}
|
||||||
|
if (DNSServers !== settings.DNSServers) {
|
||||||
|
await post(`/settings`, { DNSServers });
|
||||||
|
settings.DNSServers = DNSServers;
|
||||||
|
}
|
||||||
forceSave = false;
|
forceSave = false;
|
||||||
return addToast({
|
return addToast({
|
||||||
message: 'Configuration saved.',
|
message: 'Configuration saved.',
|
||||||
@ -275,6 +279,17 @@
|
|||||||
on:click={() => changeSettings('isDNSCheckEnabled')}
|
on:click={() => changeSettings('isDNSCheckEnabled')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="grid grid-cols-2 items-center">
|
||||||
|
<div class="flex-col">
|
||||||
|
<div class="pt-2 text-base font-bold text-stone-100">
|
||||||
|
Custom DNS servers
|
||||||
|
</div>
|
||||||
|
<Explainer text="You can specify a custom DNS server to verify your domains all over Coolify.<br><br>By default, the OS defined DNS servers are used." />
|
||||||
|
</div>
|
||||||
|
<div class="mx-auto flex-row items-center justify-center space-y-2">
|
||||||
|
<input placeholder="1.1.1.1,8.8.8.8" bind:value={DNSServers} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center">
|
||||||
<Setting
|
<Setting
|
||||||
dataTooltip={$t('setting.must_remove_domain_before_changing')}
|
dataTooltip={$t('setting.must_remove_domain_before_changing')}
|
||||||
|
@ -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.4.0",
|
"version": "3.5.0",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"repository": "github:coollabsio/coolify",
|
"repository": "github:coollabsio/coolify",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
Loading…
Reference in New Issue
Block a user