Merge pull request #546 from coollabsio/public_repos
Import public repositories feature and more
This commit is contained in:
commit
70717dcbe5
@ -0,0 +1,42 @@
|
|||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_GitSource" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"forPublic" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"type" TEXT,
|
||||||
|
"apiUrl" TEXT,
|
||||||
|
"htmlUrl" TEXT,
|
||||||
|
"customPort" INTEGER NOT NULL DEFAULT 22,
|
||||||
|
"organization" TEXT,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
"githubAppId" TEXT,
|
||||||
|
"gitlabAppId" TEXT,
|
||||||
|
CONSTRAINT "GitSource_githubAppId_fkey" FOREIGN KEY ("githubAppId") REFERENCES "GithubApp" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT "GitSource_gitlabAppId_fkey" FOREIGN KEY ("gitlabAppId") REFERENCES "GitlabApp" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
INSERT INTO "new_GitSource" ("apiUrl", "createdAt", "customPort", "githubAppId", "gitlabAppId", "htmlUrl", "id", "name", "organization", "type", "updatedAt") SELECT "apiUrl", "createdAt", "customPort", "githubAppId", "gitlabAppId", "htmlUrl", "id", "name", "organization", "type", "updatedAt" FROM "GitSource";
|
||||||
|
DROP TABLE "GitSource";
|
||||||
|
ALTER TABLE "new_GitSource" RENAME TO "GitSource";
|
||||||
|
CREATE UNIQUE INDEX "GitSource_githubAppId_key" ON "GitSource"("githubAppId");
|
||||||
|
CREATE UNIQUE INDEX "GitSource_gitlabAppId_key" ON "GitSource"("gitlabAppId");
|
||||||
|
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,
|
||||||
|
"isPublicRepository" 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", "isBot", "previews", "updatedAt") SELECT "applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "isBot", "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;
|
@ -119,16 +119,17 @@ model Application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model ApplicationSettings {
|
model ApplicationSettings {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
applicationId String @unique
|
applicationId String @unique
|
||||||
dualCerts Boolean @default(false)
|
dualCerts Boolean @default(false)
|
||||||
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)
|
isBot Boolean @default(false)
|
||||||
createdAt DateTime @default(now())
|
isPublicRepository Boolean @default(false)
|
||||||
updatedAt DateTime @updatedAt
|
createdAt DateTime @default(now())
|
||||||
application Application @relation(fields: [applicationId], references: [id])
|
updatedAt DateTime @updatedAt
|
||||||
|
application Application @relation(fields: [applicationId], references: [id])
|
||||||
}
|
}
|
||||||
|
|
||||||
model ApplicationPersistentStorage {
|
model ApplicationPersistentStorage {
|
||||||
@ -238,6 +239,7 @@ model SshKey {
|
|||||||
model GitSource {
|
model GitSource {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
name String
|
name String
|
||||||
|
forPublic Boolean @default(false)
|
||||||
type String?
|
type String?
|
||||||
apiUrl String?
|
apiUrl String?
|
||||||
htmlUrl String?
|
htmlUrl String?
|
||||||
|
@ -66,6 +66,34 @@ async function main() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const github = await prisma.gitSource.findFirst({
|
||||||
|
where: { htmlUrl: 'https://github.com', forPublic: true }
|
||||||
|
});
|
||||||
|
const gitlab = await prisma.gitSource.findFirst({
|
||||||
|
where: { htmlUrl: 'https://gitlab.com', forPublic: true }
|
||||||
|
});
|
||||||
|
if (!github) {
|
||||||
|
await prisma.gitSource.create({
|
||||||
|
data: {
|
||||||
|
apiUrl: 'https://api.github.com',
|
||||||
|
htmlUrl: 'https://github.com',
|
||||||
|
forPublic: true,
|
||||||
|
name: 'Github Public',
|
||||||
|
type: 'github'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!gitlab) {
|
||||||
|
await prisma.gitSource.create({
|
||||||
|
data: {
|
||||||
|
apiUrl: 'https://gitlab.com/api/v4',
|
||||||
|
htmlUrl: 'https://gitlab.com',
|
||||||
|
forPublic: true,
|
||||||
|
name: 'Gitlab Public',
|
||||||
|
type: 'gitlab'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
main()
|
main()
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
|
@ -56,6 +56,7 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
baseImage,
|
baseImage,
|
||||||
baseBuildImage,
|
baseBuildImage,
|
||||||
deploymentType,
|
deploymentType,
|
||||||
|
forceRebuild
|
||||||
} = message
|
} = message
|
||||||
let {
|
let {
|
||||||
branch,
|
branch,
|
||||||
@ -69,6 +70,30 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
dockerFileLocation,
|
dockerFileLocation,
|
||||||
denoMainFile
|
denoMainFile
|
||||||
} = message
|
} = message
|
||||||
|
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 {
|
||||||
const { debug } = settings;
|
const { debug } = settings;
|
||||||
if (concurrency === 1) {
|
if (concurrency === 1) {
|
||||||
@ -131,7 +156,8 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
htmlUrl: gitSource.htmlUrl,
|
htmlUrl: gitSource.htmlUrl,
|
||||||
projectId,
|
projectId,
|
||||||
deployKeyId: gitSource.gitlabApp?.deployKeyId || null,
|
deployKeyId: gitSource.gitlabApp?.deployKeyId || null,
|
||||||
privateSshKey: decrypt(gitSource.gitlabApp?.privateSshKey) || null
|
privateSshKey: decrypt(gitSource.gitlabApp?.privateSshKey) || null,
|
||||||
|
forPublic: gitSource.forPublic
|
||||||
});
|
});
|
||||||
if (!commit) {
|
if (!commit) {
|
||||||
throw new Error('No commit found?');
|
throw new Error('No commit found?');
|
||||||
@ -146,38 +172,10 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pullmergeRequestId) {
|
if (!pullmergeRequestId) {
|
||||||
const currentHash = crypto
|
|
||||||
//@ts-ignore
|
|
||||||
.createHash('sha256')
|
|
||||||
.update(
|
|
||||||
JSON.stringify({
|
|
||||||
pythonWSGI,
|
|
||||||
pythonModule,
|
|
||||||
pythonVariable,
|
|
||||||
deploymentType,
|
|
||||||
denoOptions,
|
|
||||||
baseImage,
|
|
||||||
baseBuildImage,
|
|
||||||
buildPack,
|
|
||||||
port,
|
|
||||||
exposePort,
|
|
||||||
installCommand,
|
|
||||||
buildCommand,
|
|
||||||
startCommand,
|
|
||||||
secrets,
|
|
||||||
branch,
|
|
||||||
repository,
|
|
||||||
fqdn
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.digest('hex');
|
|
||||||
|
|
||||||
if (configHash !== currentHash) {
|
if (configHash !== currentHash) {
|
||||||
await prisma.application.update({
|
|
||||||
where: { id: applicationId },
|
|
||||||
data: { configHash: currentHash }
|
|
||||||
});
|
|
||||||
deployNeeded = true;
|
deployNeeded = true;
|
||||||
if (configHash) {
|
if (configHash) {
|
||||||
await saveBuildLog({ line: 'Configuration changed.', buildId, applicationId });
|
await saveBuildLog({ line: 'Configuration changed.', buildId, applicationId });
|
||||||
@ -200,8 +198,10 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
//
|
//
|
||||||
}
|
}
|
||||||
await copyBaseConfigurationFiles(buildPack, workdir, buildId, applicationId, baseImage);
|
await copyBaseConfigurationFiles(buildPack, workdir, buildId, applicationId, baseImage);
|
||||||
|
|
||||||
|
if (forceRebuild) deployNeeded = true
|
||||||
if (!imageFound || deployNeeded) {
|
if (!imageFound || deployNeeded) {
|
||||||
// if (true) {
|
// if (true) {
|
||||||
if (buildpacks[buildPack])
|
if (buildpacks[buildPack])
|
||||||
await buildpacks[buildPack]({
|
await buildpacks[buildPack]({
|
||||||
dockerId: destinationDocker.id,
|
dockerId: destinationDocker.id,
|
||||||
@ -250,7 +250,9 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
const envs = [];
|
const envs = [
|
||||||
|
`PORT=${port}`
|
||||||
|
];
|
||||||
if (secrets.length > 0) {
|
if (secrets.length > 0) {
|
||||||
secrets.forEach((secret) => {
|
secrets.forEach((secret) => {
|
||||||
if (pullmergeRequestId) {
|
if (pullmergeRequestId) {
|
||||||
@ -336,6 +338,10 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
}
|
}
|
||||||
await saveBuildLog({ line: 'Proxy will be updated shortly.', buildId, applicationId });
|
await saveBuildLog({ line: 'Proxy will be updated shortly.', buildId, applicationId });
|
||||||
await prisma.build.update({ where: { id: message.build_id }, data: { status: 'success' } });
|
await prisma.build.update({ where: { id: message.build_id }, data: { status: 'success' } });
|
||||||
|
if (!pullmergeRequestId) await prisma.application.update({
|
||||||
|
where: { id: applicationId },
|
||||||
|
data: { configHash: currentHash }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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.5.2';
|
export const version = '3.6.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';
|
||||||
@ -1176,6 +1176,25 @@ export async function updatePasswordInDb(database, user, newPassword, isRoot) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export async function checkExposedPort({ id, configuredPort, exposePort, dockerId, remoteIpAddress }: { id: string, configuredPort?: number, exposePort: number, dockerId: string, remoteIpAddress?: string }) {
|
||||||
|
if (exposePort < 1024 || exposePort > 65535) {
|
||||||
|
throw { status: 500, message: `Exposed Port needs to be between 1024 and 65535.` }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configuredPort) {
|
||||||
|
if (configuredPort !== exposePort) {
|
||||||
|
const availablePort = await getFreeExposedPort(id, exposePort, dockerId, remoteIpAddress);
|
||||||
|
if (availablePort.toString() !== exposePort.toString()) {
|
||||||
|
throw { status: 500, message: `Port ${exposePort} is already in use.` }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const availablePort = await getFreeExposedPort(id, exposePort, dockerId, remoteIpAddress);
|
||||||
|
if (availablePort.toString() !== exposePort.toString()) {
|
||||||
|
throw { status: 500, message: `Port ${exposePort} is already in use.` }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
export async function getFreeExposedPort(id, exposePort, dockerId, remoteIpAddress) {
|
export async function getFreeExposedPort(id, exposePort, dockerId, remoteIpAddress) {
|
||||||
const { default: getPort } = await import('get-port');
|
const { default: getPort } = await import('get-port');
|
||||||
const applicationUsed = await (
|
const applicationUsed = await (
|
||||||
|
@ -71,7 +71,6 @@ export async function removeContainer({
|
|||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const { stdout } = await executeDockerCmd({ dockerId, command: `docker inspect --format '{{json .State}}' ${id}` })
|
const { stdout } = await executeDockerCmd({ dockerId, command: `docker inspect --format '{{json .State}}' ${id}` })
|
||||||
console.log(id)
|
|
||||||
if (JSON.parse(stdout).Running) {
|
if (JSON.parse(stdout).Running) {
|
||||||
await executeDockerCmd({ dockerId, command: `docker stop -t 0 ${id}` })
|
await executeDockerCmd({ dockerId, command: `docker stop -t 0 ${id}` })
|
||||||
await executeDockerCmd({ dockerId, command: `docker rm ${id}` })
|
await executeDockerCmd({ dockerId, command: `docker rm ${id}` })
|
||||||
|
@ -12,7 +12,8 @@ export default async function ({
|
|||||||
htmlUrl,
|
htmlUrl,
|
||||||
branch,
|
branch,
|
||||||
buildId,
|
buildId,
|
||||||
customPort
|
customPort,
|
||||||
|
forPublic
|
||||||
}: {
|
}: {
|
||||||
applicationId: string;
|
applicationId: string;
|
||||||
workdir: string;
|
workdir: string;
|
||||||
@ -23,41 +24,55 @@ export default async function ({
|
|||||||
branch: string;
|
branch: string;
|
||||||
buildId: string;
|
buildId: string;
|
||||||
customPort: number;
|
customPort: number;
|
||||||
|
forPublic?: boolean;
|
||||||
}): Promise<string> {
|
}): Promise<string> {
|
||||||
const { default: got } = await import('got')
|
const { default: got } = await import('got')
|
||||||
const url = htmlUrl.replace('https://', '').replace('http://', '');
|
const url = htmlUrl.replace('https://', '').replace('http://', '');
|
||||||
await saveBuildLog({ line: 'GitHub importer started.', buildId, applicationId });
|
await saveBuildLog({ line: 'GitHub importer started.', buildId, applicationId });
|
||||||
|
if (forPublic) {
|
||||||
|
await saveBuildLog({
|
||||||
|
line: `Cloning ${repository}:${branch} branch.`,
|
||||||
|
buildId,
|
||||||
|
applicationId
|
||||||
|
});
|
||||||
|
await asyncExecShell(
|
||||||
|
`git clone -q -b ${branch} https://${url}/${repository}.git ${workdir}/ && cd ${workdir} && git submodule update --init --recursive && git lfs pull && cd .. `
|
||||||
|
);
|
||||||
|
|
||||||
const body = await prisma.githubApp.findUnique({ where: { id: githubAppId } });
|
} else {
|
||||||
if (body.privateKey) body.privateKey = decrypt(body.privateKey);
|
const body = await prisma.githubApp.findUnique({ where: { id: githubAppId } });
|
||||||
const { privateKey, appId, installationId } = body
|
if (body.privateKey) body.privateKey = decrypt(body.privateKey);
|
||||||
|
const { privateKey, appId, installationId } = body
|
||||||
|
const githubPrivateKey = privateKey.replace(/\\n/g, '\n').replace(/"/g, '');
|
||||||
|
|
||||||
const githubPrivateKey = privateKey.replace(/\\n/g, '\n').replace(/"/g, '');
|
const payload = {
|
||||||
|
iat: Math.round(new Date().getTime() / 1000),
|
||||||
const payload = {
|
exp: Math.round(new Date().getTime() / 1000 + 60),
|
||||||
iat: Math.round(new Date().getTime() / 1000),
|
iss: appId
|
||||||
exp: Math.round(new Date().getTime() / 1000 + 60),
|
};
|
||||||
iss: appId
|
const jwtToken = jsonwebtoken.sign(payload, githubPrivateKey, {
|
||||||
};
|
algorithm: 'RS256'
|
||||||
const jwtToken = jsonwebtoken.sign(payload, githubPrivateKey, {
|
});
|
||||||
algorithm: 'RS256'
|
const { token } = await got
|
||||||
});
|
.post(`${apiUrl}/app/installations/${installationId}/access_tokens`, {
|
||||||
const { token } = await got
|
headers: {
|
||||||
.post(`${apiUrl}/app/installations/${installationId}/access_tokens`, {
|
Authorization: `Bearer ${jwtToken}`,
|
||||||
headers: {
|
Accept: 'application/vnd.github.machine-man-preview+json'
|
||||||
Authorization: `Bearer ${jwtToken}`,
|
}
|
||||||
Accept: 'application/vnd.github.machine-man-preview+json'
|
})
|
||||||
}
|
.json();
|
||||||
})
|
await saveBuildLog({
|
||||||
.json();
|
line: `Cloning ${repository}:${branch} branch.`,
|
||||||
await saveBuildLog({
|
buildId,
|
||||||
line: `Cloning ${repository}:${branch} branch.`,
|
applicationId
|
||||||
buildId,
|
});
|
||||||
applicationId
|
await asyncExecShell(
|
||||||
});
|
`git clone -q -b ${branch} https://x-access-token:${token}@${url}/${repository}.git --config core.sshCommand="ssh -p ${customPort}" ${workdir}/ && cd ${workdir} && git submodule update --init --recursive && git lfs pull && cd .. `
|
||||||
await asyncExecShell(
|
);
|
||||||
`git clone -q -b ${branch} https://x-access-token:${token}@${url}/${repository}.git --config core.sshCommand="ssh -p ${customPort}" ${workdir}/ && cd ${workdir} && git submodule update --init --recursive && git lfs pull && cd .. `
|
}
|
||||||
);
|
|
||||||
const { stdout: commit } = await asyncExecShell(`cd ${workdir}/ && git rev-parse HEAD`);
|
const { stdout: commit } = await asyncExecShell(`cd ${workdir}/ && git rev-parse HEAD`);
|
||||||
|
|
||||||
return commit.replace('\n', '');
|
return commit.replace('\n', '');
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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, listSettings, prisma, stopBuild, uniqueName } from '../../../../lib/common';
|
import { checkDomainsIsValidInDNS, checkDoubleBranch, checkExposedPort, 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';
|
||||||
|
|
||||||
@ -238,6 +238,9 @@ export async function saveApplication(request: FastifyRequest<SaveApplication>,
|
|||||||
if (exposePort) {
|
if (exposePort) {
|
||||||
exposePort = Number(exposePort);
|
exposePort = Number(exposePort);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { destinationDockerId } = await prisma.application.findUnique({ where: { id } })
|
||||||
|
if (exposePort) await checkExposedPort({ id, exposePort, dockerId: destinationDockerId })
|
||||||
if (denoOptions) denoOptions = denoOptions.trim();
|
if (denoOptions) denoOptions = denoOptions.trim();
|
||||||
const defaultConfiguration = await setDefaultConfiguration({
|
const defaultConfiguration = await setDefaultConfiguration({
|
||||||
buildPack,
|
buildPack,
|
||||||
@ -392,18 +395,7 @@ export async function checkDNS(request: FastifyRequest<CheckDNS>) {
|
|||||||
if (found) {
|
if (found) {
|
||||||
throw { status: 500, message: `Domain ${getDomain(fqdn).replace('www.', '')} is already in use!` }
|
throw { status: 500, message: `Domain ${getDomain(fqdn).replace('www.', '')} is already in use!` }
|
||||||
}
|
}
|
||||||
if (exposePort) {
|
await checkExposedPort({ id, configuredPort, exposePort, dockerId, remoteIpAddress })
|
||||||
if (exposePort < 1024 || exposePort > 65535) {
|
|
||||||
throw { status: 500, message: `Exposed Port needs to be between 1024 and 65535.` }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (configuredPort !== exposePort) {
|
|
||||||
const availablePort = await getFreeExposedPort(id, exposePort, dockerId, remoteIpAddress);
|
|
||||||
if (availablePort.toString() !== exposePort.toString()) {
|
|
||||||
throw { status: 500, message: `Port ${exposePort} is already in use.` }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isDNSCheckEnabled && !isDev && !forceSave) {
|
if (isDNSCheckEnabled && !isDev && !forceSave) {
|
||||||
let hostname = request.hostname.split(':')[0];
|
let hostname = request.hostname.split(':')[0];
|
||||||
if (remoteEngine) hostname = remoteIpAddress;
|
if (remoteEngine) hostname = remoteIpAddress;
|
||||||
@ -436,7 +428,7 @@ export async function deployApplication(request: FastifyRequest<DeployApplicatio
|
|||||||
try {
|
try {
|
||||||
const { id } = request.params
|
const { id } = request.params
|
||||||
const teamId = request.user?.teamId;
|
const teamId = request.user?.teamId;
|
||||||
const { pullmergeRequestId = null, branch } = request.body
|
const { pullmergeRequestId = null, branch, forceRebuild } = request.body
|
||||||
const buildId = cuid();
|
const buildId = cuid();
|
||||||
const application = await getApplicationFromDB(id, teamId);
|
const application = await getApplicationFromDB(id, teamId);
|
||||||
if (application) {
|
if (application) {
|
||||||
@ -475,13 +467,15 @@ export async function deployApplication(request: FastifyRequest<DeployApplicatio
|
|||||||
type: 'manual',
|
type: 'manual',
|
||||||
...application,
|
...application,
|
||||||
sourceBranch: branch,
|
sourceBranch: branch,
|
||||||
pullmergeRequestId
|
pullmergeRequestId,
|
||||||
|
forceRebuild
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
scheduler.workers.get('deployApplication').postMessage({
|
scheduler.workers.get('deployApplication').postMessage({
|
||||||
build_id: buildId,
|
build_id: buildId,
|
||||||
type: 'manual',
|
type: 'manual',
|
||||||
...application
|
...application,
|
||||||
|
forceRebuild
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -499,11 +493,20 @@ export async function deployApplication(request: FastifyRequest<DeployApplicatio
|
|||||||
export async function saveApplicationSource(request: FastifyRequest<SaveApplicationSource>, reply: FastifyReply) {
|
export async function saveApplicationSource(request: FastifyRequest<SaveApplicationSource>, reply: FastifyReply) {
|
||||||
try {
|
try {
|
||||||
const { id } = request.params
|
const { id } = request.params
|
||||||
const { gitSourceId } = request.body
|
const { gitSourceId, forPublic, type } = request.body
|
||||||
await prisma.application.update({
|
if (forPublic) {
|
||||||
where: { id },
|
const publicGit = await prisma.gitSource.findFirst({ where: { type, forPublic } });
|
||||||
data: { gitSource: { connect: { id: gitSourceId } } }
|
await prisma.application.update({
|
||||||
});
|
where: { id },
|
||||||
|
data: { gitSource: { connect: { id: publicGit.id } } }
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await prisma.application.update({
|
||||||
|
where: { id },
|
||||||
|
data: { gitSource: { connect: { id: gitSourceId } } }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return reply.code(201).send()
|
return reply.code(201).send()
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message })
|
||||||
@ -557,7 +560,7 @@ export async function checkRepository(request: FastifyRequest<CheckRepository>)
|
|||||||
export async function saveRepository(request, reply) {
|
export async function saveRepository(request, reply) {
|
||||||
try {
|
try {
|
||||||
const { id } = request.params
|
const { id } = request.params
|
||||||
let { repository, branch, projectId, autodeploy, webhookToken } = request.body
|
let { repository, branch, projectId, autodeploy, webhookToken, isPublicRepository = false } = request.body
|
||||||
|
|
||||||
repository = repository.toLowerCase();
|
repository = repository.toLowerCase();
|
||||||
branch = branch.toLowerCase();
|
branch = branch.toLowerCase();
|
||||||
@ -565,17 +568,19 @@ export async function saveRepository(request, reply) {
|
|||||||
if (webhookToken) {
|
if (webhookToken) {
|
||||||
await prisma.application.update({
|
await prisma.application.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: { repository, branch, projectId, gitSource: { update: { gitlabApp: { update: { webhookToken: webhookToken ? webhookToken : undefined } } } }, settings: { update: { autodeploy } } }
|
data: { repository, branch, projectId, gitSource: { update: { gitlabApp: { update: { webhookToken: webhookToken ? webhookToken : undefined } } } }, settings: { update: { autodeploy, isPublicRepository } } }
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await prisma.application.update({
|
await prisma.application.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: { repository, branch, projectId, settings: { update: { autodeploy } } }
|
data: { repository, branch, projectId, settings: { update: { autodeploy, isPublicRepository } } }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const isDouble = await checkDoubleBranch(branch, projectId);
|
if (!isPublicRepository) {
|
||||||
if (isDouble) {
|
const isDouble = await checkDoubleBranch(branch, projectId);
|
||||||
await prisma.applicationSettings.updateMany({ where: { application: { branch, projectId } }, data: { autodeploy: false } })
|
if (isDouble) {
|
||||||
|
await prisma.applicationSettings.updateMany({ where: { application: { branch, projectId } }, data: { autodeploy: false, isPublicRepository } })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return reply.code(201).send()
|
return reply.code(201).send()
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
@ -607,7 +612,8 @@ export async function getBuildPack(request) {
|
|||||||
projectId: application.projectId,
|
projectId: application.projectId,
|
||||||
repository: application.repository,
|
repository: application.repository,
|
||||||
branch: application.branch,
|
branch: application.branch,
|
||||||
apiUrl: application.gitSource.apiUrl
|
apiUrl: application.gitSource.apiUrl,
|
||||||
|
isPublicRepository: application.settings.isPublicRepository
|
||||||
}
|
}
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message })
|
||||||
@ -658,7 +664,6 @@ export async function saveSecret(request: FastifyRequest<SaveSecret>, reply: Fas
|
|||||||
throw { status: 500, message: `Secret ${name} already exists.` }
|
throw { status: 500, message: `Secret ${name} already exists.` }
|
||||||
} else {
|
} else {
|
||||||
value = encrypt(value.trim());
|
value = encrypt(value.trim());
|
||||||
console.log({value})
|
|
||||||
await prisma.secret.create({
|
await prisma.secret.create({
|
||||||
data: { name, value, isBuildSecret, isPRMRSecret, application: { connect: { id } } }
|
data: { name, value, isBuildSecret, isPRMRSecret, application: { connect: { id } } }
|
||||||
});
|
});
|
||||||
|
@ -44,13 +44,13 @@ export interface CheckDNS extends OnlyId {
|
|||||||
}
|
}
|
||||||
export interface DeployApplication {
|
export interface DeployApplication {
|
||||||
Querystring: { domain: string }
|
Querystring: { domain: string }
|
||||||
Body: { pullmergeRequestId: string | null, branch: string }
|
Body: { pullmergeRequestId: string | null, branch: string, forceRebuild?: boolean }
|
||||||
}
|
}
|
||||||
export interface GetImages {
|
export interface GetImages {
|
||||||
Body: { buildPack: string, deploymentType: string }
|
Body: { buildPack: string, deploymentType: string }
|
||||||
}
|
}
|
||||||
export interface SaveApplicationSource extends OnlyId {
|
export interface SaveApplicationSource extends OnlyId {
|
||||||
Body: { gitSourceId: string }
|
Body: { gitSourceId?: string | null, forPublic?: boolean, type?: string }
|
||||||
}
|
}
|
||||||
export interface CheckRepository extends OnlyId {
|
export interface CheckRepository extends OnlyId {
|
||||||
Querystring: { repository: string, branch: string }
|
Querystring: { repository: string, branch: string }
|
||||||
@ -115,7 +115,8 @@ export interface CancelDeployment {
|
|||||||
export interface DeployApplication extends OnlyId {
|
export interface DeployApplication extends OnlyId {
|
||||||
Body: {
|
Body: {
|
||||||
pullmergeRequestId: string | null,
|
pullmergeRequestId: string | null,
|
||||||
branch: string
|
branch: string,
|
||||||
|
forceRebuild?: boolean
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +79,6 @@ export async function newDestination(request: FastifyRequest<NewDestination>, re
|
|||||||
|
|
||||||
let { name, network, engine, isCoolifyProxyUsed, remoteIpAddress, remoteUser, remotePort } = request.body
|
let { name, network, engine, isCoolifyProxyUsed, remoteIpAddress, remoteUser, remotePort } = request.body
|
||||||
if (id === 'new') {
|
if (id === 'new') {
|
||||||
console.log(engine)
|
|
||||||
if (engine) {
|
if (engine) {
|
||||||
const { stdout } = await asyncExecShell(`DOCKER_HOST=unix:///var/run/docker.sock docker network ls --filter 'name=^${network}$' --format '{{json .}}'`);
|
const { stdout } = await asyncExecShell(`DOCKER_HOST=unix:///var/run/docker.sock docker network ls --filter 'name=^${network}$' --format '{{json .}}'`);
|
||||||
if (stdout === '') {
|
if (stdout === '') {
|
||||||
|
@ -2,7 +2,7 @@ import type { FastifyReply, FastifyRequest } from 'fastify';
|
|||||||
import fs from 'fs/promises';
|
import fs from 'fs/promises';
|
||||||
import yaml from 'js-yaml';
|
import yaml from 'js-yaml';
|
||||||
import bcrypt from 'bcryptjs';
|
import bcrypt from 'bcryptjs';
|
||||||
import { prisma, uniqueName, asyncExecShell, getServiceImage, configureServiceType, getServiceFromDB, getContainerUsage, removeService, isDomainConfigured, saveUpdateableFields, fixType, decrypt, encrypt, getServiceMainPort, createDirectories, ComposeFile, makeLabelForServices, getFreePublicPort, getDomain, errorHandler, generatePassword, isDev, stopTcpHttpProxy, supportedServiceTypesAndVersions, executeDockerCmd, listSettings, getFreeExposedPort, checkDomainsIsValidInDNS, persistentVolumes, asyncSleep, isARM, defaultComposeConfiguration } from '../../../../lib/common';
|
import { prisma, uniqueName, asyncExecShell, getServiceImage, configureServiceType, getServiceFromDB, getContainerUsage, removeService, isDomainConfigured, saveUpdateableFields, fixType, decrypt, encrypt, getServiceMainPort, createDirectories, ComposeFile, makeLabelForServices, getFreePublicPort, getDomain, errorHandler, generatePassword, isDev, stopTcpHttpProxy, supportedServiceTypesAndVersions, executeDockerCmd, listSettings, getFreeExposedPort, checkDomainsIsValidInDNS, persistentVolumes, asyncSleep, isARM, defaultComposeConfiguration, checkExposedPort } from '../../../../lib/common';
|
||||||
import { day } from '../../../../lib/dayjs';
|
import { day } from '../../../../lib/dayjs';
|
||||||
import { checkContainer, isContainerExited, removeContainer } from '../../../../lib/docker';
|
import { checkContainer, isContainerExited, removeContainer } from '../../../../lib/docker';
|
||||||
import cuid from 'cuid';
|
import cuid from 'cuid';
|
||||||
@ -378,18 +378,7 @@ export async function checkService(request: FastifyRequest<CheckService>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (exposePort) {
|
await checkExposedPort({ id, configuredPort, exposePort, dockerId, remoteIpAddress })
|
||||||
if (exposePort < 1024 || exposePort > 65535) {
|
|
||||||
throw { status: 500, message: `Exposed Port needs to be between 1024 and 65535.` }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (configuredPort !== exposePort) {
|
|
||||||
const availablePort = await getFreeExposedPort(id, exposePort, dockerId, remoteIpAddress);
|
|
||||||
if (availablePort.toString() !== exposePort.toString()) {
|
|
||||||
throw { status: 500, message: `Port ${exposePort} is already in use.` }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isDNSCheckEnabled && !isDev && !forceSave) {
|
if (isDNSCheckEnabled && !isDev && !forceSave) {
|
||||||
let hostname = request.hostname.split(':')[0];
|
let hostname = request.hostname.split(':')[0];
|
||||||
if (remoteEngine) hostname = remoteIpAddress;
|
if (remoteEngine) hostname = remoteIpAddress;
|
||||||
|
@ -484,7 +484,6 @@ export async function traefikOtherConfiguration(request: FastifyRequest<TraefikO
|
|||||||
}
|
}
|
||||||
throw { status: 500 }
|
throw { status: 500 }
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
console.log(status, message);
|
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,7 +159,7 @@
|
|||||||
"storage_saved": "Storage saved.",
|
"storage_saved": "Storage saved.",
|
||||||
"storage_updated": "Storage updated.",
|
"storage_updated": "Storage updated.",
|
||||||
"storage_deleted": "Storage deleted.",
|
"storage_deleted": "Storage deleted.",
|
||||||
"persistent_storage_explainer": "You can specify any folder that you want to be persistent across deployments. <br>This is useful for storing data such as a database (SQLite) or a cache."
|
"persistent_storage_explainer": "You can specify any folder that you want to be persistent across deployments.<br><span class='text-green-500 font-bold'>/example</span> means it will preserve <span class='text-green-500 font-bold'>/app/example</span> in the container as <span class='text-green-500 font-bold'>/app</span> is <span class='text-green-500 font-bold'>the root directory</span> for your application.<br><br>This is useful for storing data such as a <span class='text-green-500 font-bold'>database (SQLite)</span> or a <span class='text-green-500 font-bold'>cache</span>."
|
||||||
},
|
},
|
||||||
"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}}'?",
|
||||||
|
@ -77,9 +77,9 @@
|
|||||||
|
|
||||||
const { id } = $page.params;
|
const { id } = $page.params;
|
||||||
|
|
||||||
async function handleDeploySubmit() {
|
async function handleDeploySubmit(forceRebuild = false) {
|
||||||
try {
|
try {
|
||||||
const { buildId } = await post(`/applications/${id}/deploy`, { ...application });
|
const { buildId } = await post(`/applications/${id}/deploy`, { ...application, forceRebuild });
|
||||||
addToast({
|
addToast({
|
||||||
message: $t('application.deployment_queued'),
|
message: $t('application.deployment_queued'),
|
||||||
type: 'success'
|
type: 'success'
|
||||||
@ -141,8 +141,7 @@
|
|||||||
if (
|
if (
|
||||||
application.gitSourceId &&
|
application.gitSourceId &&
|
||||||
application.destinationDockerId &&
|
application.destinationDockerId &&
|
||||||
(application.fqdn ||
|
(application.fqdn || application.settings.isBot)
|
||||||
application.settings.isBot)
|
|
||||||
) {
|
) {
|
||||||
await getStatus();
|
await getStatus();
|
||||||
statusInterval = setInterval(async () => {
|
statusInterval = setInterval(async () => {
|
||||||
@ -256,7 +255,7 @@
|
|||||||
<rect x="14" y="5" width="4" height="14" rx="1" />
|
<rect x="14" y="5" width="4" height="14" rx="1" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<form on:submit|preventDefault={handleDeploySubmit}>
|
<form on:submit|preventDefault={() => handleDeploySubmit(true)}>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={$disabledButton || !isQueueActive}
|
disabled={$disabledButton || !isQueueActive}
|
||||||
@ -264,7 +263,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'
|
? 'Force 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.'}
|
||||||
>
|
>
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
delete tempBuildPack.color;
|
delete tempBuildPack.color;
|
||||||
delete tempBuildPack.hoverColor;
|
delete tempBuildPack.hoverColor;
|
||||||
|
|
||||||
if (foundConfig.buildPack !== name) {
|
if (foundConfig?.buildPack !== name) {
|
||||||
await post(`/applications/${id}`, { ...tempBuildPack, buildPack: name });
|
await post(`/applications/${id}`, { ...tempBuildPack, buildPack: name });
|
||||||
}
|
}
|
||||||
await post(`/applications/${id}/configuration/buildpack`, { buildPack: name });
|
await post(`/applications/${id}/configuration/buildpack`, { buildPack: name });
|
||||||
|
@ -0,0 +1,199 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { get, post } from '$lib/api';
|
||||||
|
import { t } from '$lib/translations';
|
||||||
|
import { page } from '$app/stores';
|
||||||
|
|
||||||
|
import Select from 'svelte-select';
|
||||||
|
import Explainer from '$lib/components/Explainer.svelte';
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
import { errorNotification } from '$lib/common';
|
||||||
|
|
||||||
|
const { id } = $page.params;
|
||||||
|
|
||||||
|
let publicRepositoryLink: string;
|
||||||
|
let projectId: number;
|
||||||
|
let repositoryName: string;
|
||||||
|
let branchName: string;
|
||||||
|
let ownerName: string;
|
||||||
|
let type: string;
|
||||||
|
let branchSelectOptions: any = [];
|
||||||
|
let loading = {
|
||||||
|
branches: false
|
||||||
|
};
|
||||||
|
async function loadBranches() {
|
||||||
|
try {
|
||||||
|
loading.branches = true;
|
||||||
|
|
||||||
|
const protocol = publicRepositoryLink.split(':')[0];
|
||||||
|
const gitUrl = publicRepositoryLink.replace('http://', '').replace('https://', '');
|
||||||
|
|
||||||
|
let [host, ...path] = gitUrl.split('/');
|
||||||
|
const [owner, repository, ...branch] = path;
|
||||||
|
|
||||||
|
ownerName = owner;
|
||||||
|
repositoryName = repository;
|
||||||
|
|
||||||
|
if (host === 'github.com') {
|
||||||
|
host = 'api.github.com';
|
||||||
|
type = 'github';
|
||||||
|
if (branch[0] === 'tree' && branch[1]) {
|
||||||
|
branchName = branch[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (host === 'gitlab.com') {
|
||||||
|
host = 'gitlab.com/api/v4';
|
||||||
|
type = 'gitlab';
|
||||||
|
if (branch[1] === 'tree' && branch[2]) {
|
||||||
|
branchName = branch[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const apiUrl = `${protocol}://${host}`;
|
||||||
|
if (type === 'github') {
|
||||||
|
const repositoryDetails = await get(`${apiUrl}/repos/${ownerName}/${repositoryName}`);
|
||||||
|
projectId = repositoryDetails.id.toString();
|
||||||
|
}
|
||||||
|
if (type === 'gitlab') {
|
||||||
|
const repositoryDetails = await get(`${apiUrl}/projects/${ownerName}%2F${repositoryName}`);
|
||||||
|
projectId = repositoryDetails.id.toString();
|
||||||
|
}
|
||||||
|
if (type === 'github' && branchName) {
|
||||||
|
try {
|
||||||
|
await get(`${apiUrl}/repos/${ownerName}/${repositoryName}/branches/${branchName}`);
|
||||||
|
await saveRepository();
|
||||||
|
loading.branches = false;
|
||||||
|
return;
|
||||||
|
} catch (error) {
|
||||||
|
errorNotification(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (type === 'gitlab' && branchName) {
|
||||||
|
try {
|
||||||
|
await get(
|
||||||
|
`${apiUrl}/projects/${ownerName}%2F${repositoryName}/repository/branches/${branchName}`
|
||||||
|
);
|
||||||
|
await saveRepository();
|
||||||
|
loading.branches = false;
|
||||||
|
return;
|
||||||
|
} catch (error) {
|
||||||
|
errorNotification(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let branches: any[] = [];
|
||||||
|
let page = 1;
|
||||||
|
let branchCount = 0;
|
||||||
|
const loadedBranches = await loadBranchesByPage(
|
||||||
|
apiUrl,
|
||||||
|
ownerName,
|
||||||
|
repositoryName,
|
||||||
|
page,
|
||||||
|
type
|
||||||
|
);
|
||||||
|
branches = branches.concat(loadedBranches);
|
||||||
|
branchCount = branches.length;
|
||||||
|
if (branchCount === 100) {
|
||||||
|
while (branchCount === 100) {
|
||||||
|
page = page + 1;
|
||||||
|
const nextBranches = await loadBranchesByPage(
|
||||||
|
apiUrl,
|
||||||
|
ownerName,
|
||||||
|
repositoryName,
|
||||||
|
page,
|
||||||
|
type
|
||||||
|
);
|
||||||
|
branches = branches.concat(nextBranches);
|
||||||
|
branchCount = nextBranches.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loading.branches = false;
|
||||||
|
branchSelectOptions = branches.map((branch: any) => ({
|
||||||
|
value: branch.name,
|
||||||
|
label: branch.name
|
||||||
|
}));
|
||||||
|
} catch (error) {
|
||||||
|
return errorNotification(error);
|
||||||
|
} finally {
|
||||||
|
loading.branches = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function loadBranchesByPage(
|
||||||
|
apiUrl: string,
|
||||||
|
owner: string,
|
||||||
|
repository: string,
|
||||||
|
page = 1,
|
||||||
|
type: string
|
||||||
|
) {
|
||||||
|
if (type === 'github') {
|
||||||
|
return await get(`${apiUrl}/repos/${owner}/${repository}/branches?per_page=100&page=${page}`);
|
||||||
|
}
|
||||||
|
if (type === 'gitlab') {
|
||||||
|
return await get(
|
||||||
|
`${apiUrl}/projects/${ownerName}%2F${repositoryName}/repository/branches?page=${page}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function saveRepository(event?: any) {
|
||||||
|
try {
|
||||||
|
if (event?.detail?.value) {
|
||||||
|
branchName = event.detail.value;
|
||||||
|
}
|
||||||
|
await post(`/applications/${id}/configuration/source`, {
|
||||||
|
gitSourceId: null,
|
||||||
|
forPublic: true,
|
||||||
|
type
|
||||||
|
});
|
||||||
|
await post(`/applications/${id}/configuration/repository`, {
|
||||||
|
repository: `${ownerName}/${repositoryName}`,
|
||||||
|
branch: branchName,
|
||||||
|
projectId,
|
||||||
|
autodeploy: false,
|
||||||
|
webhookToken: null,
|
||||||
|
isPublicRepository: true
|
||||||
|
});
|
||||||
|
|
||||||
|
return await goto(`/applications/${id}/configuration/destination`);
|
||||||
|
} catch (error) {
|
||||||
|
return errorNotification(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="mx-auto max-w-5xl">
|
||||||
|
<div class="grid grid-flow-row gap-2 px-10">
|
||||||
|
<div class="flex">
|
||||||
|
<form class="flex" on:submit|preventDefault={loadBranches}>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<input
|
||||||
|
placeholder="eg: https://github.com/coollabsio/nodejs-example/tree/main"
|
||||||
|
class="text-xs"
|
||||||
|
bind:value={publicRepositoryLink}
|
||||||
|
/>
|
||||||
|
{#if branchSelectOptions.length > 0}
|
||||||
|
<div class="custom-select-wrapper">
|
||||||
|
<Select
|
||||||
|
placeholder={loading.branches
|
||||||
|
? $t('application.configuration.loading_branches')
|
||||||
|
: !publicRepositoryLink
|
||||||
|
? $t('application.configuration.select_a_repository_first')
|
||||||
|
: $t('application.configuration.select_a_branch')}
|
||||||
|
isWaiting={loading.branches}
|
||||||
|
showIndicator={!!publicRepositoryLink && !loading.branches}
|
||||||
|
id="branches"
|
||||||
|
on:select={saveRepository}
|
||||||
|
items={branchSelectOptions}
|
||||||
|
isDisabled={loading.branches || !!!publicRepositoryLink}
|
||||||
|
isClearable={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="btn mx-4 bg-orange-600" class:loading={loading.branches} type="submit"
|
||||||
|
>Load Repository</button
|
||||||
|
>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Explainer
|
||||||
|
text="Examples:<br><br>https://github.com/coollabsio/nodejs-example<br>https://github.com/coollabsio/nodejs-example/tree/main<br>https://gitlab.com/aleveha/fastify-example<br>https://gitlab.com/aleveha/fastify-example/-/tree/master<br><br>Only works with Github.com and Gitlab.com."
|
||||||
|
/>
|
||||||
|
</div>
|
@ -47,6 +47,7 @@
|
|||||||
export let branch: any;
|
export let branch: any;
|
||||||
export let type: any;
|
export let type: any;
|
||||||
export let application: any;
|
export let application: any;
|
||||||
|
export let isPublicRepository: boolean;
|
||||||
|
|
||||||
function checkPackageJSONContents({ key, json }: { key: any; json: any }) {
|
function checkPackageJSONContents({ key, json }: { key: any; json: any }) {
|
||||||
return json?.dependencies?.hasOwnProperty(key) || json?.devDependencies?.hasOwnProperty(key);
|
return json?.dependencies?.hasOwnProperty(key) || json?.devDependencies?.hasOwnProperty(key);
|
||||||
@ -236,7 +237,7 @@
|
|||||||
if (error.message === 'Bad credentials') {
|
if (error.message === 'Bad credentials') {
|
||||||
const { token } = await get(`/applications/${id}/configuration/githubToken`);
|
const { token } = await get(`/applications/${id}/configuration/githubToken`);
|
||||||
$appSession.tokens.github = token;
|
$appSession.tokens.github = token;
|
||||||
return await scanRepository()
|
return await scanRepository();
|
||||||
}
|
}
|
||||||
return errorNotification(error);
|
return errorNotification(error);
|
||||||
} finally {
|
} finally {
|
||||||
@ -245,7 +246,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
await scanRepository();
|
if (!isPublicRepository) {
|
||||||
|
await scanRepository();
|
||||||
|
} else {
|
||||||
|
scanning = false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -262,27 +267,25 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
|
<div class="max-w-5xl mx-auto ">
|
||||||
|
<div class="title pb-2">Coolify</div>
|
||||||
<div class="max-w-7xl mx-auto ">
|
|
||||||
<div class="title pb-2">Coolify Buildpacks</div>
|
|
||||||
<div class="flex flex-wrap justify-center">
|
<div class="flex flex-wrap justify-center">
|
||||||
{#each buildPacks.filter(bp => bp.isCoolifyBuildPack === true) as buildPack}
|
{#each buildPacks.filter((bp) => bp.isCoolifyBuildPack === true) as buildPack}
|
||||||
<div class="p-2">
|
<div class="p-2">
|
||||||
<BuildPack {packageManager} {buildPack} {scanning} bind:foundConfig />
|
<BuildPack {packageManager} {buildPack} {scanning} bind:foundConfig />
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="max-w-7xl mx-auto ">
|
<div class="max-w-5xl mx-auto ">
|
||||||
<div class="title pb-2">Heroku</div>
|
<div class="title pb-2">Other</div>
|
||||||
<div class="flex flex-wrap justify-center">
|
<div class="flex flex-wrap justify-center">
|
||||||
{#each buildPacks.filter(bp => bp.isHerokuBuildPack === true) as buildPack}
|
{#each buildPacks.filter((bp) => bp.isHerokuBuildPack === true) as buildPack}
|
||||||
<div class="p-2">
|
<div class="p-2">
|
||||||
<BuildPack {packageManager} {buildPack} {scanning} bind:foundConfig />
|
<BuildPack {packageManager} {buildPack} {scanning} bind:foundConfig />
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -48,3 +48,4 @@
|
|||||||
<GitlabRepositories {application} {appId} {settings} />
|
<GitlabRepositories {application} {appId} {settings} />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -31,6 +31,8 @@
|
|||||||
import { t } from '$lib/translations';
|
import { t } from '$lib/translations';
|
||||||
import { errorNotification } from '$lib/common';
|
import { errorNotification } from '$lib/common';
|
||||||
import { appSession } from '$lib/store';
|
import { appSession } from '$lib/store';
|
||||||
|
import PublicRepository from './_PublicRepository.svelte';
|
||||||
|
import Explainer from '$lib/components/Explainer.svelte';
|
||||||
|
|
||||||
const { id } = $page.params;
|
const { id } = $page.params;
|
||||||
const from = $page.url.searchParams.get('from');
|
const from = $page.url.searchParams.get('from');
|
||||||
@ -71,120 +73,126 @@
|
|||||||
{$t('application.configuration.select_a_git_source')}
|
{$t('application.configuration.select_a_git_source')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col justify-center">
|
<div class="max-w-5xl mx-auto ">
|
||||||
{#if !filteredSources || ownSources.length === 0}
|
<div class="title pb-8">Git App</div>
|
||||||
<div class="flex-col">
|
<div class="flex flex-wrap justify-center">
|
||||||
<div class="pb-2 text-center font-bold">
|
{#if !filteredSources || ownSources.length === 0}
|
||||||
{$t('application.configuration.no_configurable_git')}
|
<div class="flex-col">
|
||||||
</div>
|
<div class="pb-2 text-center font-bold">
|
||||||
<div class="flex justify-center">
|
{$t('application.configuration.no_configurable_git')}
|
||||||
<a
|
</div>
|
||||||
href="/sources/new?from={$page.url.pathname}"
|
<div class="flex justify-center">
|
||||||
class="add-icon bg-orange-600 hover:bg-orange-500"
|
<a
|
||||||
>
|
href="/sources/new?from={$page.url.pathname}"
|
||||||
<svg
|
class="add-icon bg-orange-600 hover:bg-orange-500"
|
||||||
class="w-6"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor"
|
|
||||||
><path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
|
|
||||||
/></svg
|
|
||||||
>
|
>
|
||||||
</a>
|
<svg
|
||||||
|
class="w-6"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
><path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
|
||||||
|
/></svg
|
||||||
|
>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{:else}
|
||||||
{:else}
|
<div class="flex flex-col flex-wrap justify-center px-2 md:flex-row ">
|
||||||
<div class="flex flex-col flex-wrap justify-center px-2 md:flex-row ">
|
{#each ownSources as source}
|
||||||
{#each ownSources as source}
|
<div class="p-2 relative">
|
||||||
<div class="p-2 relative">
|
<div class="absolute -m-4">
|
||||||
<div class="absolute -m-4">
|
{#if source?.type === 'gitlab'}
|
||||||
{#if source?.type === 'gitlab'}
|
<svg viewBox="0 0 128 128" class="w-8">
|
||||||
<svg viewBox="0 0 128 128" class="w-8">
|
<path
|
||||||
<path
|
fill="#FC6D26"
|
||||||
fill="#FC6D26"
|
d="M126.615 72.31l-7.034-21.647L105.64 7.76c-.716-2.206-3.84-2.206-4.556 0l-13.94 42.903H40.856L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664 1.385 72.31a4.792 4.792 0 001.74 5.358L64 121.894l60.874-44.227a4.793 4.793 0 001.74-5.357"
|
||||||
d="M126.615 72.31l-7.034-21.647L105.64 7.76c-.716-2.206-3.84-2.206-4.556 0l-13.94 42.903H40.856L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664 1.385 72.31a4.792 4.792 0 001.74 5.358L64 121.894l60.874-44.227a4.793 4.793 0 001.74-5.357"
|
/><path fill="#E24329" d="M64 121.894l23.144-71.23H40.856L64 121.893z" /><path
|
||||||
/><path fill="#E24329" d="M64 121.894l23.144-71.23H40.856L64 121.893z" /><path
|
fill="#FC6D26"
|
||||||
fill="#FC6D26"
|
d="M64 121.894l-23.144-71.23H8.42L64 121.893z"
|
||||||
d="M64 121.894l-23.144-71.23H8.42L64 121.893z"
|
|
||||||
/><path
|
|
||||||
fill="#FCA326"
|
|
||||||
d="M8.42 50.663L1.384 72.31a4.79 4.79 0 001.74 5.357L64 121.894 8.42 50.664z"
|
|
||||||
/><path
|
|
||||||
fill="#E24329"
|
|
||||||
d="M8.42 50.663h32.436L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664z"
|
|
||||||
/><path fill="#FC6D26" d="M64 121.894l23.144-71.23h32.437L64 121.893z" /><path
|
|
||||||
fill="#FCA326"
|
|
||||||
d="M119.58 50.663l7.035 21.647a4.79 4.79 0 01-1.74 5.357L64 121.894l55.58-71.23z"
|
|
||||||
/><path
|
|
||||||
fill="#E24329"
|
|
||||||
d="M119.58 50.663H87.145l13.94-42.902c.717-2.206 3.84-2.206 4.557 0l13.94 42.903z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
{:else if source?.type === 'github'}
|
|
||||||
<svg viewBox="0 0 128 128" class="w-8">
|
|
||||||
<g fill="#ffffff"
|
|
||||||
><path
|
|
||||||
fill-rule="evenodd"
|
|
||||||
clip-rule="evenodd"
|
|
||||||
d="M64 5.103c-33.347 0-60.388 27.035-60.388 60.388 0 26.682 17.303 49.317 41.297 57.303 3.017.56 4.125-1.31 4.125-2.905 0-1.44-.056-6.197-.082-11.243-16.8 3.653-20.345-7.125-20.345-7.125-2.747-6.98-6.705-8.836-6.705-8.836-5.48-3.748.413-3.67.413-3.67 6.063.425 9.257 6.223 9.257 6.223 5.386 9.23 14.127 6.562 17.573 5.02.542-3.903 2.107-6.568 3.834-8.076-13.413-1.525-27.514-6.704-27.514-29.843 0-6.593 2.36-11.98 6.223-16.21-.628-1.52-2.695-7.662.584-15.98 0 0 5.07-1.623 16.61 6.19C53.7 35 58.867 34.327 64 34.304c5.13.023 10.3.694 15.127 2.033 11.526-7.813 16.59-6.19 16.59-6.19 3.287 8.317 1.22 14.46.593 15.98 3.872 4.23 6.215 9.617 6.215 16.21 0 23.194-14.127 28.3-27.574 29.796 2.167 1.874 4.097 5.55 4.097 11.183 0 8.08-.07 14.583-.07 16.572 0 1.607 1.088 3.49 4.148 2.897 23.98-7.994 41.263-30.622 41.263-57.294C124.388 32.14 97.35 5.104 64 5.104z"
|
|
||||||
/><path
|
/><path
|
||||||
d="M26.484 91.806c-.133.3-.605.39-1.035.185-.44-.196-.685-.605-.543-.906.13-.31.603-.395 1.04-.188.44.197.69.61.537.91zm2.446 2.729c-.287.267-.85.143-1.232-.28-.396-.42-.47-.983-.177-1.254.298-.266.844-.14 1.24.28.394.426.472.984.17 1.255zM31.312 98.012c-.37.258-.976.017-1.35-.52-.37-.538-.37-1.183.01-1.44.373-.258.97-.025 1.35.507.368.545.368 1.19-.01 1.452zm3.261 3.361c-.33.365-1.036.267-1.552-.23-.527-.487-.674-1.18-.343-1.544.336-.366 1.045-.264 1.564.23.527.486.686 1.18.333 1.543zm4.5 1.951c-.147.473-.825.688-1.51.486-.683-.207-1.13-.76-.99-1.238.14-.477.823-.7 1.512-.485.683.206 1.13.756.988 1.237zm4.943.361c.017.498-.563.91-1.28.92-.723.017-1.308-.387-1.315-.877 0-.503.568-.91 1.29-.924.717-.013 1.306.387 1.306.88zm4.598-.782c.086.485-.413.984-1.126 1.117-.7.13-1.35-.172-1.44-.653-.086-.498.422-.997 1.122-1.126.714-.123 1.354.17 1.444.663zm0 0"
|
fill="#FCA326"
|
||||||
/></g
|
d="M8.42 50.663L1.384 72.31a4.79 4.79 0 001.74 5.357L64 121.894 8.42 50.664z"
|
||||||
>
|
/><path
|
||||||
</svg>
|
fill="#E24329"
|
||||||
{/if}
|
d="M8.42 50.663h32.436L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664z"
|
||||||
|
/><path fill="#FC6D26" d="M64 121.894l23.144-71.23h32.437L64 121.893z" /><path
|
||||||
|
fill="#FCA326"
|
||||||
|
d="M119.58 50.663l7.035 21.647a4.79 4.79 0 01-1.74 5.357L64 121.894l55.58-71.23z"
|
||||||
|
/><path
|
||||||
|
fill="#E24329"
|
||||||
|
d="M119.58 50.663H87.145l13.94-42.902c.717-2.206 3.84-2.206 4.557 0l13.94 42.903z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
{:else if source?.type === 'github'}
|
||||||
|
<svg viewBox="0 0 128 128" class="w-8">
|
||||||
|
<g fill="#ffffff"
|
||||||
|
><path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M64 5.103c-33.347 0-60.388 27.035-60.388 60.388 0 26.682 17.303 49.317 41.297 57.303 3.017.56 4.125-1.31 4.125-2.905 0-1.44-.056-6.197-.082-11.243-16.8 3.653-20.345-7.125-20.345-7.125-2.747-6.98-6.705-8.836-6.705-8.836-5.48-3.748.413-3.67.413-3.67 6.063.425 9.257 6.223 9.257 6.223 5.386 9.23 14.127 6.562 17.573 5.02.542-3.903 2.107-6.568 3.834-8.076-13.413-1.525-27.514-6.704-27.514-29.843 0-6.593 2.36-11.98 6.223-16.21-.628-1.52-2.695-7.662.584-15.98 0 0 5.07-1.623 16.61 6.19C53.7 35 58.867 34.327 64 34.304c5.13.023 10.3.694 15.127 2.033 11.526-7.813 16.59-6.19 16.59-6.19 3.287 8.317 1.22 14.46.593 15.98 3.872 4.23 6.215 9.617 6.215 16.21 0 23.194-14.127 28.3-27.574 29.796 2.167 1.874 4.097 5.55 4.097 11.183 0 8.08-.07 14.583-.07 16.572 0 1.607 1.088 3.49 4.148 2.897 23.98-7.994 41.263-30.622 41.263-57.294C124.388 32.14 97.35 5.104 64 5.104z"
|
||||||
|
/><path
|
||||||
|
d="M26.484 91.806c-.133.3-.605.39-1.035.185-.44-.196-.685-.605-.543-.906.13-.31.603-.395 1.04-.188.44.197.69.61.537.91zm2.446 2.729c-.287.267-.85.143-1.232-.28-.396-.42-.47-.983-.177-1.254.298-.266.844-.14 1.24.28.394.426.472.984.17 1.255zM31.312 98.012c-.37.258-.976.017-1.35-.52-.37-.538-.37-1.183.01-1.44.373-.258.97-.025 1.35.507.368.545.368 1.19-.01 1.452zm3.261 3.361c-.33.365-1.036.267-1.552-.23-.527-.487-.674-1.18-.343-1.544.336-.366 1.045-.264 1.564.23.527.486.686 1.18.333 1.543zm4.5 1.951c-.147.473-.825.688-1.51.486-.683-.207-1.13-.76-.99-1.238.14-.477.823-.7 1.512-.485.683.206 1.13.756.988 1.237zm4.943.361c.017.498-.563.91-1.28.92-.723.017-1.308-.387-1.315-.877 0-.503.568-.91 1.29-.924.717-.013 1.306.387 1.306.88zm4.598-.782c.086.485-.413.984-1.126 1.117-.7.13-1.35-.172-1.44-.653-.086-.498.422-.997 1.122-1.126.714-.123 1.354.17 1.444.663zm0 0"
|
||||||
|
/></g
|
||||||
|
>
|
||||||
|
</svg>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<form on:submit|preventDefault={() => handleSubmit(source.id)}>
|
||||||
|
<button
|
||||||
|
disabled={source.gitlabApp && !source.gitlabAppId}
|
||||||
|
type="submit"
|
||||||
|
class="disabled:opacity-95 bg-coolgray-200 disabled:text-white box-selection hover:bg-orange-700 group"
|
||||||
|
class:border-red-500={source.gitlabApp && !source.gitlabAppId}
|
||||||
|
class:border-0={source.gitlabApp && !source.gitlabAppId}
|
||||||
|
class:border-l-4={source.gitlabApp && !source.gitlabAppId}
|
||||||
|
>
|
||||||
|
<div class="font-bold text-xl text-center truncate">{source.name}</div>
|
||||||
|
{#if source.gitlabApp && !source.gitlabAppId}
|
||||||
|
<div class="font-bold text-center truncate text-red-500 group-hover:text-white">
|
||||||
|
Configuration missing
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<form on:submit|preventDefault={() => handleSubmit(source.id)}>
|
{/each}
|
||||||
<button
|
</div>
|
||||||
disabled={source.gitlabApp && !source.gitlabAppId}
|
|
||||||
type="submit"
|
|
||||||
class="disabled:opacity-95 bg-coolgray-200 disabled:text-white box-selection hover:bg-orange-700 group"
|
|
||||||
class:border-red-500={source.gitlabApp && !source.gitlabAppId}
|
|
||||||
class:border-0={source.gitlabApp && !source.gitlabAppId}
|
|
||||||
class:border-l-4={source.gitlabApp && !source.gitlabAppId}
|
|
||||||
>
|
|
||||||
<div class="font-bold text-xl text-center truncate">{source.name}</div>
|
|
||||||
{#if source.gitlabApp && !source.gitlabAppId}
|
|
||||||
<div class="font-bold text-center truncate text-red-500 group-hover:text-white">
|
|
||||||
Configuration missing
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if otherSources.length > 0 && $appSession.teamId === '0'}
|
{#if otherSources.length > 0 && $appSession.teamId === '0'}
|
||||||
<div class="px-6 pb-5 pt-10 text-xl font-bold">Other Sources</div>
|
<div class="px-6 pb-5 pt-10 text-xl font-bold">Other Sources</div>
|
||||||
|
{/if}
|
||||||
|
<div class="flex flex-col flex-wrap justify-center px-2 md:flex-row">
|
||||||
|
{#each otherSources as source}
|
||||||
|
<div class="p-2">
|
||||||
|
<form on:submit|preventDefault={() => handleSubmit(source.id)}>
|
||||||
|
<button
|
||||||
|
disabled={source.gitlabApp && !source.gitlabAppId}
|
||||||
|
type="submit"
|
||||||
|
class="disabled:opacity-95 bg-coolgray-200 disabled:text-white box-selection hover:bg-orange-700 group"
|
||||||
|
class:border-red-500={source.gitlabApp && !source.gitlabAppId}
|
||||||
|
class:border-0={source.gitlabApp && !source.gitlabAppId}
|
||||||
|
class:border-l-4={source.gitlabApp && !source.gitlabAppId}
|
||||||
|
>
|
||||||
|
<div class="font-bold text-xl text-center truncate">{source.name}</div>
|
||||||
|
{#if source.gitlabApp && !source.gitlabAppId}
|
||||||
|
<div class="font-bold text-center truncate text-red-500 group-hover:text-white">
|
||||||
|
{$t('application.configuration.configuration_missing')}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="flex flex-col flex-wrap justify-center px-2 md:flex-row">
|
</div>
|
||||||
{#each otherSources as source}
|
<div class="title py-4">Public Repository</div>
|
||||||
<div class="p-2">
|
|
||||||
<form on:submit|preventDefault={() => handleSubmit(source.id)}>
|
<PublicRepository />
|
||||||
<button
|
|
||||||
disabled={source.gitlabApp && !source.gitlabAppId}
|
|
||||||
type="submit"
|
|
||||||
class="disabled:opacity-95 bg-coolgray-200 disabled:text-white box-selection hover:bg-orange-700 group"
|
|
||||||
class:border-red-500={source.gitlabApp && !source.gitlabAppId}
|
|
||||||
class:border-0={source.gitlabApp && !source.gitlabAppId}
|
|
||||||
class:border-l-4={source.gitlabApp && !source.gitlabAppId}
|
|
||||||
>
|
|
||||||
<div class="font-bold text-xl text-center truncate">{source.name}</div>
|
|
||||||
{#if source.gitlabApp && !source.gitlabAppId}
|
|
||||||
<div class="font-bold text-center truncate text-red-500 group-hover:text-white">
|
|
||||||
{$t('application.configuration.configuration_missing')}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -133,6 +133,7 @@
|
|||||||
autodeploy = !autodeploy;
|
autodeploy = !autodeploy;
|
||||||
}
|
}
|
||||||
if (name === 'isBot') {
|
if (name === 'isBot') {
|
||||||
|
if ($status.application.isRunning) return;
|
||||||
isBot = !isBot;
|
isBot = !isBot;
|
||||||
application.settings.isBot = isBot;
|
application.settings.isBot = isBot;
|
||||||
setLocation(application, settings);
|
setLocation(application, settings);
|
||||||
@ -345,8 +346,11 @@
|
|||||||
<label for="gitSource" class="text-base font-bold text-stone-100"
|
<label for="gitSource" class="text-base font-bold text-stone-100"
|
||||||
>{$t('application.git_source')}</label
|
>{$t('application.git_source')}</label
|
||||||
>
|
>
|
||||||
{#if isDisabled}
|
{#if isDisabled || application.settings.isPublicRepository}
|
||||||
<input disabled={isDisabled} value={application.gitSource.name} />
|
<input
|
||||||
|
disabled={isDisabled || application.settings.isPublicRepository}
|
||||||
|
value={application.gitSource.name}
|
||||||
|
/>
|
||||||
{:else}
|
{:else}
|
||||||
<a
|
<a
|
||||||
href={`/applications/${id}/configuration/source?from=/applications/${id}`}
|
href={`/applications/${id}/configuration/source?from=/applications/${id}`}
|
||||||
@ -363,8 +367,11 @@
|
|||||||
<label for="repository" class="text-base font-bold text-stone-100"
|
<label for="repository" class="text-base font-bold text-stone-100"
|
||||||
>{$t('application.git_repository')}</label
|
>{$t('application.git_repository')}</label
|
||||||
>
|
>
|
||||||
{#if isDisabled}
|
{#if isDisabled || application.settings.isPublicRepository}
|
||||||
<input disabled={isDisabled} value="{application.repository}/{application.branch}" />
|
<input
|
||||||
|
disabled={isDisabled || application.settings.isPublicRepository}
|
||||||
|
value="{application.repository}/{application.branch}"
|
||||||
|
/>
|
||||||
{:else}
|
{:else}
|
||||||
<a
|
<a
|
||||||
href={`/applications/${id}/configuration/repository?from=/applications/${id}&to=/applications/${id}/configuration/buildpack`}
|
href={`/applications/${id}/configuration/repository?from=/applications/${id}&to=/applications/${id}/configuration/buildpack`}
|
||||||
@ -488,6 +495,7 @@
|
|||||||
on:click={() => changeSettings('isBot')}
|
on:click={() => changeSettings('isBot')}
|
||||||
title="Is your application a bot?"
|
title="Is your application a bot?"
|
||||||
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>"
|
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>"
|
||||||
|
disabled={$status.application.isRunning}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{#if !isBot}
|
{#if !isBot}
|
||||||
@ -612,7 +620,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
{#if !staticDeployments.includes(application.buildPack) && !isBot}
|
{#if !staticDeployments.includes(application.buildPack)}
|
||||||
<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
|
||||||
@ -623,6 +631,9 @@
|
|||||||
bind:value={application.port}
|
bind:value={application.port}
|
||||||
placeholder="{$t('forms.default')}: 'python' ? '8000' : '3000'"
|
placeholder="{$t('forms.default')}: 'python' ? '8000' : '3000'"
|
||||||
/>
|
/>
|
||||||
|
<Explainer
|
||||||
|
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">
|
||||||
@ -770,15 +781,17 @@
|
|||||||
<div class="title">{$t('application.features')}</div>
|
<div class="title">{$t('application.features')}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="px-10 pb-10">
|
<div class="px-10 pb-10">
|
||||||
<div class="grid grid-cols-2 items-center">
|
{#if !application.settings.isPublicRepository}
|
||||||
<Setting
|
<div class="grid grid-cols-2 items-center">
|
||||||
isCenter={false}
|
<Setting
|
||||||
bind:setting={autodeploy}
|
isCenter={false}
|
||||||
on:click={() => changeSettings('autodeploy')}
|
bind:setting={autodeploy}
|
||||||
title={$t('application.enable_automatic_deployment')}
|
on:click={() => changeSettings('autodeploy')}
|
||||||
description={$t('application.enable_auto_deploy_webhooks')}
|
title={$t('application.enable_automatic_deployment')}
|
||||||
/>
|
description={$t('application.enable_auto_deploy_webhooks')}
|
||||||
</div>
|
/>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
{#if !application.settings.isBot}
|
{#if !application.settings.isBot}
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center">
|
||||||
<Setting
|
<Setting
|
||||||
|
@ -109,7 +109,7 @@
|
|||||||
<div class="flex justify-end sticky top-0 p-2 mx-1">
|
<div class="flex justify-end sticky top-0 p-2 mx-1">
|
||||||
<button
|
<button
|
||||||
on:click={followBuild}
|
on:click={followBuild}
|
||||||
class="bg-transparent btn btn-sm tooltip tooltip-primary tooltip-bottom hover:text-green-500 hover:bg-coolgray-500"
|
class="bg-transparent btn btn-sm btn-link tooltip tooltip-primary tooltip-bottom hover:text-green-500 hover:bg-coolgray-500"
|
||||||
data-tip="Follow logs"
|
data-tip="Follow logs"
|
||||||
class:text-green-500={followingBuild}
|
class:text-green-500={followingBuild}
|
||||||
>
|
>
|
||||||
|
@ -147,7 +147,7 @@
|
|||||||
<div class="flex justify-end sticky top-0 p-1 mx-1">
|
<div class="flex justify-end sticky top-0 p-1 mx-1">
|
||||||
<button
|
<button
|
||||||
on:click={followBuild}
|
on:click={followBuild}
|
||||||
class="bg-transparent btn btn-sm tooltip tooltip-primary tooltip-bottom"
|
class="bg-transparent btn btn-sm btn-link tooltip tooltip-primary tooltip-bottom"
|
||||||
data-tip="Follow logs"
|
data-tip="Follow logs"
|
||||||
class:text-green-500={followingLogs}
|
class:text-green-500={followingLogs}
|
||||||
>
|
>
|
||||||
|
@ -87,9 +87,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mx-auto max-w-6xl rounded-xl px-6 pt-4">
|
<div class="mx-auto max-w-6xl rounded-xl px-6 pt-4">
|
||||||
<div class="flex justify-center py-4 text-center">
|
|
||||||
<Explainer customClass="w-full" text={$t('application.storage.persistent_storage_explainer')} />
|
|
||||||
</div>
|
|
||||||
<table class="mx-auto border-separate text-left">
|
<table class="mx-auto border-separate text-left">
|
||||||
<thead>
|
<thead>
|
||||||
<tr class="h-12">
|
<tr class="h-12">
|
||||||
@ -109,4 +107,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<div class="flex justify-center py-4 text-center">
|
||||||
|
<Explainer customClass="w-full" text={$t('application.storage.persistent_storage_explainer')} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -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.5.2",
|
"version": "3.6.0",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"repository": "github:coollabsio/coolify",
|
"repository": "github:coollabsio/coolify",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user