mirror of
https://github.com/caprover/caprover
synced 2026-09-26 16:45:39 +00:00
+11
-3
@@ -1,12 +1,20 @@
|
||||
## [Next Version - available as `edge`]
|
||||
|
||||
- TBD
|
||||
- TBD.
|
||||
|
||||
## [1.9.0] - 2021-02-15
|
||||
|
||||
- New: Automatically adding Docker Hub credentials to the public images to improve limit rate [Issue-906](https://github.com/caprover/caprover/issues/906)
|
||||
- New: Dark mode is added [PR-62](https://github.com/caprover/caprover-frontend/pull/62)
|
||||
- New: Open in New Window functionality is added [PR-63](https://github.com/caprover/caprover-frontend/pull/63)
|
||||
- Fixed: Enable https on app only works if https is enabled on dashboard [Issue-998](https://github.com/caprover/caprover/issues/998)
|
||||
- Update: npm modules updated to the latest versions.
|
||||
|
||||
## [1.8.2] - 2020-11-02
|
||||
|
||||
- Fixed: The following frontend bugs didn't make it to 1.8.1 due to a release mistake
|
||||
- Fixed: The following frontend bug fixes didn't make it to 1.8.1 due to a release mistake
|
||||
- Fixed: Logout did not clear cookies. [issue-810](https://github.com/caprover/caprover/issues/810)
|
||||
- Fixed: Creating a new app while another app is building breaks the UI [Issue-56](https://github.com/caprover/caprover-frontend/
|
||||
- Fixed: Creating a new app while another app is building breaks the UI [Issue-56](https://github.com/caprover/caprover-frontend/issues/56)
|
||||
|
||||
## [1.8.1] - 2020-10-31
|
||||
|
||||
|
||||
@@ -1,15 +1,51 @@
|
||||
### CapRover [](https://hub.docker.com/r/caprover/caprover/) [](https://opencollective.com/caprover#backer) [](https://github.com/caprover/caprover/releases)
|
||||
<div align="center">
|
||||
<h1>CapRover</h1>
|
||||
<a href="https://hub.docker.com/r/caprover/caprover/" target="_blank" title="Docker Pulls">
|
||||
<img src="https://img.shields.io/docker/pulls/caprover/caprover.svg" alt="Docker Pulls"/>
|
||||
</a>
|
||||
<a href="https://opencollective.com/caprover#backer" target="_blank" title="Open Collective backers and sponsors">
|
||||
<img src="https://img.shields.io/opencollective/all/caprover" alt="Open Collective backers and sponsors"/>
|
||||
</a>
|
||||
<a href="https://github.com/caprover/caprover/releases" target="_blank" title="GitHub release (latest by date)">
|
||||
<img src="https://img.shields.io/github/v/release/caprover/caprover" alt="GitHub release (latest by date)"/>
|
||||
</a>
|
||||
|
||||
#### Easiest app/database deployment platform and webserver package for your NodeJS, Python, PHP, Ruby, Go applications. No Docker, nginx knowledge required! [](https://twitter.com/intent/tweet?url=https%3A%2F%2Fgithub.com%2Fcaprover%2Fcaprover&via=cap_rover&text=I%20found%20the%20easiest%20webserver%20package%20for%20NodeJS%2C%20PHP%2C%20MySQL%2C%20WordPress%20and%20everything%21&hashtags=CapRover%2Cnodejs%2Cdocker%2Cnginx%2Cwebdev)
|
||||
Easiest app/database deployment platform and webserver package for your NodeJS, Python, PHP, Ruby, Go applications.
|
||||
|
||||
[](https://youtu.be/VPHEXPfsvyQ)
|
||||
No Docker, nginx knowledge required!
|
||||
|
||||
[](https://twitter.com/intent/tweet?url=https%3A%2F%2Fgithub.com%2Fcaprover%2Fcaprover&via=cap_rover&text=I%20found%20the%20easiest%20webserver%20package%20for%20NodeJS%2C%20PHP%2C%20MySQL%2C%20WordPress%20and%20everything%21&hashtags=CapRover%2Cnodejs%2Cdocker%2Cnginx%2Cwebdev)
|
||||
|
||||
<a href="https://youtu.be/VPHEXPfsvyQ" target="_blank" title="YouTube">
|
||||
<img src="https://raw.githubusercontent.com/caprover/caprover-website/master/graphics/screenshots-video-small.gif" alt="YouTube"/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
## What's this?
|
||||
|
||||
CapRover is an extremely easy to use app/database deployment & web server manager for your **NodeJS, Python, PHP, ASP.NET, Ruby, MySQL, MongoDB, Postgres, WordPress (and etc...)** applications!
|
||||
|
||||
It's blazingly fast and very robust as it uses Docker, nginx, LetsEncrypt and NetData under the hood behind its simple-to-use interface.
|
||||
|
||||
✔ CLI for automation and scripting
|
||||
|
||||
✔ Web GUI for ease of access and convenience
|
||||
|
||||
✔ No lock-in! Remove CapRover and your apps keep working!
|
||||
|
||||
✔ Docker Swarm under the hood for containerization and clustering
|
||||
|
||||
✔ Nginx (fully customizable template) under the hood for load-balancing
|
||||
|
||||
✔ Let's Encrypt under the hood for free SSL (HTTPS)
|
||||
|
||||
### Seriously! Who should care about CapRover?
|
||||
- A [web] developer who does not like spending hours and days setting up a server, build tools, sending code to server, build it, get an SSL certificate, install it, update nginx over and over again.
|
||||
- A developer who uses expensive services like Heroku, Microsoft Azure and etc. And is interested in reducing their cost by 4x (Heroku charges 25$/month for their 1gb instance, the same server is 5$ on vultr!!)
|
||||
- Someone who prefers to write more of `showResults(getUserList())` and not much of `$ apt-get install libstdc++6 > /dev/null`
|
||||
- A developer who likes installing MySQL, MongoDB and etc on their server by selecting from a dropdown and clicking on install!
|
||||
- How much server/docker/linux knowledge is required to set up a CapRover server? Answer: Knowledge of Copy & Paste!! Head over to "Getting Started" for information on what to copy & paste ;-)
|
||||
|
||||
- A [web] developer who does not like spending hours and days setting up a server, build tools, sending code to server, build it, get an SSL certificate, install it, update nginx over and over again.
|
||||
- A developer who uses expensive services like Heroku, Microsoft Azure and etc. And is interested in reducing their cost by 4x (Heroku charges 25$/month for their 1gb instance, the same server is 5$ on vultr!!)
|
||||
- Someone who prefers to write more of `showResults(getUserList())` and not much of `$ apt-get install libstdc++6 > /dev/null`
|
||||
- A developer who likes installing MySQL, MongoDB and etc on their server by selecting from a dropdown and clicking on install!
|
||||
- How much server/docker/linux knowledge is required to set up a CapRover server? Answer: Knowledge of Copy & Paste!! Head over to "Getting Started" for information on what to copy & paste ;-)
|
||||
|
||||
## Learn More!
|
||||
|
||||
@@ -20,9 +56,8 @@ For more details and documentation, please visit https://CapRover.com/
|
||||
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
|
||||
<a href="https://github.com/caprover/caprover/graphs/contributors"><img src="https://opencollective.com/caprover/contributors.svg?width=690&button=false" /></a>
|
||||
|
||||
|
||||
## Backers
|
||||
|
||||
Thank you to all our backers! 🙏
|
||||
Thank you to all our backers! 🙏
|
||||
|
||||
[](https://opencollective.com/caprover#backer)
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
####################################################################
|
||||
##### Updates all services with registry token
|
||||
####################################################################
|
||||
|
||||
# curl -sSL https://raw.githubusercontent.com/caprover/caprover/master/dev-scripts/add-registry-to-all-services.sh | bash -s --
|
||||
|
||||
|
||||
all_services=$(docker service ls --format {{.Name}})
|
||||
for srv in $all_services
|
||||
do
|
||||
echo "Service: $srv"
|
||||
docker service update --with-registry-auth $srv --label-add "registry-added" --force --detach
|
||||
done
|
||||
@@ -15,7 +15,7 @@ RUN npm install --production && \
|
||||
|
||||
# Build frontend code using a fixed hash commit.
|
||||
|
||||
ENV FRONTEND_COMMIT_HASH 3d5623f1d6b15a1426a3c995605b5e78e852e79b
|
||||
ENV FRONTEND_COMMIT_HASH e2dc2415860d5d1096096f06fc204e6b186110bf
|
||||
|
||||
RUN mkdir -p /usr/src/app-frontend && cd /usr/src/app-frontend && \
|
||||
git clone https://github.com/githubsaturn/caprover-frontend.git && \
|
||||
|
||||
Generated
+1098
-2286
File diff suppressed because it is too large
Load Diff
+28
-28
@@ -16,60 +16,60 @@
|
||||
"@types/bcryptjs": "^2.4.2",
|
||||
"@types/configstore": "^4.0.0",
|
||||
"@types/cookie-parser": "^1.4.2",
|
||||
"@types/dockerode": "^2.5.34",
|
||||
"@types/ejs": "^3.0.4",
|
||||
"@types/express": "^4.17.7",
|
||||
"@types/fs-extra": "^9.0.1",
|
||||
"@types/http-proxy": "^1.17.4",
|
||||
"@types/jest": "^26.0.10",
|
||||
"@types/dockerode": "^3.2.2",
|
||||
"@types/ejs": "^3.0.5",
|
||||
"@types/express": "^4.17.11",
|
||||
"@types/fs-extra": "^9.0.7",
|
||||
"@types/http-proxy": "^1.17.5",
|
||||
"@types/jest": "^26.0.20",
|
||||
"@types/js-base64": "^3.0.0",
|
||||
"@types/jsonwebtoken": "^8.5.0",
|
||||
"@types/morgan": "^1.9.1",
|
||||
"@types/multer": "^1.4.4",
|
||||
"@types/morgan": "^1.9.2",
|
||||
"@types/multer": "^1.4.5",
|
||||
"@types/on-finished": "^2.3.1",
|
||||
"@types/request": "^2.48.5",
|
||||
"@types/require-from-string": "^1.2.0",
|
||||
"@types/serve-favicon": "^2.5.0",
|
||||
"@types/ssh2": "^0.5.44",
|
||||
"@types/tar": "^4.0.3",
|
||||
"@types/serve-favicon": "^2.5.2",
|
||||
"@types/ssh2": "^0.5.46",
|
||||
"@types/tar": "^4.0.4",
|
||||
"@types/uuid": "^8.3.0",
|
||||
"@types/validator": "^13.1.0",
|
||||
"axios": "^0.19.2",
|
||||
"@types/validator": "^13.1.3",
|
||||
"axios": "^0.21.1",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"body-parser": "^1.19.0",
|
||||
"configstore": "^5.0.1",
|
||||
"cookie-parser": "~1.4.5",
|
||||
"debug": "~4.1.1",
|
||||
"debug": "~4.3.1",
|
||||
"dockerode": "^3.2.1",
|
||||
"ejs": "^3.1.3",
|
||||
"ejs": "^3.1.6",
|
||||
"express": "^4.17.1",
|
||||
"fs-extra": "^9.0.1",
|
||||
"fs-extra": "^9.1.0",
|
||||
"http-proxy": "^1.18.1",
|
||||
"is-valid-path": "^0.1.1",
|
||||
"js-base64": "^3.4.5",
|
||||
"js-base64": "^3.6.0",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"madge": "^3.9.2",
|
||||
"moment": "^2.27.0",
|
||||
"madge": "^4.0.0",
|
||||
"moment": "^2.29.1",
|
||||
"morgan": "^1.10.0",
|
||||
"multer": "^1.4.2",
|
||||
"on-finished": "^2.3.0",
|
||||
"prettier": "^2.0.5",
|
||||
"public-ip": "^4.0.2",
|
||||
"prettier": "^2.2.1",
|
||||
"public-ip": "^4.0.3",
|
||||
"recursive-readdir": "^2.2.2",
|
||||
"request": "^2.88.2",
|
||||
"require-from-string": "^2.0.2",
|
||||
"serve-favicon": "~2.5.0",
|
||||
"simple-git": "^2.17.0",
|
||||
"simple-git": "^2.34.2",
|
||||
"ssh2": "^0.8.9",
|
||||
"tar": "^6.0.5",
|
||||
"tar": "^6.1.0",
|
||||
"tslint": "^6.1.3",
|
||||
"typescript": "^3.9.7",
|
||||
"uuid": "^8.3.0",
|
||||
"validator": "^13.1.1",
|
||||
"typescript": "^4.1.5",
|
||||
"uuid": "^8.3.2",
|
||||
"validator": "^13.5.2",
|
||||
"yaml": "^1.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jest": "^26.4.0",
|
||||
"ts-jest": "^26.2.0"
|
||||
"jest": "^26.6.3",
|
||||
"ts-jest": "^26.5.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ class DataStore {
|
||||
insertOneClickBaseUrl(url: string) {
|
||||
const self = this
|
||||
|
||||
return new Promise<string>(function (resolve, reject) {
|
||||
return new Promise<void>(function (resolve, reject) {
|
||||
const parsedArray = JSON.parse(
|
||||
self.data.get(CUSTOM_ONE_CLICK_APP_URLS) || '[]'
|
||||
) as string[]
|
||||
@@ -284,7 +284,7 @@ class DataStore {
|
||||
deleteOneClickBaseUrl(url: string) {
|
||||
const self = this
|
||||
|
||||
return new Promise<string>(function (resolve, reject) {
|
||||
return new Promise<void>(function (resolve, reject) {
|
||||
const parsedArray = JSON.parse(
|
||||
self.data.get(CUSTOM_ONE_CLICK_APP_URLS) || '[]'
|
||||
) as string[]
|
||||
|
||||
+24
-5
@@ -159,7 +159,7 @@ class DockerApi {
|
||||
return self.dockerode.listServices()
|
||||
})
|
||||
.then(function (services) {
|
||||
return (services || []) as DockerService[]
|
||||
return ((services || []) as unknown) as DockerService[]
|
||||
})
|
||||
}
|
||||
|
||||
@@ -241,7 +241,8 @@ class DockerApi {
|
||||
newVersionNumber: number,
|
||||
tarballFilePath: string,
|
||||
buildLogs: BuildLog,
|
||||
envVars: IAppEnvVar[]
|
||||
envVars: IAppEnvVar[],
|
||||
registryConfig: DockerRegistryConfig
|
||||
) {
|
||||
const self = this
|
||||
|
||||
@@ -257,10 +258,28 @@ class DockerApi {
|
||||
buildargs[env.key] = env.value
|
||||
})
|
||||
|
||||
return self.dockerode.buildImage(tarballFilePath, {
|
||||
if (envVars.length > 0) {
|
||||
buildLogs.log(
|
||||
'Ignore warnings for unconsumed build-args if there is any'
|
||||
)
|
||||
}
|
||||
|
||||
const optionsForBuild = {
|
||||
t: imageName,
|
||||
buildargs: buildargs,
|
||||
})
|
||||
} as any
|
||||
|
||||
if (Object.keys(registryConfig).length > 0) {
|
||||
// https://github.com/apocas/dockerode/blob/ed6ef39e0fc81963fedf208c7e0854d8a44cb9a8/lib/docker.js#L271-L274
|
||||
// https://github.com/apocas/docker-modem/blob/master/lib/modem.js#L160-L163
|
||||
// "X-Registry-Config" in docker API
|
||||
optionsForBuild['registryconfig'] = registryConfig
|
||||
}
|
||||
|
||||
return self.dockerode.buildImage(
|
||||
tarballFilePath,
|
||||
optionsForBuild as {}
|
||||
)
|
||||
})
|
||||
.then(function (stream) {
|
||||
return new Promise<void>(function (resolve, reject) {
|
||||
@@ -956,7 +975,7 @@ class DockerApi {
|
||||
ensureSecretOnService(serviceName: string, secretName: string) {
|
||||
const self = this
|
||||
|
||||
let secretToExpose: Docker.SecretInfo
|
||||
let secretToExpose: Docker.Secret
|
||||
|
||||
return self.dockerode
|
||||
.listSecrets({
|
||||
|
||||
+16
-13
@@ -58,20 +58,23 @@ export default class DockerUtils {
|
||||
let hasExisted = false
|
||||
|
||||
stream
|
||||
.on('close', function (
|
||||
code: string,
|
||||
signal: string
|
||||
) {
|
||||
Logger.d(
|
||||
`Stream :: close :: code: ${code}, signal: ${signal}`
|
||||
)
|
||||
conn.end()
|
||||
if (hasExisted) {
|
||||
return
|
||||
.on(
|
||||
'close',
|
||||
function (
|
||||
code: string,
|
||||
signal: string
|
||||
) {
|
||||
Logger.d(
|
||||
`Stream :: close :: code: ${code}, signal: ${signal}`
|
||||
)
|
||||
conn.end()
|
||||
if (hasExisted) {
|
||||
return
|
||||
}
|
||||
hasExisted = true
|
||||
resolve()
|
||||
}
|
||||
hasExisted = true
|
||||
resolve()
|
||||
})
|
||||
)
|
||||
.on('data', function (data: string) {
|
||||
Logger.d(`STDOUT: ${data}`)
|
||||
})
|
||||
|
||||
@@ -4,3 +4,22 @@ interface DockerAuthObj {
|
||||
password: string
|
||||
// email: string // Email is optional in auth
|
||||
}
|
||||
|
||||
/*
|
||||
{
|
||||
"docker.example.com": {
|
||||
"username": "janedoe",
|
||||
"password": "hunter2"
|
||||
},
|
||||
"https://index.docker.io/v1/": {
|
||||
"username": "mobydock",
|
||||
"password": "conta1n3rize14"
|
||||
}
|
||||
}
|
||||
*/
|
||||
interface DockerRegistryConfig {
|
||||
[serveraddress: string]: {
|
||||
username: string
|
||||
password: string
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ export interface Mount {
|
||||
Target: string
|
||||
}
|
||||
|
||||
export interface ContainerSpec {
|
||||
export interface ContainerSpecType {
|
||||
Image: string
|
||||
Env: string[]
|
||||
Mounts: Mount[]
|
||||
@@ -33,7 +33,7 @@ export interface LogDriver {
|
||||
}
|
||||
|
||||
export interface TaskTemplate {
|
||||
ContainerSpec: ContainerSpec
|
||||
ContainerSpec: ContainerSpecType
|
||||
Resources: Resources
|
||||
Placement: Placement
|
||||
LogDriver: LogDriver
|
||||
|
||||
@@ -2,6 +2,7 @@ import express = require('express')
|
||||
import ApiStatusCodes from '../../../../api/ApiStatusCodes'
|
||||
import BaseApi from '../../../../api/BaseApi'
|
||||
import InjectionExtractor from '../../../../injection/InjectionExtractor'
|
||||
import Logger from '../../../../utils/Logger'
|
||||
import multer = require('multer')
|
||||
|
||||
const TEMP_UPLOAD = 'temp_upload/'
|
||||
@@ -69,67 +70,75 @@ router.post('/:appName/', function (req, res, next) {
|
||||
.catch(ApiStatusCodes.createCatcher(res))
|
||||
})
|
||||
|
||||
router.post('/:appName/', upload.single('sourceFile'), function (
|
||||
req,
|
||||
res,
|
||||
next
|
||||
) {
|
||||
const serviceManager = InjectionExtractor.extractUserFromInjected(res).user
|
||||
.serviceManager
|
||||
router.post(
|
||||
'/:appName/',
|
||||
upload.single('sourceFile'),
|
||||
function (req, res, next) {
|
||||
const serviceManager = InjectionExtractor.extractUserFromInjected(res)
|
||||
.user.serviceManager
|
||||
|
||||
const appName = req.params.appName
|
||||
const isDetachedBuild = !!req.query.detached
|
||||
const captainDefinitionContent =
|
||||
(req.body.captainDefinitionContent || '') + ''
|
||||
const gitHash = (req.body.gitHash || '') + ''
|
||||
let tarballSourceFilePath: string = !!req.file ? req.file.path : ''
|
||||
const appName = req.params.appName
|
||||
const isDetachedBuild = !!req.query.detached
|
||||
const captainDefinitionContent =
|
||||
(req.body.captainDefinitionContent || '') + ''
|
||||
const gitHash = (req.body.gitHash || '') + ''
|
||||
let tarballSourceFilePath: string = !!req.file ? req.file.path : ''
|
||||
|
||||
if (!!tarballSourceFilePath === !!captainDefinitionContent) {
|
||||
res.send(
|
||||
new BaseApi(
|
||||
ApiStatusCodes.ILLEGAL_OPERATION,
|
||||
'Either tarballfile or captainDefinitionContent should be present.'
|
||||
)
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
Promise.resolve().then(function () {
|
||||
const promiseToDeployNewVer = serviceManager.scheduleDeployNewVersion(
|
||||
appName,
|
||||
{
|
||||
uploadedTarPathSource: !!tarballSourceFilePath
|
||||
? {
|
||||
uploadedTarPath: tarballSourceFilePath,
|
||||
gitHash,
|
||||
}
|
||||
: undefined,
|
||||
captainDefinitionContentSource: !!captainDefinitionContent
|
||||
? {
|
||||
captainDefinitionContent,
|
||||
gitHash,
|
||||
}
|
||||
: undefined,
|
||||
}
|
||||
)
|
||||
|
||||
if (isDetachedBuild) {
|
||||
if (!!tarballSourceFilePath === !!captainDefinitionContent) {
|
||||
res.send(
|
||||
new BaseApi(
|
||||
ApiStatusCodes.STATUS_OK_DEPLOY_STARTED,
|
||||
'Deploy is started'
|
||||
ApiStatusCodes.ILLEGAL_OPERATION,
|
||||
'Either tarballfile or captainDefinitionContent should be present.'
|
||||
)
|
||||
)
|
||||
} else {
|
||||
promiseToDeployNewVer
|
||||
.then(function () {
|
||||
res.send(
|
||||
new BaseApi(ApiStatusCodes.STATUS_OK, 'Deploy is done')
|
||||
)
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res))
|
||||
return
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Promise.resolve().then(function () {
|
||||
const promiseToDeployNewVer = serviceManager.scheduleDeployNewVersion(
|
||||
appName,
|
||||
{
|
||||
uploadedTarPathSource: !!tarballSourceFilePath
|
||||
? {
|
||||
uploadedTarPath: tarballSourceFilePath,
|
||||
gitHash,
|
||||
}
|
||||
: undefined,
|
||||
captainDefinitionContentSource: !!captainDefinitionContent
|
||||
? {
|
||||
captainDefinitionContent,
|
||||
gitHash,
|
||||
}
|
||||
: undefined,
|
||||
}
|
||||
)
|
||||
|
||||
if (isDetachedBuild) {
|
||||
res.send(
|
||||
new BaseApi(
|
||||
ApiStatusCodes.STATUS_OK_DEPLOY_STARTED,
|
||||
'Deploy is started'
|
||||
)
|
||||
)
|
||||
|
||||
// To avoid unhandled promise error
|
||||
promiseToDeployNewVer.catch(function (err) {
|
||||
Logger.e(err)
|
||||
})
|
||||
} else {
|
||||
promiseToDeployNewVer
|
||||
.then(function () {
|
||||
res.send(
|
||||
new BaseApi(
|
||||
ApiStatusCodes.STATUS_OK,
|
||||
'Deploy is done'
|
||||
)
|
||||
)
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res))
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
export default router
|
||||
|
||||
@@ -84,9 +84,10 @@ class DockerRegistryHelper {
|
||||
)
|
||||
})
|
||||
.catch(function (error: AnyError) {
|
||||
return new Promise<
|
||||
void
|
||||
>(function (resolve, reject) {
|
||||
return new Promise<void>(function (
|
||||
resolve,
|
||||
reject
|
||||
) {
|
||||
Logger.e('PUSH FAILED')
|
||||
Logger.e(error)
|
||||
reject(
|
||||
@@ -131,10 +132,59 @@ class DockerRegistryHelper {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if none of the registries explicitly relates to the image name, try the official one
|
||||
for (let index = 0; index < regs.length; index++) {
|
||||
const element = regs[index]
|
||||
if (element.registryDomain === 'registry-1.docker.io') {
|
||||
return {
|
||||
serveraddress: element.registryDomain,
|
||||
username: element.registryUser,
|
||||
password: element.registryPassword,
|
||||
// email: CaptainConstants.defaultEmail, // email is optional
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined
|
||||
})
|
||||
}
|
||||
|
||||
createDockerRegistryConfig(): Promise<DockerRegistryConfig> {
|
||||
const self = this
|
||||
return Promise.resolve() //
|
||||
.then(function () {
|
||||
//
|
||||
return self.getAllRegistries()
|
||||
})
|
||||
.then(function (regs) {
|
||||
let registryConfig: DockerRegistryConfig = {}
|
||||
|
||||
for (let index = 0; index < regs.length; index++) {
|
||||
const element = regs[index]
|
||||
// https://docs.docker.com/engine/api/v1.40/#operation/ImageBuild
|
||||
// For: X-Registry-Config
|
||||
// Only the registry domain name (and port if not the default 443) are required. However,
|
||||
// for legacy reasons, the Docker Hub registry must be specified with both a https:// prefix
|
||||
// and a /v1/ suffix even though Docker will prefer to use the v2 registry API.
|
||||
|
||||
if (element.registryDomain.indexOf('.docker.io') >= 0) {
|
||||
registryConfig['https://index.docker.io/v1/'] = {
|
||||
username: element.registryUser,
|
||||
password: element.registryPassword,
|
||||
}
|
||||
} else {
|
||||
registryConfig[element.registryDomain] = {
|
||||
username: element.registryUser,
|
||||
password: element.registryPassword,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return registryConfig
|
||||
})
|
||||
}
|
||||
|
||||
setDefaultPushRegistry(registryId: string) {
|
||||
const self = this
|
||||
return Promise.resolve().then(function () {
|
||||
|
||||
@@ -250,13 +250,17 @@ export default class ImageMaker {
|
||||
)
|
||||
})
|
||||
.then(function () {
|
||||
return self.dockerRegistryHelper.createDockerRegistryConfig()
|
||||
})
|
||||
.then(function (registryConfig) {
|
||||
return self.dockerApi
|
||||
.buildImageFromDockerFile(
|
||||
baseImageNameWithoutVersionAndReg,
|
||||
appVersion,
|
||||
tarFilePath,
|
||||
self.buildLogsManager.getAppBuildLogs(appName),
|
||||
envVars
|
||||
envVars,
|
||||
registryConfig
|
||||
)
|
||||
.catch(function (error: AnyError) {
|
||||
throw ApiStatusCodes.createError(
|
||||
|
||||
@@ -12,6 +12,9 @@ import DomainResolveChecker from './system/DomainResolveChecker'
|
||||
import LoadBalancerManager from './system/LoadBalancerManager'
|
||||
import requireFromString = require('require-from-string')
|
||||
|
||||
const ERROR_FIRST_ENABLE_ROOT_SSL =
|
||||
'You have to first enable SSL for your root domain'
|
||||
|
||||
const serviceMangerCache = {} as IHashMapGeneric<ServiceManager>
|
||||
|
||||
interface QueuedPromise {
|
||||
@@ -210,6 +213,16 @@ class ServiceManager {
|
||||
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return self.dataStore.getHasRootSsl()
|
||||
})
|
||||
.then(function (rootHasSsl) {
|
||||
if (!rootHasSsl) {
|
||||
throw ApiStatusCodes.createError(
|
||||
ApiStatusCodes.STATUS_ERROR_GENERIC,
|
||||
ERROR_FIRST_ENABLE_ROOT_SSL
|
||||
)
|
||||
}
|
||||
|
||||
Logger.d(`Verifying Captain owns domain: ${customDomain}`)
|
||||
|
||||
return self.domainResolveChecker.verifyCaptainOwnsDomainOrThrow(
|
||||
@@ -320,6 +333,15 @@ class ServiceManager {
|
||||
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return self.dataStore.getHasRootSsl()
|
||||
})
|
||||
.then(function (rootHasSsl) {
|
||||
if (!rootHasSsl) {
|
||||
throw ApiStatusCodes.createError(
|
||||
ApiStatusCodes.STATUS_ERROR_GENERIC,
|
||||
ERROR_FIRST_ENABLE_ROOT_SSL
|
||||
)
|
||||
}
|
||||
return self.verifyCaptainOwnsGenericSubDomain(appName)
|
||||
})
|
||||
.then(function () {
|
||||
@@ -884,15 +906,17 @@ class ServiceManager {
|
||||
|
||||
// if we pass in networks here. Almost always it results in a delayed update which causes
|
||||
// update errors if they happen right away!
|
||||
return dockerApi.createServiceOnNodeId(
|
||||
CaptainConstants.configs.appPlaceholderImageName,
|
||||
serviceName,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined
|
||||
)
|
||||
return dockerApi
|
||||
.createServiceOnNodeId(
|
||||
CaptainConstants.configs.appPlaceholderImageName,
|
||||
serviceName,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined
|
||||
)
|
||||
.then(() => true)
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
|
||||
@@ -427,20 +427,23 @@ export default class BackupManager {
|
||||
let hasExisted = false
|
||||
|
||||
stream
|
||||
.on('close', function (
|
||||
code: string,
|
||||
signal: string
|
||||
) {
|
||||
Logger.d(
|
||||
`Stream :: close :: code: ${code}, signal: ${signal}`
|
||||
)
|
||||
conn.end()
|
||||
if (hasExisted) {
|
||||
return
|
||||
.on(
|
||||
'close',
|
||||
function (
|
||||
code: string,
|
||||
signal: string
|
||||
) {
|
||||
Logger.d(
|
||||
`Stream :: close :: code: ${code}, signal: ${signal}`
|
||||
)
|
||||
conn.end()
|
||||
if (hasExisted) {
|
||||
return
|
||||
}
|
||||
hasExisted = true
|
||||
resolve(dataReceived.join(''))
|
||||
}
|
||||
hasExisted = true
|
||||
resolve(dataReceived.join(''))
|
||||
})
|
||||
)
|
||||
.on('data', function (data: string) {
|
||||
Logger.d(`STDOUT: ${data}`)
|
||||
dataReceived.push(data)
|
||||
|
||||
@@ -17,7 +17,7 @@ const CONSTANT_FILE_OVERRIDE_USER =
|
||||
const configs = {
|
||||
publishedNameOnDockerHub: 'caprover/caprover',
|
||||
|
||||
version: '1.8.2',
|
||||
version: '1.9.0',
|
||||
|
||||
defaultMaxLogSize: '512m',
|
||||
|
||||
|
||||
@@ -174,24 +174,23 @@ function checkPortOrThrow(ipAddr: string, portToTest: number) {
|
||||
reject(new Error(`Port timed out: ${portToTest}`))
|
||||
}, 5000)
|
||||
|
||||
request(`http://${ipAddr}:${portToTest}`, function (
|
||||
error,
|
||||
response,
|
||||
body
|
||||
) {
|
||||
if (finished) {
|
||||
return
|
||||
}
|
||||
request(
|
||||
`http://${ipAddr}:${portToTest}`,
|
||||
function (error, response, body) {
|
||||
if (finished) {
|
||||
return
|
||||
}
|
||||
|
||||
finished = true
|
||||
finished = true
|
||||
|
||||
if (body + '' === FIREWALL_PASSED) {
|
||||
resolve()
|
||||
} else {
|
||||
printError()
|
||||
reject(new Error(`Port seems to be closed: ${portToTest}`))
|
||||
if (body + '' === FIREWALL_PASSED) {
|
||||
resolve()
|
||||
} else {
|
||||
printError()
|
||||
reject(new Error(`Port seems to be closed: ${portToTest}`))
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ if (s.forceSsl) {
|
||||
}
|
||||
|
||||
location / {
|
||||
return 302 https://$http_host$request_uri;
|
||||
return 302 https://$http_host$request_uri$is_args$query_string;
|
||||
}
|
||||
}
|
||||
<%
|
||||
|
||||
Reference in New Issue
Block a user