v1.0.11 (#41)
Features: - Build packs for popular frontend frameworks. It will help to understand which build packs should be chosen. Fixes: - Github queries optimized. - Save repositories to store (faster navigation). - Remove unnecessary data on dashboard requests. - Speed up static site builds with a lot. UI: - Redesign of the application deployment page. - Redesign of database deployments page.
This commit is contained in:
parent
b416e3ab3e
commit
cccb9a5fec
123
README.md
123
README.md
@ -1,93 +1,64 @@
|
||||
# About
|
||||
|
||||
https://andrasbacsai.com/farewell-netlify-and-heroku-after-3-days-of-coding
|
||||
# Coolify
|
||||
|
||||
# Features
|
||||
- Deploy your Node.js, static sites, PHP or any custom application (with custom Dockerfile) just by pushing code to git.
|
||||
- Hassle-free installation and upgrade process.
|
||||
- One-click MongoDB, MySQL, PostgreSQL, CouchDB deployments!
|
||||
An open-source, hassle-free, self-hostable Heroku & Netlify alternative.
|
||||
|
||||
# Upcoming features
|
||||
- Backups & monitoring.
|
||||
- User analytics with privacy in mind.
|
||||
- And much more (see [Roadmap](https://github.com/coollabsio/coolify/projects/1)).
|
||||
## Demo
|
||||
|
||||
[Small video](https://cdn.coollabs.io/assets/coolify/video/coolify.webm)
|
||||
|
||||
# FAQ
|
||||
Q: What is a buildpack?
|
||||
|
||||
A: It defines your application's final form.
|
||||
`Static` means that it will be hosted as a static site.
|
||||
`NodeJs` means that it will be started as a node application.
|
||||
|
||||
# Screenshots
|
||||
|
||||
[Login](https://coolify.io/login.jpg)
|
||||
|
||||
[Applications](https://coolify.io/applications.jpg)
|
||||
|
||||
[Databases](https://coolify.io/databases.jpg)
|
||||
|
||||
[Configuration](https://coolify.io/configuration.jpg)
|
||||
|
||||
[Settings](https://coolify.io/settings.jpg)
|
||||
|
||||
[Logs](https://coolify.io/logs.jpg)
|
||||
|
||||
# Getting Started
|
||||
|
||||
Automatically: `/bin/bash -c "$(curl -fsSL https://get.coollabs.io/coolify/install.sh)"`
|
||||
|
||||
Manually:
|
||||
### Requirements before installation
|
||||
- [Docker](https://docs.docker.com/engine/install/) version 20+
|
||||
- Docker in [swarm mode enabled](https://docs.docker.com/engine/reference/commandline/swarm_init/) (should be set manually before installation)
|
||||
- A [MongoDB](https://docs.mongodb.com/manual/installation/) instance.
|
||||
- We have a [simple installation](https://github.com/coollabsio/infrastructure/tree/main/mongo) if you need one
|
||||
- A configured DNS entry (see `.env.template`)
|
||||
- [Github App](https://docs.github.com/en/developers/apps/creating-a-github-app)
|
||||
|
||||
- GitHub App name: could be anything weird
|
||||
- Homepage URL: https://yourdomain
|
||||
|
||||
Identifying and authorizing users:
|
||||
- Callback URL: https://yourdomain/api/v1/login/github/app
|
||||
- Request user authorization (OAuth) during installation -> Check!
|
||||
|
||||
Webhook:
|
||||
- Active -> Check!
|
||||
- Webhook URL: https://yourdomain/api/v1/webhooks/deploy
|
||||
- Webhook Secret: it should be super secret
|
||||
|
||||
Repository permissions:
|
||||
- Contents: Read-only
|
||||
- Metadata: Read-only
|
||||
|
||||
User permissions:
|
||||
- Email: Read-only
|
||||
## Installation
|
||||
|
||||
Subscribe to events:
|
||||
- Push -> Check!
|
||||
Installation is automated with the following command:
|
||||
|
||||
### Installation
|
||||
- Clone this repository: `git clone git@github.com:coollabsio/coolify.git`
|
||||
- Set `.env` (see `.env.template`)
|
||||
- Installation: `bash install.sh all`
|
||||
```bash
|
||||
/bin/bash -c "$(curl -fsSL https://get.coollabs.io/coolify/install.sh)"
|
||||
```
|
||||
|
||||
## Manual updating process (You probably never need to do this!)
|
||||
### Update everything (proxy+coolify)
|
||||
- `bash install.sh all`
|
||||
|
||||
## Features
|
||||
You can deploy any of the following applications, databases and services easily.
|
||||
|
||||
### Update coolify only
|
||||
- `bash install.sh coolify`
|
||||
(constantly growing lists)
|
||||
|
||||
### Update proxy only
|
||||
- `bash install.sh proxy`
|
||||
### Applications
|
||||
With Github integration
|
||||
|
||||
- Static sites
|
||||
- NodeJS
|
||||
- VueJS
|
||||
- NuxtJS
|
||||
- React/Preact
|
||||
- NextJS
|
||||
- Gatsby
|
||||
- Svelte
|
||||
- PHP
|
||||
- Rust
|
||||
- or any custom dockerfile
|
||||
|
||||
### Databases
|
||||
- MongoDB
|
||||
- MySQL
|
||||
- PostgreSQL
|
||||
- CouchDB
|
||||
|
||||
### Services
|
||||
- [Plausible Analytics](https://plausible.io)
|
||||
|
||||
|
||||
## Support
|
||||
|
||||
# Contact
|
||||
- Twitter: [@andrasbacsai](https://twitter.com/andrasbacsai)
|
||||
- Telegram: [@andrasbacsai](https://t.me/andrasbacsai)
|
||||
- Email: [andras@coollabs.io](mailto:andras@coollabs.io)
|
||||
- Discord: [Invitation](https://discord.com/invite/bvS3WhR)
|
||||
|
||||
## Roadmap
|
||||
[See the Roadmap here](https://github.com/coollabsio/coolify/projects/1)
|
||||
|
||||
## License
|
||||
|
||||
# License
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Please see the [LICENSE](/LICENSE) file in our repository for the full text.
|
||||
|
||||
|
25
api/buildPacks/gatsby/index.js
Normal file
25
api/buildPacks/gatsby/index.js
Normal file
@ -0,0 +1,25 @@
|
||||
const fs = require('fs').promises
|
||||
const { buildImage } = require('../helpers')
|
||||
const { streamEvents, docker } = require('../../libs/docker')
|
||||
|
||||
// 'HEALTHCHECK --timeout=10s --start-period=10s --interval=5s CMD curl -I -s -f http://localhost/ || exit 1',
|
||||
const publishStaticDocker = (configuration) => {
|
||||
return [
|
||||
'FROM nginx:stable-alpine',
|
||||
'COPY nginx.conf /etc/nginx/nginx.conf',
|
||||
'WORKDIR /usr/share/nginx/html',
|
||||
`COPY --from=${configuration.build.container.name}:${configuration.build.container.tag}-cache /usr/src/app/${configuration.publish.directory} ./`,
|
||||
'EXPOSE 80',
|
||||
'CMD ["nginx", "-g", "daemon off;"]'
|
||||
].join('\n')
|
||||
}
|
||||
|
||||
module.exports = async function (configuration) {
|
||||
await buildImage(configuration, true)
|
||||
await fs.writeFile(`${configuration.general.workdir}/Dockerfile`, publishStaticDocker(configuration))
|
||||
const stream = await docker.engine.buildImage(
|
||||
{ src: ['.'], context: configuration.general.workdir },
|
||||
{ t: `${configuration.build.container.name}:${configuration.build.container.tag}` }
|
||||
)
|
||||
await streamEvents(stream, configuration)
|
||||
}
|
@ -10,11 +10,11 @@ const buildImageNodeDocker = (configuration) => {
|
||||
`RUN ${configuration.build.command.build}`
|
||||
].join('\n')
|
||||
}
|
||||
async function buildImage (configuration) {
|
||||
async function buildImage (configuration, cacheBuild) {
|
||||
await fs.writeFile(`${configuration.general.workdir}/Dockerfile`, buildImageNodeDocker(configuration))
|
||||
const stream = await docker.engine.buildImage(
|
||||
{ src: ['.'], context: configuration.general.workdir },
|
||||
{ t: `${configuration.build.container.name}:${configuration.build.container.tag}` }
|
||||
{ t: `${configuration.build.container.name}:${cacheBuild ? `${configuration.build.container.tag}-cache` : configuration.build.container.tag}` }
|
||||
)
|
||||
await streamEvents(stream, configuration)
|
||||
}
|
||||
|
@ -1,7 +1,13 @@
|
||||
const static = require('./static')
|
||||
const Static = require('./static')
|
||||
const react = require('./react')
|
||||
const nextjs = require('./nextjs')
|
||||
const nuxtjs = require('./nuxtjs')
|
||||
const gatsby = require('./gatsby')
|
||||
const vuejs = require('./vuejs')
|
||||
const svelte = require('./svelte')
|
||||
const nodejs = require('./nodejs')
|
||||
const php = require('./php')
|
||||
const custom = require('./custom')
|
||||
const docker = require('./docker')
|
||||
const rust = require('./rust')
|
||||
|
||||
module.exports = { static, nodejs, php, custom, rust }
|
||||
module.exports = { static: Static, nodejs, php, docker, rust, react, vuejs, nextjs, nuxtjs, svelte, gatsby }
|
||||
|
28
api/buildPacks/nextjs/index.js
Normal file
28
api/buildPacks/nextjs/index.js
Normal file
@ -0,0 +1,28 @@
|
||||
const fs = require('fs').promises
|
||||
const { buildImage } = require('../helpers')
|
||||
const { streamEvents, docker } = require('../../libs/docker')
|
||||
// `HEALTHCHECK --timeout=10s --start-period=10s --interval=5s CMD curl -I -s -f http://localhost:${configuration.publish.port}${configuration.publish.path} || exit 1`,
|
||||
const publishNodejsDocker = (configuration) => {
|
||||
return [
|
||||
'FROM node:lts',
|
||||
'WORKDIR /usr/src/app',
|
||||
configuration.build.command.build
|
||||
? `COPY --from=${configuration.build.container.name}:${configuration.build.container.tag} /usr/src/app/${configuration.publish.directory} ./`
|
||||
: `
|
||||
COPY ${configuration.build.directory}/package*.json ./
|
||||
RUN ${configuration.build.command.installation}
|
||||
COPY ./${configuration.build.directory} ./`,
|
||||
`EXPOSE ${configuration.publish.port}`,
|
||||
'CMD [ "yarn", "start" ]'
|
||||
].join('\n')
|
||||
}
|
||||
|
||||
module.exports = async function (configuration) {
|
||||
await buildImage(configuration)
|
||||
await fs.writeFile(`${configuration.general.workdir}/Dockerfile`, publishNodejsDocker(configuration))
|
||||
const stream = await docker.engine.buildImage(
|
||||
{ src: ['.'], context: configuration.general.workdir },
|
||||
{ t: `${configuration.build.container.name}:${configuration.build.container.tag}` }
|
||||
)
|
||||
await streamEvents(stream, configuration)
|
||||
}
|
28
api/buildPacks/nuxtjs/index.js
Normal file
28
api/buildPacks/nuxtjs/index.js
Normal file
@ -0,0 +1,28 @@
|
||||
const fs = require('fs').promises
|
||||
const { buildImage } = require('../helpers')
|
||||
const { streamEvents, docker } = require('../../libs/docker')
|
||||
// `HEALTHCHECK --timeout=10s --start-period=10s --interval=5s CMD curl -I -s -f http://localhost:${configuration.publish.port}${configuration.publish.path} || exit 1`,
|
||||
const publishNodejsDocker = (configuration) => {
|
||||
return [
|
||||
'FROM node:lts',
|
||||
'WORKDIR /usr/src/app',
|
||||
configuration.build.command.build
|
||||
? `COPY --from=${configuration.build.container.name}:${configuration.build.container.tag} /usr/src/app/${configuration.publish.directory} ./`
|
||||
: `
|
||||
COPY ${configuration.build.directory}/package*.json ./
|
||||
RUN ${configuration.build.command.installation}
|
||||
COPY ./${configuration.build.directory} ./`,
|
||||
`EXPOSE ${configuration.publish.port}`,
|
||||
'CMD [ "yarn", "start" ]'
|
||||
].join('\n')
|
||||
}
|
||||
|
||||
module.exports = async function (configuration) {
|
||||
await buildImage(configuration)
|
||||
await fs.writeFile(`${configuration.general.workdir}/Dockerfile`, publishNodejsDocker(configuration))
|
||||
const stream = await docker.engine.buildImage(
|
||||
{ src: ['.'], context: configuration.general.workdir },
|
||||
{ t: `${configuration.build.container.name}:${configuration.build.container.tag}` }
|
||||
)
|
||||
await streamEvents(stream, configuration)
|
||||
}
|
25
api/buildPacks/react/index.js
Normal file
25
api/buildPacks/react/index.js
Normal file
@ -0,0 +1,25 @@
|
||||
const fs = require('fs').promises
|
||||
const { buildImage } = require('../helpers')
|
||||
const { streamEvents, docker } = require('../../libs/docker')
|
||||
|
||||
// 'HEALTHCHECK --timeout=10s --start-period=10s --interval=5s CMD curl -I -s -f http://localhost/ || exit 1',
|
||||
const publishStaticDocker = (configuration) => {
|
||||
return [
|
||||
'FROM nginx:stable-alpine',
|
||||
'COPY nginx.conf /etc/nginx/nginx.conf',
|
||||
'WORKDIR /usr/share/nginx/html',
|
||||
`COPY --from=${configuration.build.container.name}:${configuration.build.container.tag}-cache /usr/src/app/${configuration.publish.directory} ./`,
|
||||
'EXPOSE 80',
|
||||
'CMD ["nginx", "-g", "daemon off;"]'
|
||||
].join('\n')
|
||||
}
|
||||
|
||||
module.exports = async function (configuration) {
|
||||
await buildImage(configuration, true)
|
||||
await fs.writeFile(`${configuration.general.workdir}/Dockerfile`, publishStaticDocker(configuration))
|
||||
const stream = await docker.engine.buildImage(
|
||||
{ src: ['.'], context: configuration.general.workdir },
|
||||
{ t: `${configuration.build.container.name}:${configuration.build.container.tag}` }
|
||||
)
|
||||
await streamEvents(stream, configuration)
|
||||
}
|
@ -9,7 +9,7 @@ const publishStaticDocker = (configuration) => {
|
||||
'COPY nginx.conf /etc/nginx/nginx.conf',
|
||||
'WORKDIR /usr/share/nginx/html',
|
||||
configuration.build.command.build
|
||||
? `COPY --from=${configuration.build.container.name}:${configuration.build.container.tag} /usr/src/app/${configuration.publish.directory} ./`
|
||||
? `COPY --from=${configuration.build.container.name}:${configuration.build.container.tag}-cache /usr/src/app/${configuration.publish.directory} ./`
|
||||
: `COPY ./${configuration.build.directory} ./`,
|
||||
'EXPOSE 80',
|
||||
'CMD ["nginx", "-g", "daemon off;"]'
|
||||
@ -17,9 +17,8 @@ const publishStaticDocker = (configuration) => {
|
||||
}
|
||||
|
||||
module.exports = async function (configuration) {
|
||||
if (configuration.build.command.build) await buildImage(configuration)
|
||||
if (configuration.build.command.build) await buildImage(configuration, true)
|
||||
await fs.writeFile(`${configuration.general.workdir}/Dockerfile`, publishStaticDocker(configuration))
|
||||
|
||||
const stream = await docker.engine.buildImage(
|
||||
{ src: ['.'], context: configuration.general.workdir },
|
||||
{ t: `${configuration.build.container.name}:${configuration.build.container.tag}` }
|
||||
|
25
api/buildPacks/svelte/index.js
Normal file
25
api/buildPacks/svelte/index.js
Normal file
@ -0,0 +1,25 @@
|
||||
const fs = require('fs').promises
|
||||
const { buildImage } = require('../helpers')
|
||||
const { streamEvents, docker } = require('../../libs/docker')
|
||||
|
||||
// 'HEALTHCHECK --timeout=10s --start-period=10s --interval=5s CMD curl -I -s -f http://localhost/ || exit 1',
|
||||
const publishStaticDocker = (configuration) => {
|
||||
return [
|
||||
'FROM nginx:stable-alpine',
|
||||
'COPY nginx.conf /etc/nginx/nginx.conf',
|
||||
'WORKDIR /usr/share/nginx/html',
|
||||
`COPY --from=${configuration.build.container.name}:${configuration.build.container.tag}-cache /usr/src/app/${configuration.publish.directory} ./`,
|
||||
'EXPOSE 80',
|
||||
'CMD ["nginx", "-g", "daemon off;"]'
|
||||
].join('\n')
|
||||
}
|
||||
|
||||
module.exports = async function (configuration) {
|
||||
await buildImage(configuration, true)
|
||||
await fs.writeFile(`${configuration.general.workdir}/Dockerfile`, publishStaticDocker(configuration))
|
||||
const stream = await docker.engine.buildImage(
|
||||
{ src: ['.'], context: configuration.general.workdir },
|
||||
{ t: `${configuration.build.container.name}:${configuration.build.container.tag}` }
|
||||
)
|
||||
await streamEvents(stream, configuration)
|
||||
}
|
25
api/buildPacks/vuejs/index.js
Normal file
25
api/buildPacks/vuejs/index.js
Normal file
@ -0,0 +1,25 @@
|
||||
const fs = require('fs').promises
|
||||
const { buildImage } = require('../helpers')
|
||||
const { streamEvents, docker } = require('../../libs/docker')
|
||||
|
||||
// 'HEALTHCHECK --timeout=10s --start-period=10s --interval=5s CMD curl -I -s -f http://localhost/ || exit 1',
|
||||
const publishStaticDocker = (configuration) => {
|
||||
return [
|
||||
'FROM nginx:stable-alpine',
|
||||
'COPY nginx.conf /etc/nginx/nginx.conf',
|
||||
'WORKDIR /usr/share/nginx/html',
|
||||
`COPY --from=${configuration.build.container.name}:${configuration.build.container.tag}-cache /usr/src/app/${configuration.publish.directory} ./`,
|
||||
'EXPOSE 80',
|
||||
'CMD ["nginx", "-g", "daemon off;"]'
|
||||
].join('\n')
|
||||
}
|
||||
|
||||
module.exports = async function (configuration) {
|
||||
await buildImage(configuration, true)
|
||||
await fs.writeFile(`${configuration.general.workdir}/Dockerfile`, publishStaticDocker(configuration))
|
||||
const stream = await docker.engine.buildImage(
|
||||
{ src: ['.'], context: configuration.general.workdir },
|
||||
{ t: `${configuration.build.container.name}:${configuration.build.container.tag}` }
|
||||
)
|
||||
await streamEvents(stream, configuration)
|
||||
}
|
@ -2,11 +2,16 @@ const { docker } = require('../../docker')
|
||||
const { execShellAsync } = require('../../common')
|
||||
const Deployment = require('../../../models/Deployment')
|
||||
|
||||
async function purgeImagesContainers (configuration) {
|
||||
async function purgeImagesContainers (configuration, deleteAll = false) {
|
||||
const { name, tag } = configuration.build.container
|
||||
await execShellAsync('docker container prune -f')
|
||||
const IDsToDelete = (await execShellAsync(`docker images ls --filter=reference='${name}' --filter=before='${name}:${tag}' --format '{{json .ID }}'`)).trim().replace(/"/g, '').split('\n')
|
||||
if (IDsToDelete.length !== 0) for (const id of IDsToDelete) await execShellAsync(`docker rmi -f ${id}`)
|
||||
if (deleteAll) {
|
||||
const IDsToDelete = (await execShellAsync(`docker images ls --filter=reference='${name}' --format '{{json .ID }}'`)).trim().replace(/"/g, '').split('\n')
|
||||
if (IDsToDelete.length > 0) await execShellAsync(`docker rmi -f ${IDsToDelete.toString().replace(',', ' ')}`)
|
||||
} else {
|
||||
const IDsToDelete = (await execShellAsync(`docker images ls --filter=reference='${name}' --filter=before='${name}:${tag}' --format '{{json .ID }}'`)).trim().replace(/"/g, '').split('\n')
|
||||
if (IDsToDelete.length > 1) await execShellAsync(`docker rmi -f ${IDsToDelete.toString().replace(',', ' ')}`)
|
||||
}
|
||||
await execShellAsync('docker image prune -f')
|
||||
}
|
||||
|
||||
|
@ -23,14 +23,10 @@ function setDefaultConfiguration (configuration) {
|
||||
|
||||
if (!configuration.publish.path) configuration.publish.path = '/'
|
||||
if (!configuration.publish.port) {
|
||||
if (configuration.build.pack === 'php') {
|
||||
configuration.publish.port = 80
|
||||
} else if (configuration.build.pack === 'static') {
|
||||
configuration.publish.port = 80
|
||||
} else if (configuration.build.pack === 'nodejs') {
|
||||
configuration.publish.port = 3000
|
||||
} else if (configuration.build.pack === 'rust') {
|
||||
if (configuration.build.pack === 'nodejs' && configuration.build.pack === 'vuejs' && configuration.build.pack === 'nuxtjs' && configuration.build.pack === 'rust' && configuration.build.pack === 'nextjs') {
|
||||
configuration.publish.port = 3000
|
||||
} else {
|
||||
configuration.publish.port = 80
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
const fs = require('fs').promises
|
||||
module.exports = async function (configuration) {
|
||||
const staticDeployments = ['react', 'vuejs', 'static', 'svelte', 'gatsby']
|
||||
try {
|
||||
// TODO: Write full .dockerignore for all deployments!!
|
||||
if (configuration.build.pack === 'php') {
|
||||
@ -12,7 +13,7 @@ module.exports = async function (configuration) {
|
||||
`)
|
||||
}
|
||||
// await fs.writeFile(`${configuration.general.workdir}/.dockerignore`, 'node_modules')
|
||||
if (configuration.build.pack === 'static') {
|
||||
if (staticDeployments.includes(configuration.build.pack)) {
|
||||
await fs.writeFile(
|
||||
`${configuration.general.workdir}/nginx.conf`,
|
||||
`user nginx;
|
||||
|
@ -1,38 +1,44 @@
|
||||
const jwt = require('jsonwebtoken')
|
||||
const axios = require('axios')
|
||||
const { execShellAsync, cleanupTmp } = require('../../common')
|
||||
const { execShellAsync } = require('../../common')
|
||||
|
||||
module.exports = async function (configuration) {
|
||||
const { workdir } = configuration.general
|
||||
const { organization, name, branch } = configuration.repository
|
||||
const github = configuration.github
|
||||
|
||||
const githubPrivateKey = process.env.GITHUB_APP_PRIVATE_KEY.replace(/\\n/g, '\n').replace(/"/g, '')
|
||||
|
||||
const payload = {
|
||||
iat: Math.round(new Date().getTime() / 1000),
|
||||
exp: Math.round(new Date().getTime() / 1000 + 60),
|
||||
iss: parseInt(github.app.id)
|
||||
}
|
||||
|
||||
const jwtToken = jwt.sign(payload, githubPrivateKey, {
|
||||
algorithm: 'RS256'
|
||||
})
|
||||
const accessToken = await axios({
|
||||
method: 'POST',
|
||||
url: `https://api.github.com/app/installations/${github.installation.id}/access_tokens`,
|
||||
data: {},
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + jwtToken,
|
||||
Accept: 'application/vnd.github.machine-man-preview+json'
|
||||
try {
|
||||
const { workdir } = configuration.general
|
||||
const { organization, name, branch } = configuration.repository
|
||||
const github = configuration.github
|
||||
if (!github.installation.id || !github.app.id) {
|
||||
throw new Error('Github installation ID is invalid.')
|
||||
}
|
||||
})
|
||||
await execShellAsync(
|
||||
`mkdir -p ${workdir} && git clone -q -b ${branch} https://x-access-token:${accessToken.data.token}@github.com/${organization}/${name}.git ${workdir}/`
|
||||
)
|
||||
configuration.build.container.tag = (
|
||||
await execShellAsync(`cd ${configuration.general.workdir}/ && git rev-parse HEAD`)
|
||||
)
|
||||
.replace('\n', '')
|
||||
.slice(0, 7)
|
||||
const githubPrivateKey = process.env.GITHUB_APP_PRIVATE_KEY.replace(/\\n/g, '\n').replace(/"/g, '')
|
||||
|
||||
const payload = {
|
||||
iat: Math.round(new Date().getTime() / 1000),
|
||||
exp: Math.round(new Date().getTime() / 1000 + 60),
|
||||
iss: parseInt(github.app.id)
|
||||
}
|
||||
|
||||
const jwtToken = jwt.sign(payload, githubPrivateKey, {
|
||||
algorithm: 'RS256'
|
||||
})
|
||||
const accessToken = await axios({
|
||||
method: 'POST',
|
||||
url: `https://api.github.com/app/installations/${github.installation.id}/access_tokens`,
|
||||
data: {},
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + jwtToken,
|
||||
Accept: 'application/vnd.github.machine-man-preview+json'
|
||||
}
|
||||
})
|
||||
await execShellAsync(
|
||||
`mkdir -p ${workdir} && git clone -q -b ${branch} https://x-access-token:${accessToken.data.token}@github.com/${organization}/${name}.git ${workdir}/`
|
||||
)
|
||||
configuration.build.container.tag = (
|
||||
await execShellAsync(`cd ${configuration.general.workdir}/ && git rev-parse HEAD`)
|
||||
)
|
||||
.replace('\n', '')
|
||||
.slice(0, 7)
|
||||
} catch (error) {
|
||||
throw new Error(error)
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
const { docker } = require('../../../libs/docker')
|
||||
const { execShellAsync } = require('../../../libs/common')
|
||||
const { execShellAsync, delay } = require('../../../libs/common')
|
||||
const ApplicationLog = require('../../../models/Logs/Application')
|
||||
const Deployment = require('../../../models/Deployment')
|
||||
const { purgeImagesContainers } = require('../../../libs/applications/cleanup')
|
||||
|
||||
module.exports = async function (fastify) {
|
||||
fastify.post('/', async (request, reply) => {
|
||||
@ -25,6 +26,8 @@ module.exports = async function (fastify) {
|
||||
}
|
||||
await execShellAsync(`docker stack rm ${found.build.container.name}`)
|
||||
reply.code(200).send({ organization, name, branch })
|
||||
await delay(10000)
|
||||
await purgeImagesContainers(found, true)
|
||||
} else {
|
||||
reply.code(500).send({ message: 'Nothing to do.' })
|
||||
}
|
||||
|
@ -1,27 +1,10 @@
|
||||
const { docker } = require('../../../libs/docker')
|
||||
const Deployment = require('../../../models/Deployment')
|
||||
const ServerLog = require('../../../models/Logs/Server')
|
||||
const { saveServerLog } = require('../../../libs/logging')
|
||||
|
||||
module.exports = async function (fastify) {
|
||||
fastify.get('/', async (request, reply) => {
|
||||
try {
|
||||
const latestDeployments = await Deployment.aggregate([
|
||||
{
|
||||
$sort: { createdAt: -1 }
|
||||
},
|
||||
{
|
||||
$group:
|
||||
{
|
||||
_id: {
|
||||
repoId: '$repoId',
|
||||
branch: '$branch'
|
||||
},
|
||||
createdAt: { $last: '$createdAt' },
|
||||
progress: { $first: '$progress' }
|
||||
}
|
||||
}
|
||||
])
|
||||
const serverLogs = await ServerLog.find()
|
||||
const dockerServices = await docker.engine.listServices()
|
||||
let applications = dockerServices.filter(r => r.Spec.Labels.managedBy === 'coolify' && r.Spec.Labels.type === 'application' && r.Spec.Labels.configuration)
|
||||
@ -29,25 +12,31 @@ module.exports = async function (fastify) {
|
||||
let services = dockerServices.filter(r => r.Spec.Labels.managedBy === 'coolify' && r.Spec.Labels.type === 'service' && r.Spec.Labels.configuration)
|
||||
applications = applications.map(r => {
|
||||
if (JSON.parse(r.Spec.Labels.configuration)) {
|
||||
const configuration = JSON.parse(r.Spec.Labels.configuration)
|
||||
const status = latestDeployments.find(l => configuration.repository.id === l._id.repoId && configuration.repository.branch === l._id.branch)
|
||||
if (status && status.progress) r.progress = status.progress
|
||||
r.Spec.Labels.configuration = configuration
|
||||
return r
|
||||
return {
|
||||
configuration: JSON.parse(r.Spec.Labels.configuration),
|
||||
UpdatedAt: r.UpdatedAt
|
||||
}
|
||||
}
|
||||
return {}
|
||||
})
|
||||
databases = databases.map(r => {
|
||||
const configuration = r.Spec.Labels.configuration ? JSON.parse(r.Spec.Labels.configuration) : null
|
||||
r.Spec.Labels.configuration = configuration
|
||||
return r
|
||||
if (JSON.parse(r.Spec.Labels.configuration)) {
|
||||
return {
|
||||
configuration: JSON.parse(r.Spec.Labels.configuration)
|
||||
}
|
||||
}
|
||||
return {}
|
||||
})
|
||||
services = services.map(r => {
|
||||
const configuration = r.Spec.Labels.configuration ? JSON.parse(r.Spec.Labels.configuration) : null
|
||||
r.Spec.Labels.configuration = configuration
|
||||
return r
|
||||
if (JSON.parse(r.Spec.Labels.configuration)) {
|
||||
return {
|
||||
serviceName: r.Spec.Labels.serviceName,
|
||||
configuration: JSON.parse(r.Spec.Labels.configuration)
|
||||
}
|
||||
}
|
||||
return {}
|
||||
})
|
||||
applications = [...new Map(applications.map(item => [item.Spec.Labels.configuration.publish.domain + item.Spec.Labels.configuration.publish.path, item])).values()]
|
||||
applications = [...new Map(applications.map(item => [item.configuration.publish.domain + item.configuration.publish.path, item])).values()]
|
||||
return {
|
||||
serverLogs,
|
||||
applications: {
|
||||
|
@ -66,7 +66,6 @@ module.exports = async function (fastify) {
|
||||
if (!defaultDatabaseName) defaultDatabaseName = nickname
|
||||
|
||||
reply.code(201).send({ message: 'Deploying.' })
|
||||
// TODO: Persistent volume, custom inputs
|
||||
const deployId = cuid()
|
||||
const configuration = {
|
||||
general: {
|
||||
|
@ -12,7 +12,6 @@ const cloneRepository = require('../../../libs/applications/github/cloneReposito
|
||||
const { purgeImagesContainers } = require('../../../libs/applications/cleanup')
|
||||
|
||||
module.exports = async function (fastify) {
|
||||
// TODO: Add this to fastify plugin
|
||||
const postSchema = {
|
||||
body: {
|
||||
type: 'object',
|
||||
|
@ -1,12 +1,11 @@
|
||||
require('dotenv').config()
|
||||
const fs = require('fs')
|
||||
const util = require('util')
|
||||
const axios = require('axios')
|
||||
const mongoose = require('mongoose')
|
||||
const path = require('path')
|
||||
const { saveServerLog } = require('./libs/logging')
|
||||
const { execShellAsync } = require('./libs/common')
|
||||
const { purgeImagesContainers, cleanupStuckedDeploymentsInDB } = require('./libs/applications/cleanup')
|
||||
const { cleanupStuckedDeploymentsInDB } = require('./libs/applications/cleanup')
|
||||
const fastify = require('fastify')({
|
||||
trustProxy: true,
|
||||
logger: {
|
||||
|
@ -6,7 +6,7 @@
|
||||
<link rel="icon" href="/favicon.png" />
|
||||
<link rel="preload" as="image" href="/favicon.png">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>coolify: Heroku & Netlify alternative</title>
|
||||
<title>Coolify</title>
|
||||
<link rel="dns-prefetch" href="https://cdn.coollabs.io/" />
|
||||
<link rel="preconnect" href="https://cdn.coollabs.io/" crossorigin="" />
|
||||
<link rel="stylesheet" href="https://cdn.coollabs.io/fonts/montserrat/montserrat.css" />
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "coolify",
|
||||
"description": "An open-source, hassle-free, self-hostable Heroku & Netlify alternative.",
|
||||
"version": "1.0.10",
|
||||
"version": "1.0.11",
|
||||
"license": "AGPL-3.0",
|
||||
"scripts": {
|
||||
"lint": "standard",
|
||||
|
@ -1,24 +0,0 @@
|
||||
<script>
|
||||
import { application } from "@store";
|
||||
import Tooltip from "../../../Tooltip/TooltipInfo.svelte";
|
||||
</script>
|
||||
|
||||
<div class="grid grid-cols-1 max-w-2xl md:mx-auto mx-6 text-center">
|
||||
<label for="installCommand"
|
||||
>Install Command <Tooltip label="Command to run for installing dependencies. eg: yarn install." />
|
||||
</label>
|
||||
|
||||
<input
|
||||
class="mb-6"
|
||||
id="installCommand"
|
||||
bind:value="{$application.build.command.installation}"
|
||||
placeholder="eg: yarn install"
|
||||
/>
|
||||
<label for="buildCommand">Build Command <Tooltip label="Command to run for building your application. If empty, no build phase initiated in the deploy process." /></label>
|
||||
<input
|
||||
class="mb-6"
|
||||
id="buildCommand"
|
||||
bind:value="{$application.build.command.build}"
|
||||
placeholder="eg: yarn build"
|
||||
/>
|
||||
</div>
|
@ -1,56 +1,213 @@
|
||||
<style lang="postcss">
|
||||
.buildpack {
|
||||
@apply px-6 py-2 mx-2 my-2 bg-warmGray-800 w-48 ease-in-out transform hover:scale-105 text-center rounded border-2 border-transparent border-dashed cursor-pointer transition duration-100;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { application} from "@store";
|
||||
import { application } from "@store";
|
||||
import { onMount } from "svelte";
|
||||
import TooltipInfo from "../../../Tooltip/TooltipInfo.svelte";
|
||||
const showPorts = ['nodejs','custom','rust']
|
||||
let domainInput;
|
||||
const buildpacks = {
|
||||
static: {
|
||||
port: {
|
||||
active: false,
|
||||
number: 80,
|
||||
},
|
||||
build: true,
|
||||
},
|
||||
nodejs: {
|
||||
port: {
|
||||
active: true,
|
||||
number: 3000,
|
||||
},
|
||||
build: true,
|
||||
},
|
||||
vuejs: {
|
||||
port: {
|
||||
active: false,
|
||||
number: 80,
|
||||
},
|
||||
build: true,
|
||||
},
|
||||
nuxtjs: {
|
||||
port: {
|
||||
active: true,
|
||||
number: 3000,
|
||||
},
|
||||
build: true,
|
||||
},
|
||||
react: {
|
||||
port: {
|
||||
active: false,
|
||||
number: 80,
|
||||
},
|
||||
build: true,
|
||||
},
|
||||
nextjs: {
|
||||
port: {
|
||||
active: true,
|
||||
number: 3000,
|
||||
},
|
||||
build: true,
|
||||
},
|
||||
gatsby: {
|
||||
port: {
|
||||
active: true,
|
||||
number: 3000,
|
||||
},
|
||||
build: true,
|
||||
},
|
||||
svelte: {
|
||||
port: {
|
||||
active: false,
|
||||
number: 80,
|
||||
},
|
||||
build: true,
|
||||
},
|
||||
php: {
|
||||
port: {
|
||||
active: false,
|
||||
number: 80,
|
||||
},
|
||||
build: false,
|
||||
},
|
||||
rust: {
|
||||
port: {
|
||||
active: true,
|
||||
number: 3000,
|
||||
},
|
||||
build: false,
|
||||
},
|
||||
docker: {
|
||||
port: {
|
||||
active: true,
|
||||
number: 3000,
|
||||
},
|
||||
build: false,
|
||||
},
|
||||
};
|
||||
function selectBuildPack(event) {
|
||||
if (event.target.innerText === "React/Preact") {
|
||||
$application.build.pack = "react";
|
||||
} else {
|
||||
$application.build.pack = event.target.innerText
|
||||
.replace(/\./g, "")
|
||||
.toLowerCase();
|
||||
}
|
||||
}
|
||||
onMount(()=> {
|
||||
domainInput.focus();
|
||||
})
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div
|
||||
class="grid grid-cols-1 text-sm max-w-2xl md:mx-auto mx-6 pb-6 auto-cols-max "
|
||||
class="grid grid-cols-1 text-sm max-w-4xl md:mx-auto mx-6 pb-16 auto-cols-max "
|
||||
>
|
||||
<label for="buildPack"
|
||||
>Build Pack
|
||||
{#if $application.build.pack === 'custom'}
|
||||
<TooltipInfo
|
||||
label="Your custom Dockerfile will be used from the root directory (or from 'Base Directory' specified below) of your repository. "
|
||||
/>
|
||||
{:else if $application.build.pack === 'static'}
|
||||
<TooltipInfo
|
||||
label="Published as a static site (for build phase see 'Build Step' tab)."
|
||||
/>
|
||||
{:else if $application.build.pack === 'nodejs'}
|
||||
<TooltipInfo
|
||||
label="Published as a Node.js application (for build phase see 'Build Step' tab)."
|
||||
/>
|
||||
{:else if $application.build.pack === 'php'}
|
||||
<TooltipInfo
|
||||
size="large"
|
||||
label="Published as a PHP application."
|
||||
/>
|
||||
{:else if $application.build.pack === 'rust'}
|
||||
<TooltipInfo
|
||||
size="large"
|
||||
label="Published as a Rust application."
|
||||
/>
|
||||
{/if}
|
||||
|
||||
</label
|
||||
>
|
||||
<select id="buildPack" bind:value="{$application.build.pack}">
|
||||
<option selected class="font-bold">static</option>
|
||||
<option class="font-bold">nodejs</option>
|
||||
<option class="font-bold">php</option>
|
||||
<option class="font-bold">custom</option>
|
||||
<option class="font-bold">rust</option>
|
||||
</select>
|
||||
<div class="text-2xl font-bold border-gradient w-40">Build Packs</div>
|
||||
<div class="flex font-bold flex-wrap justify-center pt-10">
|
||||
<div
|
||||
class="{$application.build.pack === 'static'
|
||||
? 'buildpack bg-red-500'
|
||||
: 'buildpack hover:border-red-500'}"
|
||||
on:click="{selectBuildPack}"
|
||||
>
|
||||
Static
|
||||
</div>
|
||||
<div
|
||||
class="{$application.build.pack === 'nodejs'
|
||||
? 'buildpack bg-emerald-600'
|
||||
: 'buildpack hover:border-emerald-600'}"
|
||||
on:click="{selectBuildPack}"
|
||||
>
|
||||
NodeJS
|
||||
</div>
|
||||
<div
|
||||
class="{$application.build.pack === 'vuejs'
|
||||
? 'buildpack bg-green-500'
|
||||
: 'buildpack hover:border-green-500'}"
|
||||
on:click="{selectBuildPack}"
|
||||
>
|
||||
VueJS
|
||||
</div>
|
||||
<div
|
||||
class="{$application.build.pack === 'nuxtjs'
|
||||
? 'buildpack bg-green-500'
|
||||
: 'buildpack hover:border-green-500'}"
|
||||
on:click="{selectBuildPack}"
|
||||
>
|
||||
NuxtJS
|
||||
</div>
|
||||
<div
|
||||
class="{$application.build.pack === 'react'
|
||||
? 'buildpack bg-gradient-to-r from-blue-500 to-purple-500'
|
||||
: 'buildpack hover:border-blue-500'}"
|
||||
on:click="{selectBuildPack}"
|
||||
>
|
||||
React/Preact
|
||||
</div>
|
||||
<div
|
||||
class="{$application.build.pack === 'nextjs'
|
||||
? 'buildpack bg-blue-500'
|
||||
: 'buildpack hover:border-blue-500'}"
|
||||
on:click="{selectBuildPack}"
|
||||
>
|
||||
NextJS
|
||||
</div>
|
||||
<div
|
||||
class="{$application.build.pack === 'gatsby'
|
||||
? 'buildpack bg-blue-500'
|
||||
: 'buildpack hover:border-blue-500'}"
|
||||
on:click="{selectBuildPack}"
|
||||
>
|
||||
Gatsby
|
||||
</div>
|
||||
<div
|
||||
class="{$application.build.pack === 'svelte'
|
||||
? 'buildpack bg-orange-600'
|
||||
: 'buildpack hover:border-orange-600'}"
|
||||
on:click="{selectBuildPack}"
|
||||
>
|
||||
Svelte
|
||||
</div>
|
||||
<div
|
||||
class="{$application.build.pack === 'php'
|
||||
? 'buildpack bg-indigo-500'
|
||||
: 'buildpack hover:border-indigo-500'}"
|
||||
on:click="{selectBuildPack}"
|
||||
>
|
||||
PHP
|
||||
</div>
|
||||
<div
|
||||
class="{$application.build.pack === 'rust'
|
||||
? 'buildpack bg-pink-500'
|
||||
: 'buildpack hover:border-pink-500'}"
|
||||
on:click="{selectBuildPack}"
|
||||
>
|
||||
Rust
|
||||
</div>
|
||||
<div
|
||||
class="{$application.build.pack === 'docker'
|
||||
? 'buildpack bg-purple-500'
|
||||
: 'buildpack hover:border-purple-500'}"
|
||||
on:click="{selectBuildPack}"
|
||||
>
|
||||
Docker
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-2xl font-bold border-gradient w-52">General settings</div>
|
||||
<div
|
||||
class="grid grid-cols-1 max-w-2xl md:mx-auto mx-6 justify-center items-center"
|
||||
class="grid grid-cols-1 max-w-2xl md:mx-auto mx-6 justify-center items-center pt-10"
|
||||
>
|
||||
<div class="grid grid-flow-col gap-2 items-center pb-6">
|
||||
<div class="grid grid-flow-row">
|
||||
<label for="Domain" class="">Domain</label>
|
||||
<input
|
||||
bind:this={domainInput}
|
||||
class="border-2"
|
||||
class:placeholder-red-500="{$application.publish.domain == null ||
|
||||
$application.publish.domain == ''}"
|
||||
class:border-red-500="{$application.publish.domain == null ||
|
||||
@ -63,7 +220,9 @@
|
||||
<div class="grid grid-flow-row">
|
||||
<label for="Path"
|
||||
>Path <TooltipInfo
|
||||
label="{`Path to deploy your application on your domain. eg: /api means it will be deployed to -> https://${$application.publish.domain || '<yourdomain>'}/api`}"
|
||||
label="{`Path to deploy your application on your domain. eg: /api means it will be deployed to -> https://${
|
||||
$application.publish.domain || '<yourdomain>'
|
||||
}/api`}"
|
||||
/></label
|
||||
>
|
||||
<input
|
||||
@ -73,16 +232,27 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{#if showPorts.includes($application.build.pack)}
|
||||
<label for="Port" >Port</label>
|
||||
<label
|
||||
for="Port"
|
||||
class:text-warmGray-800="{!buildpacks[$application.build.pack].port
|
||||
.active}">Port</label
|
||||
>
|
||||
<input
|
||||
disabled="{!buildpacks[$application.build.pack].port.active}"
|
||||
id="Port"
|
||||
class="mb-6"
|
||||
class:bg-warmGray-900="{!buildpacks[$application.build.pack].port.active}"
|
||||
class:text-warmGray-900="{!buildpacks[$application.build.pack].port
|
||||
.active}"
|
||||
class:placeholder-warmGray-800="{!buildpacks[$application.build.pack].port
|
||||
.active}"
|
||||
class:hover:bg-warmGray-900="{!buildpacks[$application.build.pack].port
|
||||
.active}"
|
||||
class:cursor-not-allowed="{!buildpacks[$application.build.pack].port
|
||||
.active}"
|
||||
bind:value="{$application.publish.port}"
|
||||
placeholder="{$application.build.pack === 'static' ? '80' : '3000'}"
|
||||
placeholder="{buildpacks[$application.build.pack].port.number}"
|
||||
/>
|
||||
{/if}
|
||||
<div class="grid grid-flow-col gap-2 items-center pt-12">
|
||||
<div class="grid grid-flow-col gap-2 items-center pt-6 pb-12">
|
||||
<div class="grid grid-flow-row">
|
||||
<label for="baseDir"
|
||||
>Base Directory <TooltipInfo
|
||||
@ -109,4 +279,62 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="text-2xl font-bold w-40"
|
||||
class:border-gradient="{buildpacks[$application.build.pack].build}"
|
||||
class:text-warmGray-800="{!buildpacks[$application.build.pack].build}"
|
||||
>
|
||||
Commands
|
||||
</div>
|
||||
<div
|
||||
class=" max-w-2xl md:mx-auto mx-6 justify-center items-center pt-10 pb-32"
|
||||
>
|
||||
<div class="grid grid-flow-col gap-2 items-center">
|
||||
<div class="grid grid-flow-row">
|
||||
<label
|
||||
for="installCommand"
|
||||
class:text-warmGray-800="{!buildpacks[$application.build.pack].build}"
|
||||
>Install Command <TooltipInfo
|
||||
label="Command to run for installing dependencies. eg: yarn install."
|
||||
/>
|
||||
</label>
|
||||
|
||||
<input
|
||||
class="mb-6"
|
||||
class:bg-warmGray-900="{!buildpacks[$application.build.pack].build}"
|
||||
class:text-warmGray-900="{!buildpacks[$application.build.pack].build}"
|
||||
class:placeholder-warmGray-800="{!buildpacks[$application.build.pack]
|
||||
.build}"
|
||||
class:hover:bg-warmGray-900="{!buildpacks[$application.build.pack]
|
||||
.build}"
|
||||
class:cursor-not-allowed="{!buildpacks[$application.build.pack]
|
||||
.build}"
|
||||
id="installCommand"
|
||||
bind:value="{$application.build.command.installation}"
|
||||
placeholder="eg: yarn install"
|
||||
/>
|
||||
<label
|
||||
for="buildCommand"
|
||||
class:text-warmGray-800="{!buildpacks[$application.build.pack].build}"
|
||||
>Build Command <TooltipInfo
|
||||
label="Command to run for building your application. If empty, no build phase initiated in the deploy process."
|
||||
/></label
|
||||
>
|
||||
<input
|
||||
class="mb-6"
|
||||
class:bg-warmGray-900="{!buildpacks[$application.build.pack].build}"
|
||||
class:text-warmGray-900="{!buildpacks[$application.build.pack].build}"
|
||||
class:placeholder-warmGray-800="{!buildpacks[$application.build.pack]
|
||||
.build}"
|
||||
class:hover:bg-warmGray-900="{!buildpacks[$application.build.pack]
|
||||
.build}"
|
||||
class:cursor-not-allowed="{!buildpacks[$application.build.pack]
|
||||
.build}"
|
||||
id="buildCommand"
|
||||
bind:value="{$application.build.command.build}"
|
||||
placeholder="eg: yarn build"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -36,40 +36,43 @@
|
||||
];
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="max-w-2xl md:mx-auto mx-6 text-center">
|
||||
<div class="text-2xl font-bold border-gradient w-24">Secrets</div>
|
||||
<div class="max-w-xl mx-auto text-center pt-4">
|
||||
<div class="text-left text-base font-bold tracking-tight text-warmGray-400">
|
||||
New Secret
|
||||
</div>
|
||||
<div class="grid md:grid-flow-col grid-flow-row gap-2">
|
||||
<input id="secretName" bind:value="{secret.name}" placeholder="Name" />
|
||||
<input id="secretValue" bind:value="{secret.value}" placeholder="Value" />
|
||||
<button
|
||||
class="button p-1 w-20 bg-green-600 hover:bg-green-500 text-white"
|
||||
on:click="{saveSecret}">Save</button
|
||||
>
|
||||
<div class="flex space-x-4">
|
||||
<input id="secretName" bind:value="{secret.name}" placeholder="Name" class="w-64 border-2 border-transparent" />
|
||||
<input id="secretValue" bind:value="{secret.value}" placeholder="Value" class="w-64 border-2 border-transparent" />
|
||||
<button class="icon hover:text-green-500" on:click="{saveSecret}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{#if $application.publish.secrets.length > 0}
|
||||
<div class="py-4">
|
||||
{#each $application.publish.secrets as s}
|
||||
<div class="grid md:grid-flow-col grid-flow-row gap-2">
|
||||
<div class="flex space-x-4">
|
||||
<input
|
||||
id="{s.name}"
|
||||
value="{s.name}"
|
||||
disabled
|
||||
class="border-2 bg-transparent border-transparent"
|
||||
class="border-2 bg-transparent border-transparent w-64"
|
||||
class:border-red-600="{foundSecret && foundSecret.name === s.name}"
|
||||
/>
|
||||
<input
|
||||
id="{s.createdAt}"
|
||||
value="SAVED"
|
||||
disabled
|
||||
class="bg-transparent border-transparent"
|
||||
class="border-2 bg-transparent border-transparent w-64"
|
||||
/>
|
||||
<button
|
||||
class="button w-20 bg-red-600 hover:bg-red-500 text-white"
|
||||
on:click="{() => removeSecret(s.name)}">Delete</button
|
||||
>
|
||||
<button class="icon hover:text-red-500" on:click="{() => removeSecret(s.name)}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
@ -1,11 +1,10 @@
|
||||
<script>
|
||||
export let loading, branches;
|
||||
import { isActive } from "@roxi/routify";
|
||||
import { application } from "@store";
|
||||
import { application, activePage } from "@store";
|
||||
import Select from "svelte-select";
|
||||
|
||||
const selectedValue =
|
||||
!$isActive("/application/new") && $application.repository.branch
|
||||
$activePage.application !== "new" && $application.repository.branch;
|
||||
|
||||
function handleSelect(event) {
|
||||
$application.repository.branch = null;
|
||||
@ -36,10 +35,10 @@
|
||||
selectedValue="{selectedValue}"
|
||||
isClearable="{false}"
|
||||
items="{branches.map(b => ({ label: b.name, value: b.name }))}"
|
||||
showIndicator="{$isActive('/application/new')}"
|
||||
showIndicator="{$activePage.new}"
|
||||
noOptionsMessage="No branches found"
|
||||
placeholder="Select a branch"
|
||||
isDisabled="{!$isActive('/application/new')}"
|
||||
isDisabled="{!$activePage.new}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,7 +1,15 @@
|
||||
<script>
|
||||
import { redirect, isActive } from "@roxi/routify";
|
||||
import { fade } from "svelte/transition";
|
||||
import { session, application, fetch, initialApplication } from "@store";
|
||||
import {
|
||||
session,
|
||||
application,
|
||||
fetch,
|
||||
initialApplication,
|
||||
githubRepositories,
|
||||
githubInstallations,
|
||||
activePage,
|
||||
} from "@store";
|
||||
|
||||
import Login from "./Login.svelte";
|
||||
import Loading from "../../Loading.svelte";
|
||||
@ -15,8 +23,6 @@
|
||||
};
|
||||
|
||||
let branches = [];
|
||||
let repositories = [];
|
||||
|
||||
function dashify(str, options) {
|
||||
if (typeof str !== "string") return str;
|
||||
return str
|
||||
@ -29,8 +35,8 @@
|
||||
|
||||
async function loadBranches() {
|
||||
loading.branches = true;
|
||||
if ($isActive("/application/new")) $application.repository.branch = null;
|
||||
const selectedRepository = repositories.find(
|
||||
if ($activePage.new) $application.repository.branch = null;
|
||||
const selectedRepository = $githubRepositories.find(
|
||||
r => r.id === $application.repository.id,
|
||||
);
|
||||
|
||||
@ -54,6 +60,23 @@
|
||||
}
|
||||
|
||||
async function loadGithub() {
|
||||
if ($githubRepositories.length > 0) {
|
||||
$application.github.installation.id = $githubInstallations.id;
|
||||
$application.github.app.id = $githubInstallations.app_id;
|
||||
const foundRepositoryOnGithub = $githubRepositories.find(
|
||||
r =>
|
||||
r.full_name ===
|
||||
`${$application.repository.organization}/${$application.repository.name}`,
|
||||
);
|
||||
|
||||
if (foundRepositoryOnGithub) {
|
||||
$application.repository.id = foundRepositoryOnGithub.id;
|
||||
$application.repository.organization = foundRepositoryOnGithub.owner.login;
|
||||
$application.repository.name = foundRepositoryOnGithub.name;
|
||||
// await loadBranches();
|
||||
}
|
||||
return;
|
||||
}
|
||||
loading.github = true;
|
||||
try {
|
||||
const { installations } = await $fetch(
|
||||
@ -64,6 +87,7 @@
|
||||
}
|
||||
$application.github.installation.id = installations[0].id;
|
||||
$application.github.app.id = installations[0].app_id;
|
||||
$githubInstallations = installations[0];
|
||||
|
||||
let page = 1;
|
||||
let userRepos = 0;
|
||||
@ -72,21 +96,20 @@
|
||||
page,
|
||||
);
|
||||
|
||||
repositories = repositories.concat(data.repositories);
|
||||
$githubRepositories = $githubRepositories.concat(data.repositories);
|
||||
userRepos = data.total_count;
|
||||
|
||||
if (userRepos > repositories.length) {
|
||||
while (userRepos > repositories.length) {
|
||||
if (userRepos > $githubRepositories.length) {
|
||||
while (userRepos > $githubRepositories.length) {
|
||||
page = page + 1;
|
||||
const repos = await getGithubRepos(
|
||||
$application.github.installation.id,
|
||||
page,
|
||||
);
|
||||
repositories = repositories.concat(repos.repositories);
|
||||
$githubRepositories = $githubRepositories.concat(repos.repositories);
|
||||
}
|
||||
}
|
||||
|
||||
const foundRepositoryOnGithub = repositories.find(
|
||||
const foundRepositoryOnGithub = $githubRepositories.find(
|
||||
r =>
|
||||
r.full_name ===
|
||||
`${$application.repository.organization}/${$application.repository.name}`,
|
||||
@ -120,7 +143,7 @@
|
||||
if (newWindow.closed) {
|
||||
clearInterval(timer);
|
||||
loading.github = true;
|
||||
if (!$isActive("/application/new")) {
|
||||
if (!$activePage.new) {
|
||||
try {
|
||||
const config = await $fetch(`/api/v1/config`, {
|
||||
body: {
|
||||
@ -137,28 +160,46 @@
|
||||
$application = JSON.parse(JSON.stringify(initialApplication));
|
||||
}
|
||||
branches = [];
|
||||
repositories = [];
|
||||
$githubRepositories = [];
|
||||
await loadGithub();
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if !$isActive("/application/new")}
|
||||
{#if !$activePage.new}
|
||||
<div class="min-h-full text-white">
|
||||
<div
|
||||
class="py-5 text-left px-6 text-3xl tracking-tight font-bold flex items-center"
|
||||
>
|
||||
{$application.publish.domain
|
||||
? `${$application.publish.domain}${
|
||||
$application.publish.path !== "/" ? $application.publish.path : ""
|
||||
}`
|
||||
: "example.com"}
|
||||
<a
|
||||
target="_blank"
|
||||
class="text-green-500 hover:underline cursor-pointer px-2"
|
||||
class="icon mx-2"
|
||||
href="{'https://' +
|
||||
$application.publish.domain +
|
||||
$application.publish.path}"
|
||||
>{$application.publish.domain
|
||||
? `${$application.publish.domain}${$application.publish.path !== '/' ? $application.publish.path : ''}`
|
||||
: "<yourdomain>"}</a
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
|
||||
></path>
|
||||
</svg></a
|
||||
>
|
||||
|
||||
<a
|
||||
target="_blank"
|
||||
class="icon"
|
||||
@ -180,7 +221,7 @@
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
{:else if $isActive("/application/new")}
|
||||
{:else if $activePage.new}
|
||||
<div class="min-h-full text-white">
|
||||
<div
|
||||
class="py-5 text-left px-6 text-3xl tracking-tight font-bold flex items-center"
|
||||
@ -205,11 +246,10 @@
|
||||
in:fade="{{ duration: 100 }}"
|
||||
>
|
||||
<Repositories
|
||||
bind:repositories
|
||||
on:loadBranches="{loadBranches}"
|
||||
on:modifyGithubAppConfig="{modifyGithubAppConfig}"
|
||||
/>
|
||||
{#if $application.repository.organization !== "new"}
|
||||
{#if $application.repository.organization}
|
||||
<Branches loading="{loading.branches}" branches="{branches}" />
|
||||
{/if}
|
||||
|
||||
|
@ -1,43 +1,42 @@
|
||||
<script>
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { isActive } from "@roxi/routify";
|
||||
import { application } from "@store";
|
||||
import { application, githubRepositories, activePage } from "@store";
|
||||
import Select from "svelte-select";
|
||||
|
||||
function handleSelect(event) {
|
||||
$application.build.pack = 'static'
|
||||
$application.repository.id = parseInt(event.detail.value, 10);
|
||||
dispatch("loadBranches");
|
||||
}
|
||||
|
||||
export let repositories;
|
||||
let items = repositories.map(repo => ({
|
||||
let items = $githubRepositories.map(repo => ({
|
||||
label: `${repo.owner.login}/${repo.name}`,
|
||||
value: repo.id.toString(),
|
||||
}));
|
||||
|
||||
const selectedValue =
|
||||
!$isActive("/application/new") &&
|
||||
!$activePage.new &&
|
||||
`${$application.repository.organization}/${$application.repository.name}`;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
const modifyGithubAppConfig = () => dispatch("modifyGithubAppConfig");
|
||||
</script>
|
||||
|
||||
<div class="grid grid-cols-1">
|
||||
{#if repositories.length !== 0}
|
||||
<div class="grid grid-cols-1 pt-4">
|
||||
{#if $githubRepositories.length !== 0}
|
||||
<label for="repository">Organization / Repository</label>
|
||||
<div class="grid grid-cols-3 ">
|
||||
<div class="repository-select-search col-span-2">
|
||||
<Select
|
||||
isFocused="true"
|
||||
containerClasses="w-full border-none bg-transparent"
|
||||
on:select="{handleSelect}"
|
||||
selectedValue="{selectedValue}"
|
||||
isClearable="{false}"
|
||||
items="{items}"
|
||||
showIndicator="{$isActive('/application/new')}"
|
||||
showIndicator="{$activePage.new}"
|
||||
noOptionsMessage="No Repositories found"
|
||||
placeholder="Select a Repository"
|
||||
isDisabled="{!$isActive('/application/new')}"
|
||||
isDisabled="{!$activePage.new}"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
|
@ -1,17 +1,57 @@
|
||||
<script>
|
||||
import { redirect, isActive } from "@roxi/routify";
|
||||
import { redirect } from "@roxi/routify";
|
||||
import { onMount } from "svelte";
|
||||
import { toast } from "@zerodevx/svelte-toast";
|
||||
import templates from "../../../utils/templates";
|
||||
import { application, fetch, deployments } from "@store";
|
||||
import { application, fetch, deployments, activePage } from "@store";
|
||||
import General from "./ActiveTab/General.svelte";
|
||||
import BuildStep from "./ActiveTab/BuildStep.svelte";
|
||||
import Secrets from "./ActiveTab/Secrets.svelte";
|
||||
import Loading from "../../Loading.svelte";
|
||||
const buildPhaseActive = ["nodejs", "static"];
|
||||
let loading = false;
|
||||
onMount(async () => {
|
||||
if (!$isActive("/application/new")) {
|
||||
|
||||
let activeTab = {
|
||||
general: true,
|
||||
buildStep: false,
|
||||
secrets: false,
|
||||
};
|
||||
function activateTab(tab) {
|
||||
if (activeTab.hasOwnProperty(tab)) {
|
||||
activeTab = {
|
||||
general: false,
|
||||
buildStep: false,
|
||||
secrets: false,
|
||||
};
|
||||
activeTab[tab] = true;
|
||||
}
|
||||
}
|
||||
async function load() {
|
||||
const found = $deployments?.applications?.deployed.find(deployment => {
|
||||
if (
|
||||
deployment.configuration.repository.organization ===
|
||||
$application.repository.organization &&
|
||||
deployment.configuration.repository.name ===
|
||||
$application.repository.name &&
|
||||
deployment.configuration.repository.branch ===
|
||||
$application.repository.branch
|
||||
) {
|
||||
return deployment;
|
||||
}
|
||||
});
|
||||
if (found) {
|
||||
$application = { ...found.configuration };
|
||||
if ($activePage.new) {
|
||||
$activePage.new = false;
|
||||
toast.push(
|
||||
"This repository & branch is already defined. Redirecting...",
|
||||
);
|
||||
$redirect(`/application/:organization/:name/:branch/configuration`, {
|
||||
name: $application.repository.name,
|
||||
organization: $application.repository.organization,
|
||||
branch: $application.repository.branch,
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!$activePage.new) {
|
||||
const config = await $fetch(`/api/v1/config`, {
|
||||
body: {
|
||||
name: $application.repository.name,
|
||||
@ -20,29 +60,7 @@
|
||||
},
|
||||
});
|
||||
$application = { ...config };
|
||||
$redirect(`/application/:organization/:name/:branch/configuration`, {
|
||||
name: $application.repository.name,
|
||||
organization: $application.repository.organization,
|
||||
branch: $application.repository.branch,
|
||||
});
|
||||
} else {
|
||||
loading = true;
|
||||
$deployments?.applications?.deployed.find(d => {
|
||||
const conf = d?.Spec?.Labels.configuration;
|
||||
if (
|
||||
conf?.repository?.organization ===
|
||||
$application.repository.organization &&
|
||||
conf?.repository?.name === $application.repository.name &&
|
||||
conf?.repository?.branch === $application.repository.branch
|
||||
) {
|
||||
$redirect(`/application/:organization/:name/:branch/configuration`, {
|
||||
name: $application.repository.name,
|
||||
organization: $application.repository.organization,
|
||||
branch: $application.repository.branch,
|
||||
});
|
||||
toast.push("This repository & branch is already defined. Redirecting...");
|
||||
}
|
||||
});
|
||||
try {
|
||||
const dir = await $fetch(
|
||||
`https://api.github.com/repos/${$application.repository.organization}/${$application.repository.name}/contents/?ref=${$application.repository.branch}`,
|
||||
@ -70,9 +88,11 @@
|
||||
if (checkPackageJSONContents(dep)) {
|
||||
const config = templates[dep];
|
||||
$application.build.pack = config.pack;
|
||||
if (config.installation) $application.build.command.installation = config.installation;
|
||||
if (config.installation)
|
||||
$application.build.command.installation = config.installation;
|
||||
if (config.port) $application.publish.port = config.port;
|
||||
if (config.directory) $application.publish.directory = config.directory;
|
||||
if (config.directory)
|
||||
$application.publish.directory = config.directory;
|
||||
|
||||
if (
|
||||
packageJsonContent.scripts.hasOwnProperty("build") &&
|
||||
@ -80,43 +100,27 @@
|
||||
) {
|
||||
$application.build.command.build = config.build;
|
||||
}
|
||||
toast.push(`${config.name} App detected. Default values set.`);
|
||||
toast.push(`${config.name} detected. Default values set.`);
|
||||
}
|
||||
});
|
||||
} else if (CargoToml) {
|
||||
$application.build.pack = "rust";
|
||||
toast.push(`Rust language detected. Default values set.`);
|
||||
} else if (Dockerfile) {
|
||||
$application.build.pack = "custom";
|
||||
toast.push("Custom Dockerfile found. Build pack set to custom.");
|
||||
$application.build.pack = "docker";
|
||||
toast.push("Custom Dockerfile found. Build pack set to docker.");
|
||||
}
|
||||
} catch (error) {
|
||||
// Nothing detected
|
||||
}
|
||||
}
|
||||
loading = false;
|
||||
});
|
||||
let activeTab = {
|
||||
general: true,
|
||||
buildStep: false,
|
||||
secrets: false,
|
||||
};
|
||||
function activateTab(tab) {
|
||||
if (activeTab.hasOwnProperty(tab)) {
|
||||
activeTab = {
|
||||
general: false,
|
||||
buildStep: false,
|
||||
secrets: false,
|
||||
};
|
||||
activeTab[tab] = true;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if loading}
|
||||
{#await load()}
|
||||
<Loading github githubLoadingText="Scanning repository..." />
|
||||
{:else}
|
||||
<div class="block text-center py-4">
|
||||
{:then}
|
||||
<div class="block text-center py-8">
|
||||
<nav
|
||||
class="flex space-x-4 justify-center font-bold text-md text-white"
|
||||
aria-label="Tabs"
|
||||
@ -124,47 +128,26 @@
|
||||
<div
|
||||
on:click="{() => activateTab('general')}"
|
||||
class:text-green-500="{activeTab.general}"
|
||||
class="px-3 py-2 cursor-pointer hover:text-green-500"
|
||||
class="px-3 py-2 cursor-pointer hover:bg-warmGray-700 rounded-lg transition duration-100"
|
||||
>
|
||||
General
|
||||
</div>
|
||||
{#if !buildPhaseActive.includes($application.build.pack)}
|
||||
<div disabled class="px-3 py-2 text-warmGray-700 cursor-not-allowed">
|
||||
Build Step
|
||||
</div>
|
||||
{:else}
|
||||
<div
|
||||
on:click="{() => activateTab('buildStep')}"
|
||||
class:text-green-500="{activeTab.buildStep}"
|
||||
class="px-3 py-2 cursor-pointer hover:text-green-500"
|
||||
>
|
||||
Build Step
|
||||
</div>
|
||||
{/if}
|
||||
{#if $application.build.pack === "custom"}
|
||||
<div disabled class="px-3 py-2 text-warmGray-700 cursor-not-allowed">
|
||||
Secrets
|
||||
</div>
|
||||
{:else}
|
||||
<div
|
||||
on:click="{() => activateTab('secrets')}"
|
||||
class:text-green-500="{activeTab.secrets}"
|
||||
class="px-3 py-2 cursor-pointer hover:text-green-500"
|
||||
>
|
||||
Secrets
|
||||
</div>
|
||||
{/if}
|
||||
<div
|
||||
on:click="{() => activateTab('secrets')}"
|
||||
class:text-green-500="{activeTab.secrets}"
|
||||
class="px-3 py-2 cursor-pointer hover:bg-warmGray-700 rounded-lg transition duration-100"
|
||||
>
|
||||
Secrets
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="max-w-4xl mx-auto">
|
||||
<div class="h-full">
|
||||
{#if activeTab.general}
|
||||
<General />
|
||||
{:else if activeTab.buildStep}
|
||||
<BuildStep />
|
||||
{:else if activeTab.secrets}
|
||||
<Secrets />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/await}
|
195
src/components/Application/Navbar.svelte
Normal file
195
src/components/Application/Navbar.svelte
Normal file
@ -0,0 +1,195 @@
|
||||
<script>
|
||||
import { params, goto, redirect } from "@roxi/routify";
|
||||
import {
|
||||
application,
|
||||
fetch,
|
||||
initialApplication,
|
||||
initConf,
|
||||
activePage,
|
||||
} from "@store";
|
||||
import { onDestroy } from "svelte";
|
||||
import { toast } from "@zerodevx/svelte-toast";
|
||||
import Tooltip from "../../components/Tooltip/Tooltip.svelte";
|
||||
|
||||
$application.repository.organization = $params.organization;
|
||||
$application.repository.name = $params.name;
|
||||
$application.repository.branch = $params.branch;
|
||||
|
||||
async function removeApplication() {
|
||||
await $fetch(`/api/v1/application/remove`, {
|
||||
body: {
|
||||
organization: $params.organization,
|
||||
name: $params.name,
|
||||
branch: $params.branch,
|
||||
},
|
||||
});
|
||||
|
||||
toast.push("Application removed.");
|
||||
$application = JSON.parse(JSON.stringify(initialApplication));
|
||||
$redirect(`/dashboard/applications`);
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
$application = JSON.parse(JSON.stringify(initialApplication));
|
||||
});
|
||||
|
||||
async function deploy() {
|
||||
try {
|
||||
toast.push("Checking configuration.");
|
||||
await $fetch(`/api/v1/application/check`, {
|
||||
body: $application,
|
||||
});
|
||||
const { nickname, name, deployId } = await $fetch(
|
||||
`/api/v1/application/deploy`,
|
||||
{
|
||||
body: $application,
|
||||
},
|
||||
);
|
||||
$application.general.nickname = nickname;
|
||||
$application.build.container.name = name;
|
||||
$application.general.deployId = deployId;
|
||||
$initConf = JSON.parse(JSON.stringify($application));
|
||||
toast.push("Application deployment queued.");
|
||||
$redirect(
|
||||
`/application/${$application.repository.organization}/${$application.repository.name}/${$application.repository.branch}/logs/${$application.general.deployId}`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
toast.push(error.error || error || "Ooops something went wrong.");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<nav
|
||||
class="flex text-white justify-end items-center m-4 fixed right-0 top-0 space-x-4 z-50"
|
||||
>
|
||||
<Tooltip position="bottom" label="Deploy">
|
||||
<button
|
||||
disabled="{$application.publish.domain === '' ||
|
||||
$application.publish.domain === null}"
|
||||
class:cursor-not-allowed="{$application.publish.domain === '' ||
|
||||
$application.publish.domain === null}"
|
||||
class:hover:bg-green-500="{$application.publish.domain}"
|
||||
class:bg-green-600="{$application.publish.domain}"
|
||||
class:hover:bg-transparent="{$activePage.new}"
|
||||
class:text-warmGray-700="{$application.publish.domain === '' ||
|
||||
$application.publish.domain === null}"
|
||||
class="icon"
|
||||
on:click="{deploy}"
|
||||
>
|
||||
<svg
|
||||
class="w-6"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
><polyline points="16 16 12 12 8 16"></polyline><line
|
||||
x1="12"
|
||||
y1="12"
|
||||
x2="12"
|
||||
y2="21"></line><path
|
||||
d="M20.39 18.39A5 5 0 0 0 18 9h-1.26A8 8 0 1 0 3 16.3"
|
||||
></path><polyline points="16 16 12 12 8 16"></polyline></svg
|
||||
>
|
||||
</button>
|
||||
</Tooltip>
|
||||
<Tooltip position="bottom" label="Delete">
|
||||
<button
|
||||
disabled="{$application.publish.domain === '' ||
|
||||
$application.publish.domain === null ||
|
||||
$activePage.new}"
|
||||
class:cursor-not-allowed="{$application.publish.domain === '' ||
|
||||
$application.publish.domain === null ||
|
||||
$activePage.new}"
|
||||
class:hover:text-red-500="{$application.publish.domain &&
|
||||
!$activePage.new}"
|
||||
class:hover:bg-warmGray-700="{$application.publish.domain &&
|
||||
!$activePage.new}"
|
||||
class:hover:bg-transparent="{$activePage.new}"
|
||||
class:text-warmGray-700="{$application.publish.domain === '' ||
|
||||
$application.publish.domain === null ||
|
||||
$activePage.new}"
|
||||
class="icon"
|
||||
on:click="{removeApplication}"
|
||||
>
|
||||
<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="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
<div class="border border-warmGray-700 h-8"></div>
|
||||
<Tooltip position="bottom" label="Logs">
|
||||
<button
|
||||
class="icon"
|
||||
class:text-warmGray-700="{$activePage.new}"
|
||||
disabled="{$activePage.new}"
|
||||
class:hover:text-blue-400="{!$activePage.new}"
|
||||
class:hover:bg-transparent="{$activePage.new}"
|
||||
class:cursor-not-allowed="{$activePage.new}"
|
||||
class:text-blue-400="{$activePage.application === 'logs'}"
|
||||
class:bg-warmGray-700="{$activePage.application === 'logs'}"
|
||||
on:click="{() =>
|
||||
$goto(
|
||||
`/application/${$application.repository.organization}/${$application.repository.name}/${$application.repository.branch}/logs`,
|
||||
)}"
|
||||
>
|
||||
<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 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
<Tooltip position="bottom-left" label="Configuration">
|
||||
<button
|
||||
class="icon hover:text-yellow-400"
|
||||
disabled="{$activePage.new}"
|
||||
class:text-yellow-400="{$activePage.application === 'configuration' ||
|
||||
$activePage.new}"
|
||||
class:bg-warmGray-700="{$activePage.application === 'configuration' ||
|
||||
$activePage.new}"
|
||||
on:click="{() =>
|
||||
$goto(
|
||||
`/application/${$application.repository.organization}/${$application.repository.name}/${$application.repository.branch}/configuration`,
|
||||
)}"
|
||||
>
|
||||
<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 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
</nav>
|
@ -3,6 +3,10 @@
|
||||
import { isActive, redirect } from "@roxi/routify/runtime";
|
||||
import { fade } from "svelte/transition";
|
||||
import { toast } from "@zerodevx/svelte-toast";
|
||||
import MongoDb from "../SVGs/MongoDb.svelte";
|
||||
import Postgresql from "../SVGs/Postgresql.svelte";
|
||||
import Mysql from "../SVGs/Mysql.svelte";
|
||||
import CouchDb from "../SVGs/CouchDb.svelte";
|
||||
|
||||
let type;
|
||||
let defaultDatabaseName;
|
||||
@ -15,7 +19,7 @@
|
||||
defaultDatabaseName,
|
||||
},
|
||||
});
|
||||
$dbInprogress = true
|
||||
$dbInprogress = true;
|
||||
toast.push("Database deployment queued.");
|
||||
$redirect(`/dashboard/databases`);
|
||||
} catch (error) {
|
||||
@ -30,34 +34,47 @@
|
||||
>
|
||||
{#if $isActive("/database/new")}
|
||||
<div class="flex justify-center space-x-4 font-bold pb-6">
|
||||
<button
|
||||
class="button bg-gray-500 p-2 text-white hover:bg-green-600 cursor-pointer w-32"
|
||||
<div
|
||||
class="text-center flex-col items-center cursor-pointer ease-in-out transform hover:scale-105 duration-100 border-2 border-dashed border-transparent hover:border-green-600 p-2 rounded bg-warmGray-800 w-32"
|
||||
class:border-green-600="{type === 'mongodb'}"
|
||||
on:click="{() => (type = 'mongodb')}"
|
||||
class:bg-green-600="{type === 'mongodb'}"
|
||||
>
|
||||
MongoDB
|
||||
</button>
|
||||
<button
|
||||
class="button bg-gray-500 p-2 text-white hover:bg-blue-600 cursor-pointer w-32"
|
||||
<div class="flex items-center justify-center my-2">
|
||||
<MongoDb customClass="w-6" />
|
||||
</div>
|
||||
<div class="text-white">MongoDB</div>
|
||||
</div>
|
||||
<div
|
||||
class="text-center flex-col items-center cursor-pointer ease-in-out transform hover:scale-105 duration-100 border-2 border-dashed border-transparent hover:border-red-600 p-2 rounded bg-warmGray-800 w-32"
|
||||
class:border-red-600="{type === 'couchdb'}"
|
||||
on:click="{() => (type = 'couchdb')}"
|
||||
>
|
||||
<div class="flex items-center justify-center my-2">
|
||||
<CouchDb customClass="w-12 text-red-600 fill-current" />
|
||||
</div>
|
||||
<div class="text-white">Couchdb</div>
|
||||
</div>
|
||||
<div
|
||||
class="text-center flex-col items-center cursor-pointer ease-in-out transform hover:scale-105 duration-100 border-2 border-dashed border-transparent hover:border-blue-600 p-2 rounded bg-warmGray-800 w-32"
|
||||
class:border-blue-600="{type === 'postgresql'}"
|
||||
on:click="{() => (type = 'postgresql')}"
|
||||
class:bg-blue-600="{type === 'postgresql'}"
|
||||
>
|
||||
PostgreSQL
|
||||
</button>
|
||||
<button
|
||||
class="button bg-gray-500 p-2 text-white hover:bg-orange-600 cursor-pointer w-32"
|
||||
<div class="flex items-center justify-center my-2">
|
||||
<Postgresql customClass="w-12" />
|
||||
</div>
|
||||
<div class="text-white">PostgreSQL</div>
|
||||
</div>
|
||||
<div
|
||||
class="text-center flex-col items-center cursor-pointer ease-in-out transform hover:scale-105 duration-100 border-2 border-dashed border-transparent hover:border-orange-600 p-2 rounded bg-warmGray-800 w-32"
|
||||
class:border-orange-600="{type === 'mysql'}"
|
||||
on:click="{() => (type = 'mysql')}"
|
||||
class:bg-orange-600="{type === 'mysql'}"
|
||||
>
|
||||
MySQL
|
||||
</button>
|
||||
<button
|
||||
class="button bg-gray-500 p-2 text-white hover:bg-red-600 cursor-pointer w-32"
|
||||
on:click="{() => (type = 'couchdb')}"
|
||||
class:bg-red-600="{type === 'couchdb'}"
|
||||
>
|
||||
Couchdb
|
||||
</button>
|
||||
<div class="flex items-center justify-center">
|
||||
<Mysql customClass="w-10" />
|
||||
</div>
|
||||
<div class="text-white">MySQL</div>
|
||||
</div>
|
||||
|
||||
<!-- <button
|
||||
class="button bg-gray-500 p-2 text-white hover:bg-yellow-500 cursor-pointer w-32"
|
||||
on:click="{() => (type = 'clickhouse')}"
|
||||
@ -67,18 +84,15 @@
|
||||
</button> -->
|
||||
</div>
|
||||
{#if type}
|
||||
<div>
|
||||
<div
|
||||
class="grid grid-rows-1 justify-center items-center text-center pb-5"
|
||||
>
|
||||
<label for="defaultDB">Default database</label>
|
||||
<input
|
||||
id="defaultDB"
|
||||
class="w-64"
|
||||
placeholder="random"
|
||||
bind:value="{defaultDatabaseName}"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex justify-center space-x-4 items-center">
|
||||
<label for="defaultDB">Default database</label>
|
||||
<input
|
||||
id="defaultDB"
|
||||
class="w-64"
|
||||
placeholder="random"
|
||||
bind:value="{defaultDatabaseName}"
|
||||
/>
|
||||
|
||||
<button
|
||||
class:bg-green-600="{type === 'mongodb'}"
|
||||
class:hover:bg-green-500="{type === 'mongodb'}"
|
||||
|
@ -34,7 +34,7 @@
|
||||
<div class="text-left max-w-5xl mx-auto px-6" in:fade="{{ duration: 100 }}">
|
||||
<div class="pb-2 pt-5 space-y-4">
|
||||
<div class="flex space-x-5 items-center">
|
||||
<div class="text-2xl font-bold py-4 border-gradient">General</div>
|
||||
<div class="text-2xl font-bold border-gradient">General</div>
|
||||
<div class="flex-1"></div>
|
||||
<Tooltip
|
||||
position="bottom"
|
||||
@ -48,7 +48,7 @@
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center">
|
||||
<div class="flex items-center pt-4">
|
||||
<div class="font-bold w-64 text-warmGray-400">Domain</div>
|
||||
<input class="w-full" value="{service.config.baseURL}" disabled />
|
||||
</div>
|
||||
@ -64,8 +64,8 @@
|
||||
<div class="font-bold w-64 text-warmGray-400">Password</div>
|
||||
<PasswordField value="{service.config.userPassword}" />
|
||||
</div>
|
||||
<div class="text-2xl font-bold py-4 border-gradient w-32">PostgreSQL</div>
|
||||
<div class="flex items-center">
|
||||
<div class="text-2xl font-bold pt-4 border-gradient w-32">PostgreSQL</div>
|
||||
<div class="flex items-center pt-4">
|
||||
<div class="font-bold w-64 text-warmGray-400">Username</div>
|
||||
<input class="w-full" value="{service.config.generateEnvsPostgres.POSTGRESQL_USERNAME}" disabled />
|
||||
</div>
|
||||
|
@ -5,8 +5,8 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { goto, route, isActive } from "@roxi/routify/runtime";
|
||||
import { loggedIn, session, fetch, deployments } from "@store";
|
||||
import { goto, route, isChangingPage } from "@roxi/routify/runtime";
|
||||
import { loggedIn, session, fetch, deployments, activePage } from "@store";
|
||||
import { toast } from "@zerodevx/svelte-toast";
|
||||
import { onMount } from "svelte";
|
||||
import compareVersions from "compare-versions";
|
||||
@ -23,6 +23,34 @@
|
||||
window.location.hostname !== "test.andrasbacsai.dev"
|
||||
? "main"
|
||||
: "next";
|
||||
|
||||
$: if ($isChangingPage) {
|
||||
const path = window.location.pathname;
|
||||
if (path === "/dashboard/applications" || path.match(/\/application/)) {
|
||||
$activePage.mainmenu = "applications";
|
||||
} else if (path === "/dashboard/databases" || path.match(/\/database/)) {
|
||||
$activePage.mainmenu = "databases";
|
||||
} else if (path === "/dashboard/services" || path.match(/\/service/)) {
|
||||
$activePage.mainmenu = "services";
|
||||
} else if (path === "/settings") {
|
||||
$activePage.mainmenu = "settings";
|
||||
} else {
|
||||
$activePage.mainmenu = null;
|
||||
}
|
||||
|
||||
if (path.match(/\/application\/.*\/logs/)) {
|
||||
$activePage.application = "logs";
|
||||
$activePage.new = false;
|
||||
} else if (path === "/application/new") {
|
||||
$activePage.application = "configuration";
|
||||
$activePage.new = true;
|
||||
} else if (path.match(/\/application\/.*\/configuration/)) {
|
||||
$activePage.application = "configuration";
|
||||
$activePage.new = false;
|
||||
} else {
|
||||
$activePage.application = null;
|
||||
}
|
||||
}
|
||||
onMount(async () => {
|
||||
if ($session.token) {
|
||||
upgradeAvailable = await checkUpgrade();
|
||||
@ -139,18 +167,16 @@
|
||||
>
|
||||
<div
|
||||
class="flex flex-col w-full h-screen items-center transition-all duration-100"
|
||||
class:border-green-500="{$isActive('/dashboard/applications')}"
|
||||
class:border-purple-500="{$isActive('/dashboard/databases')}"
|
||||
class:border-green-500="{$activePage.mainmenu === 'applications'}"
|
||||
class:border-purple-500="{$activePage.mainmenu === 'databases'}"
|
||||
>
|
||||
<img class="w-10 pt-4 pb-4" src="/favicon.png" alt="coolLabs logo" />
|
||||
<Tooltip position="right" label="Applications">
|
||||
<div
|
||||
class="p-2 hover:bg-warmGray-700 rounded hover:text-green-500 mt-4 transition-all duration-100 cursor-pointer"
|
||||
on:click="{() => $goto('/dashboard/applications')}"
|
||||
class:text-green-500="{$isActive('/dashboard/applications') ||
|
||||
$isActive('/application')}"
|
||||
class:bg-warmGray-700="{$isActive('/dashboard/applications') ||
|
||||
$isActive('/application')}"
|
||||
class:text-green-500="{$activePage.mainmenu === 'applications'}"
|
||||
class:bg-warmGray-700="{$activePage.mainmenu === 'applications'}"
|
||||
>
|
||||
<svg
|
||||
class="w-8"
|
||||
@ -187,10 +213,8 @@
|
||||
<div
|
||||
class="p-2 hover:bg-warmGray-700 rounded hover:text-purple-500 my-4 transition-all duration-100 cursor-pointer"
|
||||
on:click="{() => $goto('/dashboard/databases')}"
|
||||
class:text-purple-500="{$isActive('/dashboard/databases') ||
|
||||
$isActive('/database')}"
|
||||
class:bg-warmGray-700="{$isActive('/dashboard/databases') ||
|
||||
$isActive('/database')}"
|
||||
class:text-purple-500="{$activePage.mainmenu === 'databases'}"
|
||||
class:bg-warmGray-700="{$activePage.mainmenu === 'databases'}"
|
||||
>
|
||||
<svg
|
||||
class="w-8"
|
||||
@ -212,22 +236,31 @@
|
||||
<div
|
||||
class="p-2 hover:bg-warmGray-700 rounded hover:text-blue-500 transition-all duration-100 cursor-pointer"
|
||||
on:click="{() => $goto('/dashboard/services')}"
|
||||
class:text-blue-500="{$isActive('/dashboard/services') ||
|
||||
$isActive('/service')}"
|
||||
class:bg-warmGray-700="{$isActive('/dashboard/services') ||
|
||||
$isActive('/service')}"
|
||||
class:text-blue-500="{$activePage.mainmenu === 'services'}"
|
||||
class:bg-warmGray-700="{$activePage.mainmenu === 'services'}"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 15a4 4 0 004 4h9a5 5 0 10-.1-9.999 5.002 5.002 0 10-9.78 2.096A4.001 4.001 0 003 15z" />
|
||||
</svg>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-8"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M3 15a4 4 0 004 4h9a5 5 0 10-.1-9.999 5.002 5.002 0 10-9.78 2.096A4.001 4.001 0 003 15z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<div class="flex-1"></div>
|
||||
<Tooltip position="right" label="Settings">
|
||||
<button
|
||||
class="p-2 hover:bg-warmGray-700 rounded hover:text-yellow-500 transition-all duration-100 cursor-pointer"
|
||||
class:text-yellow-500="{$isActive('/settings')}"
|
||||
class:bg-warmGray-700="{$isActive('/settings')}"
|
||||
class:text-yellow-500="{$activePage.mainmenu === 'settings'}"
|
||||
class:bg-warmGray-700="{$activePage.mainmenu === 'settings'}"
|
||||
on:click="{() => $goto('/settings')}"
|
||||
>
|
||||
<svg
|
||||
@ -284,7 +317,7 @@
|
||||
{/if}
|
||||
{#if upgradeAvailable}
|
||||
<footer
|
||||
class="absolute bottom-0 right-0 p-4 px-6 w-auto rounded-tl text-white "
|
||||
class="fixed bottom-0 right-0 p-4 px-6 w-auto rounded-tl text-white hover:scale-110 transform transition duration-100"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<div></div>
|
||||
|
@ -1,224 +1,75 @@
|
||||
<script>
|
||||
import { params, goto, redirect, isActive } from "@roxi/routify";
|
||||
import { application, fetch, initialApplication, initConf } from "@store";
|
||||
import { params, redirect } from "@roxi/routify";
|
||||
import {
|
||||
application,
|
||||
fetch,
|
||||
initialApplication,
|
||||
initConf,
|
||||
deployments,
|
||||
activePage,
|
||||
} from "@store";
|
||||
import { onDestroy } from "svelte";
|
||||
import { fade } from "svelte/transition";
|
||||
import Loading from "../../components/Loading.svelte";
|
||||
import { toast } from "@zerodevx/svelte-toast";
|
||||
import Tooltip from "../../components/Tooltip/Tooltip.svelte";
|
||||
import Navbar from "../../components/Application/Navbar.svelte";
|
||||
|
||||
$application.repository.organization = $params.organization;
|
||||
$application.repository.name = $params.name;
|
||||
$application.repository.branch = $params.branch;
|
||||
|
||||
async function setConfiguration() {
|
||||
try {
|
||||
const config = await $fetch(`/api/v1/config`, {
|
||||
body: {
|
||||
name: $application.repository.name,
|
||||
organization: $application.repository.organization,
|
||||
branch: $application.repository.branch,
|
||||
},
|
||||
});
|
||||
$application = { ...config };
|
||||
$initConf = JSON.parse(JSON.stringify($application));
|
||||
} catch (error) {
|
||||
toast.push("Configuration not found.");
|
||||
$redirect("/dashboard/applications");
|
||||
}
|
||||
}
|
||||
async function loadConfiguration() {
|
||||
if (!$isActive("/application/new")) {
|
||||
try {
|
||||
const config = await $fetch(`/api/v1/config`, {
|
||||
body: {
|
||||
name: $application.repository.name,
|
||||
organization: $application.repository.organization,
|
||||
branch: $application.repository.branch,
|
||||
},
|
||||
if (!$activePage.new) {
|
||||
if ($deployments.length === 0) {
|
||||
await setConfiguration();
|
||||
} else {
|
||||
const found = $deployments.applications.deployed.find(app => {
|
||||
const { organization, name, branch } = app.configuration;
|
||||
if (
|
||||
organization === $application.repository.organization &&
|
||||
name === $application.repository.name &&
|
||||
branch === $application.repository.branch
|
||||
) {
|
||||
return app;
|
||||
}
|
||||
});
|
||||
$application = { ...config };
|
||||
$initConf = JSON.parse(JSON.stringify($application));
|
||||
} catch (error) {
|
||||
toast.push("Configuration not found.");
|
||||
$redirect("/dashboard/applications");
|
||||
if (found) {
|
||||
$application = { ...found.configuration };
|
||||
$initConf = JSON.parse(JSON.stringify($application));
|
||||
} else {
|
||||
await setConfiguration();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$application = JSON.parse(JSON.stringify(initialApplication));
|
||||
}
|
||||
}
|
||||
async function removeApplication() {
|
||||
await $fetch(`/api/v1/application/remove`, {
|
||||
body: {
|
||||
organization: $params.organization,
|
||||
name: $params.name,
|
||||
branch: $params.branch,
|
||||
},
|
||||
});
|
||||
|
||||
toast.push("Application removed.");
|
||||
$redirect(`/dashboard/applications`);
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
$application = JSON.parse(JSON.stringify(initialApplication));
|
||||
});
|
||||
|
||||
async function deploy() {
|
||||
try {
|
||||
$application.build.pack = $application.build.pack.replace('.','').toLowerCase()
|
||||
toast.push("Checking inputs.");
|
||||
await $fetch(`/api/v1/application/check`, {
|
||||
body: $application,
|
||||
});
|
||||
const { nickname, name, deployId } = await $fetch(`/api/v1/application/deploy`, {
|
||||
body: $application,
|
||||
});
|
||||
$application.general.nickname = nickname;
|
||||
$application.build.container.name = name;
|
||||
$application.general.deployId = deployId;
|
||||
$initConf = JSON.parse(JSON.stringify($application));
|
||||
toast.push("Application deployment queued.");
|
||||
$goto(
|
||||
`/application/${$application.repository.organization}/${$application.repository.name}/${$application.repository.branch}/logs/${$application.general.deployId}`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
toast.push(error.error || error || "Ooops something went wrong.");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#await loadConfiguration()}
|
||||
<Loading />
|
||||
{:then}
|
||||
<nav
|
||||
class="flex text-white justify-end items-center m-4 fixed right-0 top-0 space-x-4"
|
||||
>
|
||||
<Tooltip position="bottom" label="Deploy" >
|
||||
<button
|
||||
disabled="{$application.publish.domain === '' ||
|
||||
$application.publish.domain === null}"
|
||||
class:cursor-not-allowed="{$application.publish.domain === '' ||
|
||||
$application.publish.domain === null}"
|
||||
class:hover:text-green-500="{$application.publish.domain}"
|
||||
class:hover:bg-warmGray-700="{$application.publish.domain}"
|
||||
class:hover:bg-transparent="{$isActive('/application/new')}"
|
||||
class:text-warmGray-700="{$application.publish.domain === '' ||
|
||||
$application.publish.domain === null}"
|
||||
class="icon"
|
||||
on:click="{deploy}"
|
||||
>
|
||||
|
||||
<svg
|
||||
class="w-6"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
><polyline points="16 16 12 12 8 16"></polyline><line
|
||||
x1="12"
|
||||
y1="12"
|
||||
x2="12"
|
||||
y2="21"></line><path
|
||||
d="M20.39 18.39A5 5 0 0 0 18 9h-1.26A8 8 0 1 0 3 16.3"></path><polyline
|
||||
points="16 16 12 12 8 16"></polyline></svg
|
||||
>
|
||||
|
||||
</button>
|
||||
</Tooltip>
|
||||
<Tooltip position="bottom" label="Delete" >
|
||||
<button
|
||||
disabled="{$application.publish.domain === '' ||
|
||||
$application.publish.domain === null ||
|
||||
$isActive('/application/new')}"
|
||||
class:cursor-not-allowed="{$application.publish.domain === '' ||
|
||||
$application.publish.domain === null ||
|
||||
$isActive('/application/new')}"
|
||||
class:hover:text-red-500="{$application.publish.domain &&
|
||||
!$isActive('/application/new')}"
|
||||
class:hover:bg-warmGray-700="{$application.publish.domain &&
|
||||
!$isActive('/application/new')}"
|
||||
class:hover:bg-transparent="{$isActive('/application/new')}"
|
||||
class:text-warmGray-700="{$application.publish.domain === '' ||
|
||||
$application.publish.domain === null ||
|
||||
$isActive('/application/new')}"
|
||||
class="icon"
|
||||
on:click="{removeApplication}"
|
||||
>
|
||||
<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="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
<div class="border border-warmGray-700 h-8"></div>
|
||||
<Tooltip position="bottom" label="Logs" >
|
||||
<button
|
||||
class="icon"
|
||||
class:text-warmGray-700="{$isActive('/application/new')}"
|
||||
disabled="{$isActive('/application/new')}"
|
||||
class:hover:text-blue-400="{!$isActive('/application/new')}"
|
||||
class:hover:bg-transparent="{$isActive('/application/new')}"
|
||||
class:cursor-not-allowed="{$isActive('/application/new')}"
|
||||
class:text-blue-400="{$isActive(
|
||||
`/application/${$application.repository.organization}/${$application.repository.name}/${$application.repository.branch}/logs`,
|
||||
)}"
|
||||
class:bg-warmGray-700="{$isActive(
|
||||
`/application/${$application.repository.organization}/${$application.repository.name}/${$application.repository.branch}/logs`,
|
||||
)}"
|
||||
on:click="{() =>
|
||||
$goto(
|
||||
`/application/${$application.repository.organization}/${$application.repository.name}/${$application.repository.branch}/logs`,
|
||||
)}"
|
||||
>
|
||||
<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 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
<Tooltip position="bottom-left" label="Configuration" >
|
||||
<button
|
||||
class="icon hover:text-yellow-400"
|
||||
disabled="{$isActive(`/application/new`)}"
|
||||
class:text-yellow-400="{$isActive(
|
||||
`/application/${$application.repository.organization}/${$application.repository.name}/${$application.repository.branch}/configuration`,
|
||||
) || $isActive(`/application/new`)}"
|
||||
class:bg-warmGray-700="{$isActive(
|
||||
`/application/${$application.repository.organization}/${$application.repository.name}/${$application.repository.branch}/configuration`,
|
||||
) || $isActive(`/application/new`)}"
|
||||
on:click="{() =>
|
||||
$goto(
|
||||
`/application/${$application.repository.organization}/${$application.repository.name}/${$application.repository.branch}/configuration`,
|
||||
)}"
|
||||
>
|
||||
<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 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
</nav>
|
||||
|
||||
<div class="text-white">
|
||||
<slot />
|
||||
</div>
|
||||
<Navbar />
|
||||
<div class="text-white">
|
||||
<slot />
|
||||
</div>
|
||||
{/await}
|
||||
|
@ -1,8 +1,6 @@
|
||||
<script>
|
||||
import { fetch, deployments } from "@store";
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
import { fade } from "svelte/transition";
|
||||
import { goto, isActive } from "@roxi/routify/runtime";
|
||||
import { toast } from "@zerodevx/svelte-toast";
|
||||
let loadDashboardInterval = null;
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
function switchTo(application) {
|
||||
const { branch, name, organization } = application;
|
||||
$goto(`/application/:organization/:name/:branch`, {
|
||||
$goto(`/application/:organization/:name/:branch/configuration`, {
|
||||
name,
|
||||
organization,
|
||||
branch,
|
||||
@ -48,15 +48,15 @@
|
||||
on:click="{() =>
|
||||
switchTo({
|
||||
branch:
|
||||
application.Spec.Labels.configuration.repository.branch,
|
||||
name: application.Spec.Labels.configuration.repository.name,
|
||||
application.configuration.repository.branch,
|
||||
name: application.configuration.repository.name,
|
||||
organization:
|
||||
application.Spec.Labels.configuration.repository
|
||||
application.configuration.repository
|
||||
.organization,
|
||||
})}"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
{#if application.Spec.Labels.configuration.build.pack === "static"}
|
||||
{#if application.configuration.build.pack === "static"}
|
||||
<svg
|
||||
class="text-white w-10 h-10 absolute top-0 left-0 -m-4"
|
||||
viewBox="0 0 32 32"
|
||||
@ -83,7 +83,74 @@
|
||||
></defs
|
||||
></svg
|
||||
>
|
||||
{:else if application.Spec.Labels.configuration.build.pack === "nodejs"}
|
||||
{:else if application.configuration.build.pack === "react"}
|
||||
<svg
|
||||
class="text-blue-500 w-10 h-10 absolute top-0 left-0 -m-4"
|
||||
viewBox="0 0 128 128"
|
||||
>
|
||||
<g fill="#61DAFB"
|
||||
><circle cx="64" cy="64" r="11.4"></circle><path
|
||||
d="M107.3 45.2c-2.2-.8-4.5-1.6-6.9-2.3.6-2.4 1.1-4.8 1.5-7.1 2.1-13.2-.2-22.5-6.6-26.1-1.9-1.1-4-1.6-6.4-1.6-7 0-15.9 5.2-24.9 13.9-9-8.7-17.9-13.9-24.9-13.9-2.4 0-4.5.5-6.4 1.6-6.4 3.7-8.7 13-6.6 26.1.4 2.3.9 4.7 1.5 7.1-2.4.7-4.7 1.4-6.9 2.3-12.5 4.8-19.3 11.4-19.3 18.8s6.9 14 19.3 18.8c2.2.8 4.5 1.6 6.9 2.3-.6 2.4-1.1 4.8-1.5 7.1-2.1 13.2.2 22.5 6.6 26.1 1.9 1.1 4 1.6 6.4 1.6 7.1 0 16-5.2 24.9-13.9 9 8.7 17.9 13.9 24.9 13.9 2.4 0 4.5-.5 6.4-1.6 6.4-3.7 8.7-13 6.6-26.1-.4-2.3-.9-4.7-1.5-7.1 2.4-.7 4.7-1.4 6.9-2.3 12.5-4.8 19.3-11.4 19.3-18.8s-6.8-14-19.3-18.8zm-14.8-30.5c4.1 2.4 5.5 9.8 3.8 20.3-.3 2.1-.8 4.3-1.4 6.6-5.2-1.2-10.7-2-16.5-2.5-3.4-4.8-6.9-9.1-10.4-13 7.4-7.3 14.9-12.3 21-12.3 1.3 0 2.5.3 3.5.9zm-11.2 59.3c-1.8 3.2-3.9 6.4-6.1 9.6-3.7.3-7.4.4-11.2.4-3.9 0-7.6-.1-11.2-.4-2.2-3.2-4.2-6.4-6-9.6-1.9-3.3-3.7-6.7-5.3-10 1.6-3.3 3.4-6.7 5.3-10 1.8-3.2 3.9-6.4 6.1-9.6 3.7-.3 7.4-.4 11.2-.4 3.9 0 7.6.1 11.2.4 2.2 3.2 4.2 6.4 6 9.6 1.9 3.3 3.7 6.7 5.3 10-1.7 3.3-3.4 6.6-5.3 10zm8.3-3.3c1.5 3.5 2.7 6.9 3.8 10.3-3.4.8-7 1.4-10.8 1.9 1.2-1.9 2.5-3.9 3.6-6 1.2-2.1 2.3-4.2 3.4-6.2zm-25.6 27.1c-2.4-2.6-4.7-5.4-6.9-8.3 2.3.1 4.6.2 6.9.2 2.3 0 4.6-.1 6.9-.2-2.2 2.9-4.5 5.7-6.9 8.3zm-18.6-15c-3.8-.5-7.4-1.1-10.8-1.9 1.1-3.3 2.3-6.8 3.8-10.3 1.1 2 2.2 4.1 3.4 6.1 1.2 2.2 2.4 4.1 3.6 6.1zm-7-25.5c-1.5-3.5-2.7-6.9-3.8-10.3 3.4-.8 7-1.4 10.8-1.9-1.2 1.9-2.5 3.9-3.6 6-1.2 2.1-2.3 4.2-3.4 6.2zm25.6-27.1c2.4 2.6 4.7 5.4 6.9 8.3-2.3-.1-4.6-.2-6.9-.2-2.3 0-4.6.1-6.9.2 2.2-2.9 4.5-5.7 6.9-8.3zm22.2 21l-3.6-6c3.8.5 7.4 1.1 10.8 1.9-1.1 3.3-2.3 6.8-3.8 10.3-1.1-2.1-2.2-4.2-3.4-6.2zm-54.5-16.2c-1.7-10.5-.3-17.9 3.8-20.3 1-.6 2.2-.9 3.5-.9 6 0 13.5 4.9 21 12.3-3.5 3.8-7 8.2-10.4 13-5.8.5-11.3 1.4-16.5 2.5-.6-2.3-1-4.5-1.4-6.6zm-24.7 29c0-4.7 5.7-9.7 15.7-13.4 2-.8 4.2-1.5 6.4-2.1 1.6 5 3.6 10.3 6 15.6-2.4 5.3-4.5 10.5-6 15.5-13.8-4-22.1-10-22.1-15.6zm28.5 49.3c-4.1-2.4-5.5-9.8-3.8-20.3.3-2.1.8-4.3 1.4-6.6 5.2 1.2 10.7 2 16.5 2.5 3.4 4.8 6.9 9.1 10.4 13-7.4 7.3-14.9 12.3-21 12.3-1.3 0-2.5-.3-3.5-.9zm60.8-20.3c1.7 10.5.3 17.9-3.8 20.3-1 .6-2.2.9-3.5.9-6 0-13.5-4.9-21-12.3 3.5-3.8 7-8.2 10.4-13 5.8-.5 11.3-1.4 16.5-2.5.6 2.3 1 4.5 1.4 6.6zm9-15.6c-2 .8-4.2 1.5-6.4 2.1-1.6-5-3.6-10.3-6-15.6 2.4-5.3 4.5-10.5 6-15.5 13.8 4 22.1 10 22.1 15.6 0 4.7-5.8 9.7-15.7 13.4z"
|
||||
></path></g
|
||||
>
|
||||
</svg>
|
||||
{:else if application.configuration.build.pack === "gatsby"}
|
||||
<svg class="w-10 h-10 absolute top-0 left-0 -m-4" viewBox="0 0 128 128">
|
||||
<path fill="#64328B" d="M64,0C28.7,0,0,28.7,0,64v0c0,35.3,28.7,64,64,64s64-28.7,64-64v0C128,28.7,99.3,0,64,0z M13.2,64L64,114.8 C35.9,114.8,13.2,92.1,13.2,64z M75.4,113.5l-60.9-61C19.7,30,39.9,13.2,64,13.2c16.6,0,31.3,7.9,40.5,20.2l-7.5,7.2 C89.7,30.2,77.7,23.5,64,23.5c-17.6,0-32.5,11.2-38.1,26.8C33.1,57,75.4,98.8,78.1,102c12.7-4.7,22.3-15.5,25.4-28.9H81.9v-9.4 l33,0.2C114.8,88.2,98,108.4,75.4,113.5z"></path>
|
||||
</svg>
|
||||
{:else if application.configuration.build.pack === "nuxtjs"}
|
||||
<svg class="w-10 h-10 absolute top-0 left-0 -m-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400">
|
||||
<g fill-rule="nonzero" transform="translate(0 50)" fill="none">
|
||||
<path d="M227.92099 83.45116l-13.6889 24.10141-46.8148-82.44693L23.7037 278.17052h97.3037c0 13.31084 10.61252 24.10142 23.70371 24.10142H23.70371c-8.46771 0-16.29145-4.59601-20.5246-12.05272-4.23315-7.4567-4.23272-16.64312.00114-24.0994L146.89383 13.05492c4.23415-7.45738 12.0596-12.05138 20.5284-12.05138 8.46878 0 16.29423 4.594 20.52839 12.05138l39.97037 70.39623z" fill="#00C58E"/>
|
||||
<path d="M331.6642 266.11981l-90.05432-158.56724-13.6889-24.10141-13.68888 24.10141-90.04445 158.56724c-4.23385 7.45629-4.23428 16.64271-.00113 24.09941 4.23314 7.4567 12.05689 12.05272 20.5246 12.05272h166.4c8.46946 0 16.29644-4.591 20.532-12.04837 4.23555-7.45736 4.23606-16.64592.00132-24.10376h.01976zM144.7111 278.17052L227.921 131.65399l83.19012 146.51653h-166.4z" fill="#FFF"/>
|
||||
<path d="M396.04938 290.22123c-4.23344 7.45557-12.05656 12.0507-20.52345 12.0507H311.1111c13.0912 0 23.7037-10.79057 23.7037-24.10141h40.66173L260.09877 74.98553l-18.4889 32.56704L227.921 83.45116l11.65432-20.51634c4.23416-7.45738 12.0596-12.05138 20.5284-12.05138 8.46879 0 16.29423 4.594 20.52839 12.05138l115.41728 203.185c4.23426 7.457 4.23426 16.6444 0 24.1014z" fill="#108775"/>
|
||||
</g>
|
||||
</svg>
|
||||
{:else if application.configuration.build.pack === "svelte"}
|
||||
<svg
|
||||
class="w-10 h-10 absolute top-0 left-0 -m-4"
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 98.1 118"
|
||||
style="enable-background:new 0 0 98.1 118;"
|
||||
xml:space="preserve"
|
||||
>
|
||||
<path
|
||||
fill="#FF3E00"
|
||||
d="M91.8,15.6C80.9-0.1,59.2-4.7,43.6,5.2L16.1,22.8C8.6,27.5,3.4,35.2,1.9,43.9c-1.3,7.3-0.2,14.8,3.3,21.3 c-2.4,3.6-4,7.6-4.7,11.8c-1.6,8.9,0.5,18.1,5.7,25.4c11,15.7,32.6,20.3,48.2,10.4l27.5-17.5c7.5-4.7,12.7-12.4,14.2-21.1 c1.3-7.3,0.2-14.8-3.3-21.3c2.4-3.6,4-7.6,4.7-11.8C99.2,32.1,97.1,22.9,91.8,15.6"
|
||||
></path>
|
||||
<path
|
||||
fill="#FFFFFF"
|
||||
d="M40.9,103.9c-8.9,2.3-18.2-1.2-23.4-8.7c-3.2-4.4-4.4-9.9-3.5-15.3c0.2-0.9,0.4-1.7,0.6-2.6l0.5-1.6l1.4,1 c3.3,2.4,6.9,4.2,10.8,5.4l1,0.3l-0.1,1c-0.1,1.4,0.3,2.9,1.1,4.1c1.6,2.3,4.4,3.4,7.1,2.7c0.6-0.2,1.2-0.4,1.7-0.7L65.5,72 c1.4-0.9,2.3-2.2,2.6-3.8c0.3-1.6-0.1-3.3-1-4.6c-1.6-2.3-4.4-3.3-7.1-2.6c-0.6,0.2-1.2,0.4-1.7,0.7l-10.5,6.7 c-1.7,1.1-3.6,1.9-5.6,2.4c-8.9,2.3-18.2-1.2-23.4-8.7c-3.1-4.4-4.4-9.9-3.4-15.3c0.9-5.2,4.1-9.9,8.6-12.7l27.5-17.5 c1.7-1.1,3.6-1.9,5.6-2.5c8.9-2.3,18.2,1.2,23.4,8.7c3.2,4.4,4.4,9.9,3.5,15.3c-0.2,0.9-0.4,1.7-0.7,2.6l-0.5,1.6l-1.4-1 c-3.3-2.4-6.9-4.2-10.8-5.4l-1-0.3l0.1-1c0.1-1.4-0.3-2.9-1.1-4.1c-1.6-2.3-4.4-3.3-7.1-2.6c-0.6,0.2-1.2,0.4-1.7,0.7L32.4,46.1 c-1.4,0.9-2.3,2.2-2.6,3.8s0.1,3.3,1,4.6c1.6,2.3,4.4,3.3,7.1,2.6c0.6-0.2,1.2-0.4,1.7-0.7l10.5-6.7c1.7-1.1,3.6-1.9,5.6-2.5 c8.9-2.3,18.2,1.2,23.4,8.7c3.2,4.4,4.4,9.9,3.5,15.3c-0.9,5.2-4.1,9.9-8.6,12.7l-27.5,17.5C44.8,102.5,42.9,103.3,40.9,103.9"
|
||||
></path>
|
||||
</svg>
|
||||
{:else if application.configuration.build.pack === "vuejs"}
|
||||
<svg
|
||||
class="text-green-500 w-10 h-10 absolute top-0 left-0 -m-4"
|
||||
viewBox="0 0 128 128"
|
||||
>
|
||||
<path
|
||||
d="m-2.3125e-8 8.9337 49.854 0.1586 14.167 24.47 14.432-24.47 49.547-0.1577-63.834 110.14zm126.98 0.6374-24.36 0.0207-38.476 66.052-38.453-66.052-24.749-0.0194 63.211 107.89zm-25.149-0.008-22.745 0.16758l-15.053 24.647-14.817-24.647-22.794-0.1679 37.731 64.476zM25.997 9.3929l23.002 0.0087M25.997 9.3929l23.002 0.0087"
|
||||
fill="none"></path><path
|
||||
d="m25.997 9.3929 23.002 0.0087l15.036 24.958 14.983-24.956 22.982-0.0057-37.85 65.655z"
|
||||
fill="#35495e"></path><path
|
||||
d="m0.91068 9.5686 25.066-0.1711 38.151 65.658 37.852-65.654 25.11 0.0263-62.966 108.06z"
|
||||
fill="#41b883"></path>
|
||||
</svg>
|
||||
{:else if application.configuration.build.pack === "nextjs"}
|
||||
<svg
|
||||
class="text-blue-500 w-10 h-10 absolute top-0 left-0 -m-4 fill-current"
|
||||
viewBox="0 0 128 128"
|
||||
>
|
||||
<path
|
||||
d="M64 0C28.7 0 0 28.7 0 64s28.7 64 64 64c11.2 0 21.7-2.9 30.8-7.9L48.4 55.3v36.6h-6.8V41.8h6.8l50.5 75.8C116.4 106.2 128 86.5 128 64c0-35.3-28.7-64-64-64zm22.1 84.6l-7.5-11.3V41.8h7.5v42.8z"
|
||||
></path>
|
||||
</svg>
|
||||
{:else if application.configuration.build.pack === "nodejs"}
|
||||
<svg
|
||||
class="text-green-400 w-10 h-10 absolute top-0 left-0 -m-4"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@ -98,7 +165,7 @@
|
||||
d="M224 508c-6.7 0-13.5-1.8-19.4-5.2l-61.7-36.5c-9.2-5.2-4.7-7-1.7-8 12.3-4.3 14.8-5.2 27.9-12.7 1.4-.8 3.2-.5 4.6.4l47.4 28.1c1.7 1 4.1 1 5.7 0l184.7-106.6c1.7-1 2.8-3 2.8-5V149.3c0-2.1-1.1-4-2.9-5.1L226.8 37.7c-1.7-1-4-1-5.7 0L36.6 144.3c-1.8 1-2.9 3-2.9 5.1v213.1c0 2 1.1 4 2.9 4.9l50.6 29.2c27.5 13.7 44.3-2.4 44.3-18.7V167.5c0-3 2.4-5.3 5.4-5.3h23.4c2.9 0 5.4 2.3 5.4 5.3V378c0 36.6-20 57.6-54.7 57.6-10.7 0-19.1 0-42.5-11.6l-48.4-27.9C8.1 389.2.7 376.3.7 362.4V149.3c0-13.8 7.4-26.8 19.4-33.7L204.6 9c11.7-6.6 27.2-6.6 38.8 0l184.7 106.7c12 6.9 19.4 19.8 19.4 33.7v213.1c0 13.8-7.4 26.7-19.4 33.7L243.4 502.8c-5.9 3.4-12.6 5.2-19.4 5.2zm149.1-210.1c0-39.9-27-50.5-83.7-58-57.4-7.6-63.2-11.5-63.2-24.9 0-11.1 4.9-25.9 47.4-25.9 37.9 0 51.9 8.2 57.7 33.8.5 2.4 2.7 4.2 5.2 4.2h24c1.5 0 2.9-.6 3.9-1.7s1.5-2.6 1.4-4.1c-3.7-44.1-33-64.6-92.2-64.6-52.7 0-84.1 22.2-84.1 59.5 0 40.4 31.3 51.6 81.8 56.6 60.5 5.9 65.2 14.8 65.2 26.7 0 20.6-16.6 29.4-55.5 29.4-48.9 0-59.6-12.3-63.2-36.6-.4-2.6-2.6-4.5-5.3-4.5h-23.9c-3 0-5.3 2.4-5.3 5.3 0 31.1 16.9 68.2 97.8 68.2 58.4-.1 92-23.2 92-63.4z"
|
||||
></path>
|
||||
</svg>
|
||||
{:else if application.Spec.Labels.configuration.build.pack === "php"}
|
||||
{:else if application.configuration.build.pack === "php"}
|
||||
<svg
|
||||
viewBox="0 0 128 128"
|
||||
class="text-white w-14 h-14 absolute top-0 left-0 -m-6"
|
||||
@ -108,7 +175,7 @@
|
||||
d="M64 33.039c-33.74 0-61.094 13.862-61.094 30.961s27.354 30.961 61.094 30.961 61.094-13.862 61.094-30.961-27.354-30.961-61.094-30.961zm-15.897 36.993c-1.458 1.364-3.077 1.927-4.86 2.507-1.783.581-4.052.461-6.811.461h-6.253l-1.733 10h-7.301l6.515-34h14.04c4.224 0 7.305 1.215 9.242 3.432 1.937 2.217 2.519 5.364 1.747 9.337-.319 1.637-.856 3.159-1.614 4.515-.759 1.357-1.75 2.624-2.972 3.748zm21.311 2.968l2.881-14.42c.328-1.688.208-2.942-.361-3.555-.57-.614-1.782-1.025-3.635-1.025h-5.79l-3.731 19h-7.244l6.515-33h7.244l-1.732 9h6.453c4.061 0 6.861.815 8.402 2.231s2.003 3.356 1.387 6.528l-3.031 15.241h-7.358zm40.259-11.178c-.318 1.637-.856 3.133-1.613 4.488-.758 1.357-1.748 2.598-2.971 3.722-1.458 1.364-3.078 1.927-4.86 2.507-1.782.581-4.053.461-6.812.461h-6.253l-1.732 10h-7.301l6.514-34h14.041c4.224 0 7.305 1.215 9.241 3.432 1.935 2.217 2.518 5.418 1.746 9.39zM95.919 54h-5.001l-2.727 14h4.442c2.942 0 5.136-.29 6.576-1.4 1.442-1.108 2.413-2.828 2.918-5.421.484-2.491.264-4.434-.66-5.458-.925-1.024-2.774-1.721-5.548-1.721zM38.934 54h-5.002l-2.727 14h4.441c2.943 0 5.136-.29 6.577-1.4 1.441-1.108 2.413-2.828 2.917-5.421.484-2.491.264-4.434-.66-5.458s-2.772-1.721-5.546-1.721z"
|
||||
></path>
|
||||
</svg>
|
||||
{:else if application.Spec.Labels.configuration.build.pack === "custom"}
|
||||
{:else if application.configuration.build.pack === "docker"}
|
||||
<svg
|
||||
viewBox="0 0 128 128"
|
||||
class="w-16 h-16 absolute top-0 left-0 -m-8"
|
||||
@ -185,7 +252,7 @@
|
||||
></path></g
|
||||
>
|
||||
</svg>
|
||||
{:else if application.Spec.Labels.configuration.build.pack === "rust"}
|
||||
{:else if application.configuration.build.pack === "rust"}
|
||||
<svg
|
||||
class="w-14 h-14 absolute top-0 left-0 -m-6"
|
||||
viewBox="0 0 128 128"
|
||||
@ -201,10 +268,10 @@
|
||||
<div
|
||||
class="text-base font-bold text-center w-full text-white pb-6"
|
||||
>
|
||||
{application.Spec.Labels.configuration.publish
|
||||
.domain}{application.Spec.Labels.configuration.publish
|
||||
{application.configuration.publish
|
||||
.domain}{application.configuration.publish
|
||||
.path !== "/"
|
||||
? application.Spec.Labels.configuration.publish.path
|
||||
? application.configuration.publish.path
|
||||
: ""}
|
||||
</div>
|
||||
<div
|
||||
|
@ -91,26 +91,26 @@
|
||||
class="px-4 pb-4"
|
||||
on:click="{() =>
|
||||
$goto(
|
||||
`/database/${database.Spec.Labels.configuration.general.deployId}/configuration`,
|
||||
`/database/${database.configuration.general.deployId}/configuration`,
|
||||
)}"
|
||||
>
|
||||
<div
|
||||
class="relative rounded-xl p-6 bg-warmGray-800 border-2 border-dashed border-transparent hover:border-purple-500 text-white shadow-md cursor-pointer ease-in-out transform hover:scale-105 duration-100 group"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
{#if database.Spec.Labels.configuration.general.type == "mongodb"}
|
||||
{#if database.configuration.general.type == "mongodb"}
|
||||
<MongoDb customClass="w-10 h-10 absolute top-0 left-0 -m-4" />
|
||||
{:else if database.Spec.Labels.configuration.general.type == "postgresql"}
|
||||
{:else if database.configuration.general.type == "postgresql"}
|
||||
<Postgresql
|
||||
customClass="w-10 h-10 absolute top-0 left-0 -m-4"
|
||||
/>
|
||||
{:else if database.Spec.Labels.configuration.general.type == "mysql"}
|
||||
{:else if database.configuration.general.type == "mysql"}
|
||||
<Mysql customClass="w-10 h-10 absolute top-0 left-0 -m-4" />
|
||||
{:else if database.Spec.Labels.configuration.general.type == "couchdb"}
|
||||
{:else if database.configuration.general.type == "couchdb"}
|
||||
<CouchDb
|
||||
customClass="w-10 h-10 fill-current text-red-600 absolute top-0 left-0 -m-4"
|
||||
/>
|
||||
{:else if database.Spec.Labels.configuration.general.type == "clickhouse"}
|
||||
{:else if database.configuration.general.type == "clickhouse"}
|
||||
<Clickhouse
|
||||
customClass="w-10 h-10 fill-current text-red-600 absolute top-0 left-0 -m-4"
|
||||
/>
|
||||
@ -119,10 +119,10 @@
|
||||
<div
|
||||
class="text-base font-bold text-white group-hover:text-white"
|
||||
>
|
||||
{database.Spec.Labels.configuration.general.nickname}
|
||||
{database.configuration.general.nickname}
|
||||
</div>
|
||||
<div class="text-xs font-bold text-warmGray-300 ">
|
||||
({database.Spec.Labels.configuration.general.type})
|
||||
({database.configuration.general.type})
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -38,21 +38,21 @@
|
||||
</button>
|
||||
</div>
|
||||
<div in:fade="{{ duration: 100 }}">
|
||||
{#if $deployments.services?.deployed.length > 0}
|
||||
{#if $deployments?.services?.deployed.length > 0}
|
||||
<div class="px-4 mx-auto py-5">
|
||||
<div class="flex items-center justify-center flex-wrap">
|
||||
{#each $deployments.services.deployed as service}
|
||||
{#each $deployments?.services?.deployed as service}
|
||||
<div
|
||||
in:fade="{{ duration: 200 }}"
|
||||
class="px-4 pb-4"
|
||||
on:click="{() =>
|
||||
$goto(`/service/${service.Spec.Labels.serviceName}/configuration`)}"
|
||||
$goto(`/service/${service.serviceName}/configuration`)}"
|
||||
>
|
||||
<div
|
||||
class="relative rounded-xl p-6 bg-warmGray-800 border-2 border-dashed border-transparent hover:border-blue-500 text-white shadow-md cursor-pointer ease-in-out transform hover:scale-105 duration-100 group"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
{#if service.Spec.Labels.serviceName == "plausible"}
|
||||
{#if service.serviceName == "plausible"}
|
||||
<div>
|
||||
<img
|
||||
alt="plausible logo"
|
||||
|
@ -17,7 +17,6 @@
|
||||
if (name) {
|
||||
try {
|
||||
$database = await $fetch(`/api/v1/databases/${name}`);
|
||||
console.log($database);
|
||||
} catch (error) {
|
||||
toast.push(`Cannot find database ${name}`);
|
||||
$redirect(`/dashboard/databases`);
|
||||
@ -49,8 +48,8 @@
|
||||
</div>
|
||||
<div class="text-left max-w-6xl mx-auto px-6" in:fade="{{ duration: 100 }}">
|
||||
<div class="pb-2 pt-5 space-y-4">
|
||||
<div class="text-2xl font-bold py-4 border-gradient w-32">Database</div>
|
||||
<div class="flex items-center">
|
||||
<div class="text-2xl font-bold border-gradient w-32">Database</div>
|
||||
<div class="flex items-center pt-4">
|
||||
<div class="font-bold w-64 text-warmGray-400">Connection string</div>
|
||||
{#if $database.config.general.type === "mongodb"}
|
||||
<PasswordField
|
||||
@ -80,11 +79,8 @@
|
||||
</div>
|
||||
{#if $database.config.general.type === "mongodb"}
|
||||
<div class="flex items-center">
|
||||
<div class="font-bold w-48 text-warmGray-400">Root password</div>
|
||||
<textarea
|
||||
disabled
|
||||
class="w-full"
|
||||
value="{$database.envs.MONGODB_ROOT_PASSWORD}"></textarea>
|
||||
<div class="font-bold w-64 text-warmGray-400">Root password</div>
|
||||
<PasswordField value="{$database.envs.MONGODB_ROOT_PASSWORD}" />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -1,11 +1,11 @@
|
||||
<script>
|
||||
import { params, goto, isActive, redirect, url } from "@roxi/routify";
|
||||
import { params, goto, isActive, redirect } from "@roxi/routify";
|
||||
import { fetch, database, initialDatabase } from "@store";
|
||||
import { toast } from "@zerodevx/svelte-toast";
|
||||
import { onDestroy } from "svelte";
|
||||
import Tooltip from "../../components/Tooltip/Tooltip.svelte";
|
||||
import Tooltip from "../../components/Tooltip/Tooltip.svelte";
|
||||
|
||||
$: name = $params.name
|
||||
$: name = $params.name;
|
||||
|
||||
onDestroy(() => {
|
||||
$database = JSON.parse(JSON.stringify(initialDatabase));
|
||||
@ -24,43 +24,38 @@ import Tooltip from "../../components/Tooltip/Tooltip.svelte";
|
||||
<nav
|
||||
class="flex text-white justify-end items-center m-4 fixed right-0 top-0 space-x-4"
|
||||
>
|
||||
<Tooltip position="bottom" label="Delete" >
|
||||
<button
|
||||
title="Delete"
|
||||
class="icon hover:text-red-500"
|
||||
on:click="{removeDB}"
|
||||
>
|
||||
<svg
|
||||
class="w-6"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
<Tooltip position="bottom" label="Delete">
|
||||
<button
|
||||
title="Delete"
|
||||
class="icon hover:text-red-500"
|
||||
on:click="{removeDB}"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
<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="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
<div class="border border-warmGray-700 h-8"></div>
|
||||
<Tooltip position="bottom-left" label="Configuration" >
|
||||
<Tooltip position="bottom-left" label="Configuration">
|
||||
<button
|
||||
class="icon hover:text-yellow-400"
|
||||
disabled="{$isActive(`/database/new`)}"
|
||||
class:text-yellow-400="{$isActive(
|
||||
`/database/${name}/configuration`,
|
||||
) || $isActive(`/application/new`)}"
|
||||
class:bg-warmGray-700="{$isActive(
|
||||
`/database/${name}/configuration`,
|
||||
) || $isActive(`/database/new`)}"
|
||||
on:click="{() =>
|
||||
$goto(
|
||||
`/database/${name}/configuration`,
|
||||
)}"
|
||||
class:text-yellow-400="{$isActive(`/database/${name}/configuration`) ||
|
||||
$isActive(`/application/new`)}"
|
||||
class:bg-warmGray-700="{$isActive(`/database/${name}/configuration`) ||
|
||||
$isActive(`/database/new`)}"
|
||||
on:click="{() => $goto(`/database/${name}/configuration`)}"
|
||||
>
|
||||
<svg
|
||||
class="w-6"
|
||||
|
@ -52,10 +52,10 @@
|
||||
</h2>
|
||||
<div class="text-center py-10">
|
||||
{#if !$loggedIn}
|
||||
<button class="text-white bg-warmGray-700 hover:bg-warmGray-600 rounded p-2 px-10 font-bold" on:click="{login}">Login with Github</button
|
||||
<button class="text-white bg-warmGray-800 hover:bg-warmGray-700 rounded p-2 px-10 font-bold" on:click="{login}">Login with Github</button
|
||||
>
|
||||
{:else}
|
||||
<button class="text-white bg-warmGray-700 hover:bg-warmGray-600 rounded p-2 px-10 font-bold" on:click="{() => $goto('/dashboard/applications')}"
|
||||
<button class="text-white bg-warmGray-800 hover:bg-warmGray-700 rounded p-2 px-10 font-bold" on:click="{() => $goto('/dashboard/applications')}"
|
||||
>Get Started</button
|
||||
>
|
||||
{/if}
|
||||
|
@ -1,81 +1,74 @@
|
||||
<script>
|
||||
import { params, goto, isActive, redirect, url } from "@roxi/routify";
|
||||
import { fetch } from "@store";
|
||||
import { toast } from "@zerodevx/svelte-toast";
|
||||
import Tooltip from "../../../components/Tooltip/Tooltip.svelte";
|
||||
|
||||
$: name = $params.name
|
||||
|
||||
async function removeService() {
|
||||
await $fetch(`/api/v1/services/${name}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
toast.push("Service removed.");
|
||||
$redirect(`/dashboard/services`);
|
||||
}
|
||||
</script>
|
||||
|
||||
import { params, goto, isActive, redirect } from "@roxi/routify";
|
||||
import { fetch } from "@store";
|
||||
import { toast } from "@zerodevx/svelte-toast";
|
||||
import Tooltip from "../../../components/Tooltip/Tooltip.svelte";
|
||||
|
||||
<nav
|
||||
class="flex text-white justify-end items-center m-4 fixed right-0 top-0 space-x-4"
|
||||
$: name = $params.name;
|
||||
|
||||
async function removeService() {
|
||||
await $fetch(`/api/v1/services/${name}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
toast.push("Service removed.");
|
||||
$redirect(`/dashboard/services`);
|
||||
}
|
||||
</script>
|
||||
|
||||
<nav
|
||||
class="flex text-white justify-end items-center m-4 fixed right-0 top-0 space-x-4"
|
||||
>
|
||||
<Tooltip position="bottom" label="Delete">
|
||||
<button
|
||||
title="Delete"
|
||||
class="icon hover:text-red-500"
|
||||
on:click="{removeService}"
|
||||
>
|
||||
<Tooltip position="bottom" label="Delete" >
|
||||
<button
|
||||
title="Delete"
|
||||
class="icon hover:text-red-500"
|
||||
on:click="{removeService}"
|
||||
<svg
|
||||
class="w-6"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<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="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
<div class="border border-warmGray-700 h-8"></div>
|
||||
<Tooltip position="bottom-left" label="Configuration" >
|
||||
<button
|
||||
class="icon hover:text-yellow-400"
|
||||
disabled="{$isActive(`/application/new`)}"
|
||||
class:text-yellow-400="{$isActive(
|
||||
`/service/${name}/configuration`,
|
||||
) || $isActive(`/application/new`)}"
|
||||
class:bg-warmGray-700="{$isActive(
|
||||
`/service/${name}/configuration`,
|
||||
) || $isActive(`/application/new`)}"
|
||||
on:click="{() =>
|
||||
$goto(
|
||||
`/service/${name}/configuration`,
|
||||
)}"
|
||||
>
|
||||
<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 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
</nav>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
<div class="border border-warmGray-700 h-8"></div>
|
||||
<Tooltip position="bottom-left" label="Configuration">
|
||||
<button
|
||||
class="icon hover:text-yellow-400"
|
||||
disabled="{$isActive(`/application/new`)}"
|
||||
class:text-yellow-400="{$isActive(`/service/${name}/configuration`) ||
|
||||
$isActive(`/application/new`)}"
|
||||
class:bg-warmGray-700="{$isActive(`/service/${name}/configuration`) ||
|
||||
$isActive(`/application/new`)}"
|
||||
on:click="{() => $goto(`/service/${name}/configuration`)}"
|
||||
>
|
||||
<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 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
</nav>
|
||||
|
||||
<div class="text-white">
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<div class="text-white">
|
||||
<slot />
|
||||
</div>
|
||||
|
@ -61,7 +61,7 @@
|
||||
<div class="space-y-2 max-w-4xl mx-auto px-6" in:fade="{{ duration: 100 }}">
|
||||
<div class="block text-center py-4">
|
||||
{#if name === "plausible"}
|
||||
<Plausible {service}/>
|
||||
<Plausible service="{service}" />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,8 +1,7 @@
|
||||
<script>
|
||||
import { params, goto, isActive, redirect, url } from "@roxi/routify";
|
||||
import { params, redirect } from "@roxi/routify";
|
||||
import { fetch, newService, initialNewService } from "@store";
|
||||
import { toast } from "@zerodevx/svelte-toast";
|
||||
import Tooltip from "../../../../components/Tooltip/Tooltip.svelte";
|
||||
import { onDestroy } from "svelte";
|
||||
import Loading from "../../../../components/Loading.svelte";
|
||||
$: type = $params.type;
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script>
|
||||
import { isActive, redirect, goto } from "@roxi/routify/runtime";
|
||||
import { isActive, goto } from "@roxi/routify/runtime";
|
||||
import { fade } from "svelte/transition";
|
||||
</script>
|
||||
|
||||
|
@ -38,10 +38,10 @@
|
||||
<div in:fade="{{ duration: 100 }}">
|
||||
<div class="max-w-4xl mx-auto px-6 pb-4">
|
||||
<div>
|
||||
<div class="text-2xl font-bold py-4 border-gradient w-32 text-white">General</div>
|
||||
<div class="divide-y divide-gray-200">
|
||||
<div class="text-2xl font-bold border-gradient w-32 pt-4 text-white">General</div>
|
||||
<div class=" pt-4">
|
||||
<div class="px-4 sm:px-6">
|
||||
<ul class="mt-2 divide-y divide-gray-200">
|
||||
<ul class="mt-2 divide-y divide-warmGray-800">
|
||||
<li class="py-4 flex items-center justify-between">
|
||||
<div class="flex flex-col">
|
||||
<p class="text-base font-bold text-warmGray-100">
|
||||
|
16
src/store.js
16
src/store.js
@ -43,7 +43,8 @@ export const fetch = writable(
|
||||
if (body) {
|
||||
config.body = JSON.stringify(body)
|
||||
}
|
||||
const response = await waitAtLeast(350, window.fetch(url, config))
|
||||
// const response = await waitAtLeast(350, window.fetch(url, config))
|
||||
const response = await window.fetch(url, config)
|
||||
if (response.status >= 200 && response.status <= 299) {
|
||||
if (response.headers.get('content-type').match(/application\/json/)) {
|
||||
return await response.json()
|
||||
@ -77,10 +78,17 @@ export const fetch = writable(
|
||||
}
|
||||
}
|
||||
)
|
||||
export const activePage = writable({
|
||||
application: null,
|
||||
new: false,
|
||||
mainmenu: null
|
||||
})
|
||||
export const session = writable(sessionStore)
|
||||
export const loggedIn = derived(session, ($session) => {
|
||||
return $session.token
|
||||
})
|
||||
export const githubRepositories = writable([])
|
||||
export const githubInstallations = writable({})
|
||||
export const savedBranch = writable()
|
||||
|
||||
export const dateOptions = readable({
|
||||
@ -93,7 +101,7 @@ export const dateOptions = readable({
|
||||
hour12: false
|
||||
})
|
||||
|
||||
export const deployments = writable({})
|
||||
export const deployments = writable([])
|
||||
|
||||
export const initConf = writable({})
|
||||
export const application = writable({
|
||||
@ -149,8 +157,8 @@ export const initialApplication = {
|
||||
},
|
||||
repository: {
|
||||
id: null,
|
||||
organization: 'new',
|
||||
name: 'start',
|
||||
organization: null,
|
||||
name: null,
|
||||
branch: null
|
||||
},
|
||||
general: {
|
||||
|
@ -4,23 +4,29 @@ const defaultBuildAndDeploy = {
|
||||
}
|
||||
|
||||
const templates = {
|
||||
svelte: {
|
||||
pack: 'svelte',
|
||||
...defaultBuildAndDeploy,
|
||||
directory: 'public',
|
||||
name: 'Svelte'
|
||||
},
|
||||
next: {
|
||||
pack: 'nodejs',
|
||||
pack: 'nextjs',
|
||||
...defaultBuildAndDeploy,
|
||||
port: 3000,
|
||||
name: 'Next.js'
|
||||
name: 'NextJS'
|
||||
},
|
||||
nuxt: {
|
||||
pack: 'nodejs',
|
||||
pack: 'nuxtjs',
|
||||
...defaultBuildAndDeploy,
|
||||
port: 3000,
|
||||
name: 'Nuxt'
|
||||
name: 'NuxtJS'
|
||||
},
|
||||
'react-scripts': {
|
||||
pack: 'static',
|
||||
pack: 'react',
|
||||
...defaultBuildAndDeploy,
|
||||
directory: 'build',
|
||||
name: 'Create React'
|
||||
name: 'React'
|
||||
},
|
||||
'parcel-bundler': {
|
||||
pack: 'static',
|
||||
@ -29,22 +35,22 @@ const templates = {
|
||||
name: 'Parcel'
|
||||
},
|
||||
'@vue/cli-service': {
|
||||
pack: 'static',
|
||||
pack: 'vuejs',
|
||||
...defaultBuildAndDeploy,
|
||||
directory: 'dist',
|
||||
name: 'Vue CLI'
|
||||
name: 'Vue'
|
||||
},
|
||||
gatsby: {
|
||||
pack: 'static',
|
||||
pack: 'gatsby',
|
||||
...defaultBuildAndDeploy,
|
||||
directory: 'public',
|
||||
name: 'Gatsby'
|
||||
},
|
||||
'preact-cli': {
|
||||
pack: 'static',
|
||||
pack: 'react',
|
||||
...defaultBuildAndDeploy,
|
||||
directory: 'build',
|
||||
name: 'Preact CLI'
|
||||
name: 'Preact'
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ module.exports = {
|
||||
],
|
||||
preserveHtmlElements: true,
|
||||
options: {
|
||||
safelist: [/svelte-/, 'border-green-500', 'border-yellow-300', 'border-red-500', 'hover:border-green-500', 'hover:border-red-200', 'hover:bg-red-200'],
|
||||
safelist: [/svelte-/, 'border-green-500', 'border-yellow-300', 'border-red-500', 'hover:border-green-500', 'hover:border-red-200', 'hover:bg-red-200', 'hover:bg-warmGray-900', 'hover:bg-transparent'],
|
||||
defaultExtractor: (content) => {
|
||||
// WARNING: tailwindExtractor is internal tailwind api
|
||||
// if this breaks after a tailwind update, report to svite repo
|
||||
|
Loading…
Reference in New Issue
Block a user