Merge pull request #1018 from caprover/master

Release 1.9.0
This commit is contained in:
Kasra Bigdeli
2021-02-15 16:30:27 -05:00
committed by GitHub
19 changed files with 1448 additions and 2448 deletions
+11 -3
View File
@@ -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
+45 -10
View File
@@ -1,15 +1,51 @@
### CapRover [![Docker Pulls](https://img.shields.io/docker/pulls/caprover/caprover.svg)](https://hub.docker.com/r/caprover/caprover/) [![Open Collective backers and sponsors](https://img.shields.io/opencollective/all/caprover)](https://opencollective.com/caprover#backer) [![GitHub release (latest by date)](https://img.shields.io/github/v/release/caprover/caprover)](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! &nbsp; [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](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.
[![YouTube](https://raw.githubusercontent.com/caprover/caprover-website/master/graphics/screenshots-video-small.gif)](https://youtu.be/VPHEXPfsvyQ)
No Docker, nginx knowledge required!
[![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](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! 🙏
[![Donate](https://opencollective.com/caprover/donate/button.png?color=blue)](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
+1 -1
View File
@@ -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 && \
+1098 -2286
View File
File diff suppressed because it is too large Load Diff
+28 -28
View File
@@ -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"
}
}
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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}`)
})
+19
View File
@@ -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
}
}
+2 -2
View File
@@ -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
+64 -55
View File
@@ -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
+53 -3
View File
@@ -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 () {
+5 -1
View File
@@ -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(
+33 -9
View File
@@ -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 () {
+16 -13
View File
@@ -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)
+1 -1
View File
@@ -17,7 +17,7 @@ const CONSTANT_FILE_OVERRIDE_USER =
const configs = {
publishedNameOnDockerHub: 'caprover/caprover',
version: '1.8.2',
version: '1.9.0',
defaultMaxLogSize: '512m',
+14 -15
View File
@@ -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}`))
}
}
})
)
})
}
+1 -1
View File
@@ -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;
}
}
<%