Skip to content

Commit

Permalink
initial production release 🎉
Browse files Browse the repository at this point in the history
  • Loading branch information
andrasbacsai committed Mar 24, 2021
0 parents commit dbe82b3
Show file tree
Hide file tree
Showing 101 changed files with 12,479 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
dist
.routify
35 changes: 35 additions & 0 deletions .env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
####################################
# Domain where your Coolify instance will be available and reachable.
# It's the same as you set in Github OAuth App and Github App as <domain>.
DOMAIN=
## Let's Encrypt contact email required
EMAIL=

# JWT Token Sign Key for logging you in to Coolify's frontend
JWT_SIGN_KEY=
# Encryption key for SECRETS - do NOT share it with others!
SECRETS_ENCRYPTION_KEY=

# Docker Engine
DOCKER_ENGINE=/var/run/docker.sock
# Docker network to use internally between the proxy and your apps
DOCKER_NETWORK=coollabs

# Mongodb
# Values in case if you are using our Mongodb installation - CHANGE user and password fields!
MONGODB_HOST=coollabs-mongodb
MONGODB_PORT=27017
MONGODB_USER=supercooldbuser
MONGODB_PASSWORD=developmentPassword4db
MONGODB_DB=coolLabs-prod

# Frontend only variables
VITE_GITHUB_APP_CLIENTID=
VITE_GITHUB_APP_NAME=

# Github OAuth & App secrets and private key - you can get it from Github.
GITHUB_APP_CLIENT_SECRET=
GITHUP_APP_WEBHOOK_SECRET=

# It should look like this. Newlines breaks with \n
GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA7Y+Uwkd8FINSwFktWGdtwCaOAazTDYR8ucEzGyR9r+ooJZhF\nOc32qgDSps6Q5DsqPOzvfhiviqU+et9VF+bJhfdzwJ+Le86QZH1RgsDMoY049XvI\nKSwP........"
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.vscode
.idea
node_modules
dist
dist-ssr
.routify
.env
yarn-error.log
api/development/console.log
.pnpm-debug.log
14 changes: 14 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"arrowParens": "avoid",
"bracketSpacing": true,
"printWidth": 80,
"semi": true,
"singleQuote": false,
"tabWidth": 2,
"trailingComma": "all",
"svelteSortOrder" : "styles-scripts-markup",
"svelteStrictMode": true,
"svelteBracketNewLine": true,
"svelteAllowShorthand": true,
"plugins": ["prettier-plugin-svelte"]
}
661 changes: 661 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

97 changes: 97 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# About

https://andrasbacsai.com/farewell-netlify-and-heroku-after-3-days-of-coding

# Features
- Deploy your Node.js and static sites just by pushing code to git.
- Hassle-free installation and upgrade process.
- One-click MongoDB, MySQL, PostgreSQL, CouchDB deployments!

# Upcoming features
- Backups & monitoring.
- User analytics with privacy in mind.
- And much more (see [Roadmap](https://github.com/coollabsio/coolify/projects/1)).


# FAQ
Q: What does Buildpack means?

A: It defines your application's final form. Static means that it will be hosted as a static site in the end. (see next question below 👇)

---

Q: How can I build a static site, like Next.js, Sapper (prerendered), etc ?

A: Use `static` builder and set your `Build command`.

# Screenshots

[Login](https://coollabs.io/coolify/login.jpg)

[Applications](https://coollabs.io/coolify/applications.jpg)

[Databases](https://coollabs.io/coolify/databases.jpg)

[Configuration](https://coollabs.io/coolify/configuration.jpg)

[Settings](https://coollabs.io/coolify/settings.jpg)

[Logs](https://coollabs.io/coolify/logs.jpg)

# Getting Started

Automatically: `sh <(curl -fsSL https://get.coollabs.io/install.sh) coolify`

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

Subscribe to events:
- Push -> Check!

### Installation
- Clone this repository: `git clone [email protected]:coollabsio/coolify.git`
- Set `.env` (see `.env.template`)
- Installation: `bash install.sh all`

## Updating process
### Update everything (proxy+coolify)
- `bash install.sh all`

### Update coolify only
- `bash install.sh coolify`

### Update proxy only
- `bash install.sh proxy`

# Contact
- Twitter: [@andrasbacsai](https://twitter.com/andrasbacsai)
- Telegram: [@andrasbacsai](https://t.me/andrasbacsai)
- Email: [[email protected]](mailto:[email protected])

# 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.
27 changes: 27 additions & 0 deletions api/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module.exports = async function (fastify, opts) {
// Private routes
fastify.register(async function (server) {
if (process.env.NODE_ENV === 'production') server.register(require('./plugins/authentication'))
server.register(require('./routes/v1/upgrade'), { prefix: '/upgrade' })
server.register(require('./routes/v1/settings'), { prefix: '/settings' })
server.register(require('./routes/v1/dashboard'), { prefix: '/dashboard' })
server.register(require('./routes/v1/config'), { prefix: '/config' })
server.register(require('./routes/v1/application/remove'), { prefix: '/application/remove' })
server.register(require('./routes/v1/application/logs'), { prefix: '/application/logs' })
server.register(require('./routes/v1/application/check'), { prefix: '/application/check' })
server.register(require('./routes/v1/application/deploy'), { prefix: '/application/deploy' })
server.register(require('./routes/v1/application/deploy/logs'), { prefix: '/application/deploy/logs' })
server.register(require('./routes/v1/databases'), { prefix: '/databases' })
})
// Public routes
fastify.register(require('./routes/v1/verify'), { prefix: '/verify' })
fastify.register(require('./routes/v1/login/github'), {
prefix: '/login/github'
})
fastify.register(require('./routes/v1/webhooks/deploy'), {
prefix: '/webhooks/deploy'
})
fastify.register(require('./routes/v1/undead'), {
prefix: '/undead'
})
}
36 changes: 36 additions & 0 deletions api/development/mongodb.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const mongoose = require('mongoose')
const { MongoMemoryServer } = require('mongodb-memory-server-core')

const mongoServer = new MongoMemoryServer({
instance: {
port: 27017,
dbName: 'coolify',
storageEngine: 'wiredTiger'
},
binary: {
version: '4.4.3'

}
})

mongoose.Promise = Promise
mongoServer.getUri().then((mongoUri) => {
const mongooseOpts = {
useNewUrlParser: true,
useUnifiedTopology: true
}

mongoose.connect(mongoUri, mongooseOpts)

mongoose.connection.on('error', (e) => {
if (e.message.code === 'ETIMEDOUT') {
console.log(e)
mongoose.connect(mongoUri, mongooseOpts)
}
console.log(e)
})

mongoose.connection.once('open', () => {
console.log(`Started in-memory mongodb ${mongoUri}`)
})
})
34 changes: 34 additions & 0 deletions api/libs/applications/build/container.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const packs = require('../../../packs')
const { saveAppLog } = require('../../logging')
const Deployment = require('../../../models/Deployment')

module.exports = async function (configuration) {
const { id, organization, name, branch } = configuration.repository
const { domain } = configuration.publish
const deployId = configuration.general.deployId

const execute = packs[configuration.build.pack]
if (execute) {
try {
await Deployment.findOneAndUpdate(
{ repoId: id, branch, deployId, organization, name, domain },
{ repoId: id, branch, deployId, organization, name, domain, progress: 'inprogress' })
await saveAppLog('### Building application.', configuration)

await execute(configuration)

await saveAppLog('### Building done.', configuration)
} catch (error) {
await Deployment.findOneAndUpdate(
{ repoId: id, branch, deployId, organization, name, domain },
{ repoId: id, branch, deployId, organization, name, domain, progress: 'failed' })
if (error.stack) throw { error: error.stack, type: 'server' }
throw { error, type: 'app' }
}
} else {
await Deployment.findOneAndUpdate(
{ repoId: id, branch, deployId, organization, name, domain },
{ repoId: id, branch, deployId, organization, name, domain, progress: 'failed' })
throw { error: 'No buildpack found.', type: 'app' }
}
}
41 changes: 41 additions & 0 deletions api/libs/applications/cleanup/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const { docker } = require('../../docker')
const { execShellAsync, delay } = require('../../common')
const Deployment = require('../../../models/Deployment')

async function purgeOldThings () {
try {
await docker.engine.pruneImages()
await docker.engine.pruneContainers()
} catch (error) {
throw { error, type: 'server' }
}
}

async function cleanup (configuration) {
const { id } = configuration.repository
const deployId = configuration.general.deployId
try {
// Cleanup stucked deployments.
const deployments = await Deployment.find({ repoId: id, deployId: { $ne: deployId }, progress: { $in: ['queued', 'inprogress'] } })
for (const deployment of deployments) {
await Deployment.findByIdAndUpdate(deployment._id, { $set: { progress: 'failed' } })
}
} catch (error) {
throw { error, type: 'server' }
}
}

async function deleteSameDeployments (configuration) {
try {
await (await docker.engine.listServices()).filter(r => r.Spec.Labels.managedBy === 'coolify' && r.Spec.Labels.type === 'application').map(async s => {
const running = JSON.parse(s.Spec.Labels.configuration)
if (running.repository.id === configuration.repository.id && running.repository.branch === configuration.repository.branch) {
await execShellAsync(`docker stack rm ${s.Spec.Labels['com.docker.stack.namespace']}`)
}
})
} catch (error) {
throw { error, type: 'server' }
}
}

module.exports = { cleanup, deleteSameDeployments, purgeOldThings }
62 changes: 62 additions & 0 deletions api/libs/applications/configuration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
const { uniqueNamesGenerator, adjectives, colors, animals } = require('unique-names-generator')
const cuid = require('cuid')
const { execShellAsync } = require('../common')
const crypto = require('crypto')

function getUniq () {
return uniqueNamesGenerator({ dictionaries: [adjectives, animals, colors], length: 2 })
}

function setDefaultConfiguration (configuration) {
try {
const nickname = getUniq()
const deployId = cuid()

const shaBase = JSON.stringify({ repository: configuration.repository })
const sha256 = crypto.createHash('sha256').update(shaBase).digest('hex')

configuration.build.container.name = sha256.slice(0, 15)

configuration.general.nickname = nickname
configuration.general.deployId = deployId
configuration.general.workdir = `/tmp/${deployId}`

if (!configuration.publish.path) configuration.publish.path = '/'
if (!configuration.publish.port) configuration.publish.port = configuration.build.pack === 'static' ? 80 : 3000

if (configuration.build.pack === 'static') {
if (!configuration.build.command.installation) configuration.build.command.installation = 'yarn install'
if (!configuration.build.directory) configuration.build.directory = '/'
}

if (configuration.build.pack === 'nodejs') {
if (!configuration.build.command.installation) configuration.build.command.installation = 'yarn install'
if (!configuration.build.directory) configuration.build.directory = '/'
}

return configuration
} catch (error) {
throw { error, type: 'server' }
}
}

async function updateServiceLabels (configuration, services) {
// In case of any failure during deployment, still update the current configuration.
const found = services.find(s => {
const config = JSON.parse(s.Spec.Labels.configuration)
if (config.repository.id === configuration.repository.id && config.repository.branch === configuration.repository.branch) {
return config
}
return null
})
if (found) {
const { ID } = found
try {
const Labels = { ...JSON.parse(found.Spec.Labels.configuration), ...configuration }
execShellAsync(`docker service update --label-add configuration='${JSON.stringify(Labels)}' --label-add com.docker.stack.image='${configuration.build.container.name}:${configuration.build.container.tag}' ${ID}`)
} catch (error) {
console.log(error)
}
}
}
module.exports = { setDefaultConfiguration, updateServiceLabels }
Loading

0 comments on commit dbe82b3

Please sign in to comment.