Merge remote-tracking branch 'upstream/main'

This commit is contained in:
Lucas Lima 2022-08-27 12:56:21 +00:00
commit 391f21f57e
31 changed files with 234 additions and 149 deletions

View File

@ -34,4 +34,4 @@ jobs:
- uses: sarisia/actions-status-discord@v1 - uses: sarisia/actions-status-discord@v1
if: always() if: always()
with: with:
webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_CHANNEL }} webhook: ${{ secrets.DISCORD_WEBHOOK_PROD_RELEASE_CHANNEL }}

View File

@ -32,4 +32,4 @@ jobs:
- uses: sarisia/actions-status-discord@v1 - uses: sarisia/actions-status-discord@v1
if: always() if: always()
with: with:
webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_CHANNEL }} webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL }}

3
.gitignore vendored
View File

@ -11,4 +11,5 @@ dist
client client
apps/api/db/*.db apps/api/db/*.db
local-serve local-serve
apps/api/db/migration.db-journal apps/api/db/migration.db-journal
apps/api/core*

View File

@ -161,7 +161,7 @@ async function initServer() {
} catch (error) { } } catch (error) { }
try { try {
const isOlder = compareVersions('3.8.1', version); const isOlder = compareVersions('3.8.1', version);
if (isOlder === -1) { if (isOlder === 1) {
await prisma.build.updateMany({ where: { status: { in: ['running', 'queued'] } }, data: { status: 'failed' } }); await prisma.build.updateMany({ where: { status: { in: ['running', 'queued'] } }, data: { status: 'failed' } });
} }
} catch (error) { } } catch (error) { }

View File

@ -4,7 +4,7 @@ import fs from 'fs/promises';
import yaml from 'js-yaml'; import yaml from 'js-yaml';
import { copyBaseConfigurationFiles, makeLabelForStandaloneApplication, saveBuildLog, setDefaultConfiguration } from '../lib/buildPacks/common'; import { copyBaseConfigurationFiles, makeLabelForStandaloneApplication, saveBuildLog, setDefaultConfiguration } from '../lib/buildPacks/common';
import { createDirectories, decrypt, defaultComposeConfiguration, executeDockerCmd, getDomain, prisma } from '../lib/common'; import { createDirectories, decrypt, defaultComposeConfiguration, executeDockerCmd, getDomain, prisma, decryptApplication } from '../lib/common';
import * as importers from '../lib/importers'; import * as importers from '../lib/importers';
import * as buildpacks from '../lib/buildPacks'; import * as buildpacks from '../lib/buildPacks';
@ -27,7 +27,7 @@ import * as buildpacks from '../lib/buildPacks';
const th = throttle(async () => { const th = throttle(async () => {
try { try {
const queuedBuilds = await prisma.build.findMany({ where: { status: 'queued' }, orderBy: { createdAt: 'asc' } }); const queuedBuilds = await prisma.build.findMany({ where: { status: { in: ['queued', 'running'] } }, orderBy: { createdAt: 'asc' } });
const { concurrentBuilds } = await prisma.setting.findFirst({}) const { concurrentBuilds } = await prisma.setting.findFirst({})
if (queuedBuilds.length > 0) { if (queuedBuilds.length > 0) {
parentPort.postMessage({ deploying: true }); parentPort.postMessage({ deploying: true });
@ -37,68 +37,72 @@ import * as buildpacks from '../lib/buildPacks';
for (const queueBuild of queuedBuilds) { for (const queueBuild of queuedBuilds) {
actions.push(async () => { actions.push(async () => {
const application = await prisma.application.findUnique({ where: { id: queueBuild.applicationId }, include: { destinationDocker: true, gitSource: { include: { githubApp: true, gitlabApp: true } }, persistentStorage: true, secrets: true, settings: true, teams: true } }) let application = await prisma.application.findUnique({ where: { id: queueBuild.applicationId }, include: { destinationDocker: true, gitSource: { include: { githubApp: true, gitlabApp: true } }, persistentStorage: true, secrets: true, settings: true, teams: true } })
const { id: buildId, type, sourceBranch = null, pullmergeRequestId = null, forceRebuild } = queueBuild let { id: buildId, type, sourceBranch = null, pullmergeRequestId = null, forceRebuild } = queueBuild
const { application = decryptApplication(application)
id: applicationId,
repository,
name,
destinationDocker,
destinationDockerId,
gitSource,
configHash,
fqdn,
projectId,
secrets,
phpModules,
settings,
persistentStorage,
pythonWSGI,
pythonModule,
pythonVariable,
denoOptions,
exposePort,
baseImage,
baseBuildImage,
deploymentType,
} = application
let {
branch,
buildPack,
port,
installCommand,
buildCommand,
startCommand,
baseDirectory,
publishDirectory,
dockerFileLocation,
denoMainFile
} = application
const currentHash = crypto
.createHash('sha256')
.update(
JSON.stringify({
pythonWSGI,
pythonModule,
pythonVariable,
deploymentType,
denoOptions,
baseImage,
baseBuildImage,
buildPack,
port,
exposePort,
installCommand,
buildCommand,
startCommand,
secrets,
branch,
repository,
fqdn
})
)
.digest('hex');
try { try {
if (queueBuild.status === 'running') {
await saveBuildLog({ line: 'Building halted, restarting...', buildId, applicationId: application.id });
}
const {
id: applicationId,
repository,
name,
destinationDocker,
destinationDockerId,
gitSource,
configHash,
fqdn,
projectId,
secrets,
phpModules,
settings,
persistentStorage,
pythonWSGI,
pythonModule,
pythonVariable,
denoOptions,
exposePort,
baseImage,
baseBuildImage,
deploymentType,
} = application
let {
branch,
buildPack,
port,
installCommand,
buildCommand,
startCommand,
baseDirectory,
publishDirectory,
dockerFileLocation,
denoMainFile
} = application
const currentHash = crypto
.createHash('sha256')
.update(
JSON.stringify({
pythonWSGI,
pythonModule,
pythonVariable,
deploymentType,
denoOptions,
baseImage,
baseBuildImage,
buildPack,
port,
exposePort,
installCommand,
buildCommand,
startCommand,
secrets,
branch,
repository,
fqdn
})
)
.digest('hex');
const { debug } = settings; const { debug } = settings;
if (concurrency === 1) { if (concurrency === 1) {
await prisma.build.updateMany({ await prisma.build.updateMany({
@ -258,7 +262,6 @@ import * as buildpacks from '../lib/buildPacks';
]; ];
if (secrets.length > 0) { if (secrets.length > 0) {
secrets.forEach((secret) => { secrets.forEach((secret) => {
secret.value = decrypt(secret.value)
if (pullmergeRequestId) { if (pullmergeRequestId) {
if (secret.isPRMRSecret) { if (secret.isPRMRSecret) {
envs.push(`${secret.name}=${secret.value}`); envs.push(`${secret.name}=${secret.value}`);
@ -353,13 +356,16 @@ import * as buildpacks from '../lib/buildPacks';
where: { id: buildId, status: { in: ['queued', 'running'] } }, where: { id: buildId, status: { in: ['queued', 'running'] } },
data: { status: 'failed' } data: { status: 'failed' }
}); });
await saveBuildLog({ line: error, buildId, applicationId }); await saveBuildLog({ line: error, buildId, applicationId: application.id });
} }
}); });
} }
await pAll.default(actions, { concurrency }) await pAll.default(actions, { concurrency })
} }
} catch (error) { } catch (error) {
console.log(error)
} finally { } finally {
} }
}) })

View File

@ -671,11 +671,10 @@ export async function buildCacheImageWithNode(data, imageForBuild) {
if (isPnpm) { if (isPnpm) {
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7'); Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7');
} }
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
if (installCommand) { if (installCommand) {
Dockerfile.push(`COPY .${baseDirectory || ''}/package.json ./`);
Dockerfile.push(`RUN ${installCommand}`); Dockerfile.push(`RUN ${installCommand}`);
} }
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
Dockerfile.push(`RUN ${buildCommand}`); Dockerfile.push(`RUN ${buildCommand}`);
await fs.writeFile(`${workdir}/Dockerfile-cache`, Dockerfile.join('\n')); await fs.writeFile(`${workdir}/Dockerfile-cache`, Dockerfile.join('\n'));
await buildImage({ ...data, isCache: true }); await buildImage({ ...data, isCache: true });

View File

@ -19,7 +19,7 @@ import * as serviceFields from './serviceFields'
import { saveBuildLog } from './buildPacks/common'; import { saveBuildLog } from './buildPacks/common';
import { scheduler } from './scheduler'; import { scheduler } from './scheduler';
export const version = '3.8.3'; export const version = '3.8.5';
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';
@ -1977,6 +1977,12 @@ export async function cleanupDockerStorage(dockerId, lowDiskSpace, force) {
} catch (error) { } catch (error) {
//console.log(error); //console.log(error);
} }
// Cleanup build caches
try {
await executeDockerCmd({ dockerId, command: `docker builder prune -a -f` })
} catch (error) {
//console.log(error);
}
} }
} }
@ -2022,3 +2028,27 @@ export function defaultComposeConfiguration(network: string): any {
} }
} }
} }
export function decryptApplication(application: any) {
if (application) {
if (application?.gitSource?.githubApp?.clientSecret) {
application.gitSource.githubApp.clientSecret = decrypt(application.gitSource.githubApp.clientSecret) || null;
}
if (application?.gitSource?.githubApp?.webhookSecret) {
application.gitSource.githubApp.webhookSecret = decrypt(application.gitSource.githubApp.webhookSecret) || null;
}
if (application?.gitSource?.githubApp?.privateKey) {
application.gitSource.githubApp.privateKey = decrypt(application.gitSource.githubApp.privateKey) || null;
}
if (application?.gitSource?.gitlabApp?.appSecret) {
application.gitSource.gitlabApp.appSecret = decrypt(application.gitSource.gitlabApp.appSecret) || null;
}
if (application?.secrets.length > 0) {
application.secrets = application.secrets.map((s: any) => {
s.value = decrypt(s.value) || null
return s;
});
}
return application;
}
}

View File

@ -34,7 +34,7 @@ export async function getImages(request: FastifyRequest<GetImages>) {
const { buildPack, deploymentType } = request.body const { buildPack, deploymentType } = request.body
let publishDirectory = undefined; let publishDirectory = undefined;
let port = undefined let port = undefined
const { baseImage, baseBuildImage, baseBuildImages, baseImages, } = setDefaultBaseImage( const { baseImage, baseBuildImage, baseBuildImages, baseImages } = setDefaultBaseImage(
buildPack, deploymentType buildPack, deploymentType
); );
if (buildPack === 'nextjs') { if (buildPack === 'nextjs') {
@ -56,8 +56,7 @@ export async function getImages(request: FastifyRequest<GetImages>) {
} }
} }
return { baseImage, baseImages, baseBuildImage, baseBuildImages, publishDirectory, port }
return { baseBuildImage, baseBuildImages, publishDirectory, port }
} catch ({ status, message }) { } catch ({ status, message }) {
return errorHandler({ status, message }) return errorHandler({ status, message })
} }
@ -232,7 +231,6 @@ export async function saveApplication(request: FastifyRequest<SaveApplication>,
baseBuildImage, baseBuildImage,
deploymentType deploymentType
} = request.body } = request.body
if (port) port = Number(port); if (port) port = Number(port);
if (exposePort) { if (exposePort) {
exposePort = Number(exposePort); exposePort = Number(exposePort);
@ -451,6 +449,7 @@ export async function deployApplication(request: FastifyRequest<DeployApplicatio
data: { data: {
id: buildId, id: buildId,
applicationId: id, applicationId: id,
sourceBranch: branch,
branch: application.branch, branch: application.branch,
pullmergeRequestId, pullmergeRequestId,
forceRebuild, forceRebuild,

View File

@ -158,8 +158,11 @@ export async function getTeam(request: FastifyRequest<OnlyId>, reply: FastifyRep
}); });
const team = await prisma.team.findUnique({ where: { id }, include: { permissions: true } }); const team = await prisma.team.findUnique({ where: { id }, include: { permissions: true } });
const invitations = await prisma.teamInvitation.findMany({ where: { teamId: team.id } }); const invitations = await prisma.teamInvitation.findMany({ where: { teamId: team.id } });
const { teams } = await prisma.user.findUnique({ where: { id: userId }, include: { teams: true } })
return { return {
currentTeam: teamId,
team, team,
teams,
permissions, permissions,
invitations invitations
}; };
@ -275,10 +278,10 @@ export async function inviteToTeam(request: FastifyRequest<InviteToTeam>, reply:
if (!userFound) { if (!userFound) {
throw { throw {
message: `No user found with '${email}' email address.` message: `No user found with '${email}' email address.`
}; };
} }
const uid = userFound.id; const uid = userFound.id;
if (uid === userId) { if (uid === userId) {
throw { throw {
message: `Invitation to yourself? Whaaaaat?` message: `Invitation to yourself? Whaaaaat?`
}; };

View File

@ -9,7 +9,7 @@ export async function listSources(request: FastifyRequest) {
try { try {
const teamId = request.user?.teamId; const teamId = request.user?.teamId;
const sources = await prisma.gitSource.findMany({ const sources = await prisma.gitSource.findMany({
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }, where: { teams: { some: { id: teamId === '0' ? undefined : teamId } }, githubApp: { gitSource: { forPublic: false } } },
include: { teams: true, githubApp: true, gitlabApp: true } include: { teams: true, githubApp: true, gitlabApp: true }
}); });
return { return {

View File

@ -146,7 +146,7 @@ export async function gitHubEvents(request: FastifyRequest<GitHubEvents>): Promi
message: 'Queued. Thank you!' message: 'Queued. Thank you!'
}; };
} else if (githubEvent === 'pull_request') { } else if (githubEvent === 'pull_request') {
const pullmergeRequestId = body.number; const pullmergeRequestId = body.number.toString();
const pullmergeRequestAction = body.action; const pullmergeRequestAction = body.action;
const sourceBranch = body.pull_request.head.ref.includes('/') ? body.pull_request.head.ref.split('/')[2] : body.pull_request.head.ref; const sourceBranch = body.pull_request.head.ref.includes('/') ? body.pull_request.head.ref.split('/')[2] : body.pull_request.head.ref;
if (!allowedActions.includes(pullmergeRequestAction)) { if (!allowedActions.includes(pullmergeRequestAction)) {

View File

@ -86,7 +86,7 @@
<div class="w-full"> <div class="w-full">
<div class="items-center grid grid-flow-row md:grid-flow-col md:w-96 gap-4"> <div class="items-center grid grid-flow-row md:grid-flow-col md:w-96 gap-4">
<h1 class="title text-4xl">Hardware Details</h1> <h1 class="title lg:text-3xl">Hardware Details</h1>
<div class="grid lg:grid-flow-col gap-4"> <div class="grid lg:grid-flow-col gap-4">
{#if $appSession.teamId === '0'} {#if $appSession.teamId === '0'}
<button <button
@ -103,42 +103,40 @@
</div> </div>
</div> </div>
<div class="divider" /> <div class="divider" />
<div class="grid grid-flow-col gap-4 grid-rows-3 lg:grid-rows-1"> <div class="grid grid-flow-col gap-4 grid-rows-3 justify-start lg:justify-center lg:grid-rows-1">
<div class="stats stats-vertical lg:stats-horizontal w-full mb-5 bg-transparent rounded"> <div class="stats stats-vertical min-w-[16rem] mb-5 rounded bg-transparent">
<div class="font-bold flex lg:justify-center">Memory</div>
<div class="stat"> <div class="stat">
<div class="stat-title">Total</div> <div class="stat-title">Total Memory</div>
<div class="stat-value text-2xl"> <div class="stat-value text-2xl">
{(usage?.memory.totalMemMb).toFixed(0)}<span class="text-sm">MB</span> {(usage?.memory.totalMemMb).toFixed(0)}<span class="text-sm">MB</span>
</div> </div>
</div> </div>
<div class="stat"> <div class="stat">
<div class="stat-title">Used</div> <div class="stat-title">Used Memory</div>
<div class="stat-value text-2xl"> <div class="stat-value text-2xl">
{(usage?.memory.usedMemMb).toFixed(0)}<span class="text-sm">MB</span> {(usage?.memory.usedMemMb).toFixed(0)}<span class="text-sm">MB</span>
</div> </div>
</div> </div>
<div class="stat"> <div class="stat">
<div class="stat-title">Free</div> <div class="stat-title">Free Memory</div>
<div class="stat-value text-2xl"> <div class="stat-value text-2xl">
{usage?.memory.freeMemPercentage}<span class="text-sm">%</span> {usage?.memory.freeMemPercentage}<span class="text-sm">%</span>
</div> </div>
</div> </div>
</div> </div>
<div class="stats stats-vertical lg:stats-horizontal w-full mb-5 bg-transparent rounded"> <div class="stats stats-vertical min-w-[20rem] mb-5 bg-transparent rounded">
<div class="font-bold flex lg:justify-center">CPU</div>
<div class="stat"> <div class="stat">
<div class="stat-title">Total</div> <div class="stat-title">Total CPU</div>
<div class="stat-value text-2xl"> <div class="stat-value text-2xl">
{usage?.cpu.count} {usage?.cpu.count}
</div> </div>
</div> </div>
<div class="stat"> <div class="stat">
<div class="stat-title">Usage</div> <div class="stat-title">CPU Usage</div>
<div class="stat-value text-2xl"> <div class="stat-value text-2xl">
{usage?.cpu.usage}<span class="text-sm">%</span> {usage?.cpu.usage}<span class="text-sm">%</span>
</div> </div>
@ -149,24 +147,23 @@
<div class="stat-value text-2xl">{usage?.cpu.load}</div> <div class="stat-value text-2xl">{usage?.cpu.load}</div>
</div> </div>
</div> </div>
<div class="stats stats-vertical lg:stats-horizontal w-full mb-5 bg-transparent rounded"> <div class="stats stats-vertical min-w-[16rem] mb-5 bg-transparent rounded">
<div class="font-bold flex lg:justify-center">Disk</div>
<div class="stat"> <div class="stat">
<div class="stat-title">Total</div> <div class="stat-title">Total Disk</div>
<div class="stat-value text-2xl"> <div class="stat-value text-2xl">
{usage?.disk.totalGb}<span class="text-sm">GB</span> {usage?.disk.totalGb}<span class="text-sm">GB</span>
</div> </div>
</div> </div>
<div class="stat"> <div class="stat">
<div class="stat-title">Used</div> <div class="stat-title">Used Disk</div>
<div class="stat-value text-2xl"> <div class="stat-value text-2xl">
{usage?.disk.usedGb}<span class="text-sm">GB</span> {usage?.disk.usedGb}<span class="text-sm">GB</span>
</div> </div>
</div> </div>
<div class="stat"> <div class="stat">
<div class="stat-title">Free</div> <div class="stat-title">Free Disk</div>
<div class="stat-value text-2xl"> <div class="stat-value text-2xl">
{usage?.disk.freePercentage}<span class="text-sm">%</span> {usage?.disk.freePercentage}<span class="text-sm">%</span>
</div> </div>

View File

@ -3,7 +3,7 @@
</script> </script>
<svg viewBox="0 0 128 128" class={isAbsolute ? 'absolute top-0 left-0 -m-8 h-16 w-16' : 'mx-auto w-10 h-10'}> <svg viewBox="0 0 128 128" class={isAbsolute ? 'absolute top-0 left-0 -m-5 h-12 w-12' : 'mx-auto w-10 h-10'}>
<path d="M124.8 52.1c-4.3-2.5-10-2.8-14.8-1.4-.6-5.2-4-9.7-8-12.9l-1.6-1.3-1.4 1.6c-2.7 3.1-3.5 8.3-3.1 12.3.3 2.9 1.2 5.9 3 8.3-1.4.8-2.9 1.9-4.3 2.4-2.8 1-5.9 2-8.9 2H79V49H66V24H51v12H26v13H13v14H1.8l-.2 1.5c-.5 6.4.3 12.6 3 18.5l1.1 2.2.1.2c7.9 13.4 21.7 19 36.8 19 29.2 0 53.3-13.1 64.3-40.6 7.4.4 15-1.8 18.6-8.9l.9-1.8-1.6-1zM28 39h10v11H28V39zm13.1 44.2c0 1.7-1.4 3.1-3.1 3.1-1.7 0-3.1-1.4-3.1-3.1 0-1.7 1.4-3.1 3.1-3.1 1.7.1 3.1 1.4 3.1 3.1zM28 52h10v11H28V52zm-13 0h11v11H15V52zm27.7 50.2c-15.8-.1-24.3-5.4-31.3-12.4 2.1.1 4.1.2 5.9.2 1.6 0 3.2 0 4.7-.1 3.9-.2 7.3-.7 10.1-1.5 2.3 5.3 6.5 10.2 14 13.8h-3.4zM51 63H40V52h11v11zm0-13H40V39h11v11zm13 13H53V52h11v11zm0-13H53V39h11v11zm0-13H53V26h11v11zm13 26H66V52h11v11zM38.8 81.2c-.2-.1-.5-.2-.8-.2-1.2 0-2.2 1-2.2 2.2 0 1.2 1 2.2 2.2 2.2s2.2-1 2.2-2.2c0-.3-.1-.6-.2-.8-.2.3-.4.5-.8.5-.5 0-.9-.4-.9-.9.1-.4.3-.7.5-.8z" fill="#019BC6"></path> <path d="M124.8 52.1c-4.3-2.5-10-2.8-14.8-1.4-.6-5.2-4-9.7-8-12.9l-1.6-1.3-1.4 1.6c-2.7 3.1-3.5 8.3-3.1 12.3.3 2.9 1.2 5.9 3 8.3-1.4.8-2.9 1.9-4.3 2.4-2.8 1-5.9 2-8.9 2H79V49H66V24H51v12H26v13H13v14H1.8l-.2 1.5c-.5 6.4.3 12.6 3 18.5l1.1 2.2.1.2c7.9 13.4 21.7 19 36.8 19 29.2 0 53.3-13.1 64.3-40.6 7.4.4 15-1.8 18.6-8.9l.9-1.8-1.6-1zM28 39h10v11H28V39zm13.1 44.2c0 1.7-1.4 3.1-3.1 3.1-1.7 0-3.1-1.4-3.1-3.1 0-1.7 1.4-3.1 3.1-3.1 1.7.1 3.1 1.4 3.1 3.1zM28 52h10v11H28V52zm-13 0h11v11H15V52zm27.7 50.2c-15.8-.1-24.3-5.4-31.3-12.4 2.1.1 4.1.2 5.9.2 1.6 0 3.2 0 4.7-.1 3.9-.2 7.3-.7 10.1-1.5 2.3 5.3 6.5 10.2 14 13.8h-3.4zM51 63H40V52h11v11zm0-13H40V39h11v11zm13 13H53V52h11v11zm0-13H53V39h11v11zm0-13H53V26h11v11zm13 26H66V52h11v11zM38.8 81.2c-.2-.1-.5-.2-.8-.2-1.2 0-2.2 1-2.2 2.2 0 1.2 1 2.2 2.2 2.2s2.2-1 2.2-2.2c0-.3-.1-.6-.2-.8-.2.3-.4.5-.8.5-.5 0-.9-.4-.9-.9.1-.4.3-.7.5-.8z" fill="#019BC6"></path>
</svg> </svg>

View File

@ -4,6 +4,6 @@
<img <img
alt="ghost logo" alt="ghost logo"
class={isAbsolute ? 'w-12 absolute top-0 left-0 -m-3 -mt-5' : 'w-8 mx-auto'} class={isAbsolute ? 'w-12 h-12 absolute top-0 left-0 -m-3 -mt-5' : 'w-8 h-8 mx-auto'}
src="/ghost.png" src="/ghost.png"
/> />

View File

@ -3,7 +3,7 @@
</script> </script>
<svg <svg
class={isAbsolute ? 'w-10 h-10 absolute top-0 left-0 -m-5' : 'w-8 mx-auto'} class={isAbsolute ? 'w-10 h-10 absolute top-0 left-0 -m-5' : 'w-8 h-8 mx-auto'}
viewBox="0 0 81 84" viewBox="0 0 81 84"
fill="none" fill="none"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"

View File

@ -4,7 +4,7 @@
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class={isAbsolute ? 'w-10 h-10 absolute top-0 left-0 -m-5' : 'w-8 mx-auto'} class={isAbsolute ? 'w-10 h-10 absolute top-0 left-0 -m-5' : 'w-8 h-8 mx-auto'}
fill="none" fill="none"
viewBox="0 0 140 140" viewBox="0 0 140 140"
data-lt-extension-installed="true" data-lt-extension-installed="true"

View File

@ -4,7 +4,7 @@
<svg <svg
viewBox="0 0 127 74" viewBox="0 0 127 74"
class={isAbsolute ? 'w-10 h-10 absolute top-0 left-0 -m-5' : 'w-8 mx-auto'} class={isAbsolute ? 'w-10 h-10 absolute top-0 left-0 -m-5' : 'w-8 h-8mx-auto'}
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
><path ><path
d="M.825 73.993l23.244-59.47A21.85 21.85 0 0144.42.625h14.014L35.19 60.096a21.85 21.85 0 01-20.352 13.897H.825z" d="M.825 73.993l23.244-59.47A21.85 21.85 0 0144.42.625h14.014L35.19 60.096a21.85 21.85 0 01-20.352 13.897H.825z"

View File

@ -4,6 +4,6 @@
<img <img
alt="minio logo" alt="minio logo"
class={isAbsolute ? 'w-7 absolute top-0 left-0 -m-3 -mt-5' : 'w-4 mx-auto'} class={isAbsolute ? 'w-7 h-12 absolute top-0 left-0 -m-3 -mt-5' : 'w-4 h-8 mx-auto'}
src="/minio.png" src="/minio.png"
/> />

View File

@ -3,6 +3,6 @@
</script> </script>
<img <img
alt="moodle logo" alt="moodle logo"
class={isAbsolute ? 'w-9 absolute top-0 left-0 -m-3 -mt-5' : 'w-8 mx-auto'} class={isAbsolute ? 'w-9 h-9 absolute top-0 left-0 -m-3 -mt-5' : 'w-8 h-8 mx-auto'}
src="/moodle.png" src="/moodle.png"
/> />

View File

@ -3,7 +3,7 @@
</script> </script>
<svg <svg
class={isAbsolute ? 'w-12 h-12 absolute top-0 left-0 -m-5' : 'w-8 mx-auto'} class={isAbsolute ? 'w-12 h-12 absolute top-0 left-0 -m-5' : 'w-8 h-8 mx-auto'}
viewBox="0 0 220 105" viewBox="0 0 220 105"
> >
<g> <g>

View File

@ -4,6 +4,6 @@
<img <img
alt="nocodb logo" alt="nocodb logo"
class={isAbsolute ? 'w-10 absolute top-0 left-0 -m-5' : 'w-8 mx-auto'} class={isAbsolute ? 'w-10 h-10 absolute top-0 left-0 -m-5' : 'w-8 h-8 mx-auto'}
src="/nocodb.png" src="/nocodb.png"
/> />

View File

@ -7,7 +7,7 @@
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 856.000000 856.000000" viewBox="0 0 856.000000 856.000000"
preserveAspectRatio="xMidYMid meet" preserveAspectRatio="xMidYMid meet"
class={isAbsolute ? 'w-10 h-10 absolute top-0 left-0 -m-5' : 'w-8 mx-auto'} class={isAbsolute ? 'w-10 h-10 absolute top-0 left-0 -m-5' : 'w-8 h-8 mx-auto'}
> >
<metadata> Created by potrace 1.11, written by Peter Selinger 2001-2013 </metadata> <metadata> Created by potrace 1.11, written by Peter Selinger 2001-2013 </metadata>
<g <g

View File

@ -3,7 +3,7 @@
</script> </script>
<svg <svg
class={isAbsolute ? 'w-10 h-10 absolute top-0 left-0 -m-5' : 'w-8 mx-auto'} class={isAbsolute ? 'w-10 h-10 absolute top-0 left-0 -m-5' : 'w-8 h-8mx-auto'}
viewBox="0 0 128 128" viewBox="0 0 128 128"
> >
<path <path

View File

@ -3,7 +3,7 @@
</script> </script>
<svg <svg
class={isAbsolute ? 'w-10 absolute top-0 left-0 -m-5' : 'w-8 mx-auto'} class={isAbsolute ? 'w-10 h-10 absolute top-0 left-0 -m-5' : 'w-8 h-8mx-auto'}
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.1" version="1.1"

View File

@ -2,7 +2,7 @@
export let isAbsolute = false; export let isAbsolute = false;
</script> </script>
<svg class={isAbsolute ? 'w-10 absolute top-0 left-0 -m-5' : 'w-8 mx-auto'} viewBox="0 0 128 128"> <svg class={isAbsolute ? 'w-10 h-10 absolute top-0 left-0 -m-5' : 'w-8 h-8mx-auto'} viewBox="0 0 128 128">
<path <path
fill-rule="evenodd" fill-rule="evenodd"
clip-rule="evenodd" clip-rule="evenodd"

View File

@ -120,7 +120,13 @@
<nav class="nav-main"> <nav class="nav-main">
<div class="flex h-screen w-full flex-col items-center transition-all duration-100"> <div class="flex h-screen w-full flex-col items-center transition-all duration-100">
{#if !$appSession.whiteLabeled} {#if !$appSession.whiteLabeled}
<div class="my-4 h-10 w-10"><img src="/favicon.png" alt="coolLabs logo" /></div> <div class="mb-2 mt-4 h-10 w-10">
<img src="/favicon.png" alt="coolLabs logo" />
</div>
{:else if $appSession.whiteLabeledDetails.icon}
<div class="mb-2 mt-4 h-10 w-10">
<img src={$appSession.whiteLabeledDetails.icon} alt="White labeled logo" />
</div>
{/if} {/if}
<div class="flex flex-col space-y-2 py-2" class:mt-2={$appSession.whiteLabeled}> <div class="flex flex-col space-y-2 py-2" class:mt-2={$appSession.whiteLabeled}>
<a <a

View File

@ -57,11 +57,12 @@
message: 'Secret added.', message: 'Secret added.',
type: 'success' type: 'success'
}); });
} } else {
addToast({ addToast({
message: 'Secret updated.', message: 'Secret updated.',
type: 'success' type: 'success'
}); });
}
dispatch('refresh'); dispatch('refresh');
} catch (error) { } catch (error) {
console.log(error); console.log(error);

View File

@ -114,10 +114,33 @@
buildPack: application.buildPack, buildPack: application.buildPack,
deploymentType: application.deploymentType deploymentType: application.deploymentType
}); });
application = { const baseImageCorrect = data.baseImages.filter(
...application, (image: any) => image.value === application.baseImage
...data );
}; if (baseImageCorrect.length === 0) {
application.baseImage = data.baseImage;
}
application.baseImages = data.baseImages;
const baseBuildImageCorrect = data.baseBuildImages.filter(
(image: any) => image.value === application.baseBuildImage
);
if (baseBuildImageCorrect.length === 0) {
application.baseBuildImage = data.baseBuildImage;
}
application.baseBuildImages = data.baseBuildImages;
if (application.deploymentType === 'static' && application.port !== '80') {
application.port = data.port;
}
if (application.deploymentType === 'node' && application.port === '80') {
application.port = data.port;
}
if (application.deploymentType === 'static' && !application.publishDirectory) {
application.publishDirectory = data.publishDirectory;
}
if (application.deploymentType === 'node' && application.publishDirectory === 'out') {
application.publishDirectory = data.publishDirectory;
}
} }
async function changeSettings(name: any) { async function changeSettings(name: any) {
if (name === 'debug') { if (name === 'debug') {
@ -631,9 +654,7 @@
bind:value={application.port} bind:value={application.port}
placeholder="{$t('forms.default')}: 'python' ? '8000' : '3000'" placeholder="{$t('forms.default')}: 'python' ? '8000' : '3000'"
/> />
<Explainer <Explainer text={'The port your application listens on.'} />
text={'The port your application listens on.'}
/>
</div> </div>
{/if} {/if}
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">

View File

@ -25,18 +25,38 @@
</script> </script>
<script lang="ts"> <script lang="ts">
export let team: any;
export let currentTeam: string;
export let teams: any;
import { page } from '$app/stores'; import { page } from '$app/stores';
import { errorNotification, handlerNotFoundLoad } from '$lib/common'; import { errorNotification, handlerNotFoundLoad } from '$lib/common';
import { appSession } from '$lib/store'; import { appSession } from '$lib/store';
import { t } from '$lib/translations'; import { t } from '$lib/translations';
import DeleteIcon from '$lib/components/DeleteIcon.svelte'; import DeleteIcon from '$lib/components/DeleteIcon.svelte';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import Cookies from 'js-cookie';
const { id } = $page.params; const { id } = $page.params;
async function deleteTeam() { async function deleteTeam() {
const sure = confirm('Are you sure you want to delete this team?'); const sure = confirm('Are you sure you want to delete this team?');
if (sure) { if (sure) {
try { try {
await del(`/iam/team/${id}`, { id }); await del(`/iam/team/${id}`, { id });
if (currentTeam === id) {
const switchTeam = teams.find((team: any) => team.id !== id);
const payload = await get(`/user?teamId=${switchTeam.id}`);
if (payload.token) {
Cookies.set('token', payload.token, {
path: '/'
});
$appSession.teamId = payload.teamId;
$appSession.userId = payload.userId;
$appSession.permission = payload.permission;
$appSession.isAdmin = payload.isAdmin;
return window.location.assign('/iam');
}
}
return await goto('/iam', { replaceState: true }); return await goto('/iam', { replaceState: true });
} catch (error) { } catch (error) {
return errorNotification(error); return errorNotification(error);
@ -47,16 +67,18 @@
{#if id !== 'new'} {#if id !== 'new'}
<nav class="nav-side"> <nav class="nav-side">
<button {#if team.id !== '0'}
on:click={deleteTeam} <button
type="submit" on:click={deleteTeam}
disabled={!$appSession.isAdmin} type="submit"
class:hover:text-red-500={$appSession.isAdmin} disabled={!$appSession.isAdmin}
class="icons tooltip tooltip-primary tooltip-left bg-transparent text-sm" class:hover:text-red-500={$appSession.isAdmin}
data-tip={$appSession.isAdmin class="icons tooltip tooltip-primary tooltip-left bg-transparent text-sm"
? 'Delete' data-tip={$appSession.isAdmin
: $t('destination.permission_denied_delete_destination')}><DeleteIcon /></button ? 'Delete'
> : $t('destination.permission_denied_delete_destination')}><DeleteIcon /></button
>
{/if}
</nav> </nav>
{/if} {/if}
<slot /> <slot />

View File

@ -76,13 +76,13 @@
<div class="flex space-x-1 p-6 font-bold"> <div class="flex space-x-1 p-6 font-bold">
<div class="mr-4 text-2xl tracking-tight">{$t('index.dashboard')}</div> <div class="mr-4 text-2xl tracking-tight">{$t('index.dashboard')}</div>
</div> </div>
<div class="container lg:mx-auto lg:p-0 p-5"> <div class="container lg:mx-auto lg:p-0 px-8 p-5">
{#if $appSession.teamId === '0'} {#if $appSession.teamId === '0'}
<Usage /> <Usage />
{/if} {/if}
<h1 class="title text-4xl mt-10">Applications</h1> <h1 class="title lg:text-3xl mt-10">Applications</h1>
<div class="divider" /> <div class="divider" />
<div class="grid grid-col gap-4 auto-cols-max grid-cols-1 lg:grid-cols-3 p-4"> <div class="grid grid-col gap-8 auto-cols-max grid-cols-1 lg:grid-cols-3 p-4">
{#if applications.length > 0} {#if applications.length > 0}
{#each applications as application} {#each applications as application}
<a class="no-underline mb-5" href={`/applications/${application.id}`}> <a class="no-underline mb-5" href={`/applications/${application.id}`}>
@ -97,12 +97,12 @@
{/if} {/if}
{/await} {/await}
<div class="w-full flex flex-row"> <div class="w-full flex flex-row">
<ApplicationsIcons {application} isAbsolute={false} /> <ApplicationsIcons {application} isAbsolute={true} />
<div class="w-full flex flex-col ml-5"> <div class="w-full flex flex-col">
<h1 class="font-bold text-xl truncate"> <h1 class="font-bold text-lg lg:text-sm truncate">
{application.name} {application.name}
{#if application.settings.isBot} {#if application.settings.isBot}
<span class="text-xs"> BOT</span> <span class="text-xs">BOT</span>
{/if} {/if}
</h1> </h1>
<div class="h-10"> <div class="h-10">
@ -165,9 +165,9 @@
<h1 class="">Nothing is configured yet.</h1> <h1 class="">Nothing is configured yet.</h1>
{/if} {/if}
</div> </div>
<h1 class="title text-4xl mt-10">Services</h1> <h1 class="title lg:text-3xl mt-10">Services</h1>
<div class="divider" /> <div class="divider" />
<div class="grid grid-col gap-4 auto-cols-max grid-cols-1 lg:grid-cols-3 p-4"> <div class="grid grid-col gap-8 auto-cols-max grid-cols-1 lg:grid-cols-3 p-4">
{#if services.length > 0} {#if services.length > 0}
{#each services as service} {#each services as service}
<a class="no-underline mb-5" href={`/services/${service.id}`}> <a class="no-underline mb-5" href={`/services/${service.id}`}>
@ -182,9 +182,9 @@
{/if} {/if}
{/await} {/await}
<div class="w-full flex flex-row"> <div class="w-full flex flex-row">
<ServiceIcons type={service.type} isAbsolute={false} /> <ServiceIcons type={service.type} isAbsolute={true} />
<div class="w-full flex flex-col ml-5"> <div class="w-full flex flex-col">
<h1 class="font-bold text-xl truncate">{service.name}</h1> <h1 class="font-bold text-lg lg:text-sm truncate">{service.name}</h1>
<div class="h-10"> <div class="h-10">
{#if service?.fqdn} {#if service?.fqdn}
<h2>{service?.fqdn.replace('https://', '').replace('http://', '')}</h2> <h2>{service?.fqdn.replace('https://', '').replace('http://', '')}</h2>
@ -223,9 +223,9 @@
{/if} {/if}
</div> </div>
<h1 class="title text-4xl mt-10">Databases</h1> <h1 class="title lg:text-3xl mt-10">Databases</h1>
<div class="divider" /> <div class="divider" />
<div class="grid grid-col gap-4 auto-cols-max grid-cols-1 lg:grid-cols-3 p-4 mb-32"> <div class="grid grid-col gap-8 auto-cols-max grid-cols-1 lg:grid-cols-3 p-4 mb-32">
{#if databases.length > 0} {#if databases.length > 0}
{#each databases as database} {#each databases as database}
<a class="no-underline mb-5" href={`/databases/${database.id}`}> <a class="no-underline mb-5" href={`/databases/${database.id}`}>
@ -239,14 +239,14 @@
<span class="indicator-item badge bg-error badge-xs" /> <span class="indicator-item badge bg-error badge-xs" />
{/if} {/if}
{/await} {/await}
<div class="w-full flex flex-row pt-2"> <div class="w-full flex flex-row">
<DatabaseIcons type={database.type} isAbsolute={false} /> <DatabaseIcons type={database.type} isAbsolute={true} />
<div class="w-full flex flex-col ml-5"> <div class="w-full flex flex-col">
<div class="h-10"> <div class="h-10">
<h1 class="font-bold text-xl truncate">{database.name}</h1> <h1 class="font-bold text-lg lg:text-sm truncate">{database.name}</h1>
<div class="h-10"> <div class="h-10">
{#if database?.version} {#if database?.version}
<h2>{database?.version}</h2> <h2 class="text-xs">{database?.version}</h2>
{:else} {:else}
<h2 class="text-red-500">Not configured</h2> <h2 class="text-red-500">Not configured</h2>
{/if} {/if}

View File

@ -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.8.3", "version": "3.8.5",
"license": "Apache-2.0", "license": "Apache-2.0",
"repository": "github:coollabsio/coolify", "repository": "github:coollabsio/coolify",
"scripts": { "scripts": {