Compare commits

..

22 Commits

Author SHA1 Message Date
Girish Ramakrishnan 2c0786eb37 Use ldapjs from github directly
The 0.7.x ldapjs is over a year old and uses dtrace as a dep which
causes issues when rebuilding.
2015-07-27 13:06:30 -07:00
Johannes Zellner 3db8ebf97f Ensure the appstore ui can operate always on manifest.tags 2015-07-27 19:29:25 +02:00
Johannes Zellner 804105ce2b Add testing section in appstore and mark testing apps
This is not some final design to indicate which app is in testing
but the logistics are there, mainly css from now

Fixes #451
2015-07-27 17:09:59 +02:00
Johannes Zellner c4bb56dc95 Show non published apps in webadmin 2015-07-27 16:34:37 +02:00
Johannes Zellner 87c76a3eb3 Read apps from actual response body 2015-07-27 16:27:50 +02:00
Johannes Zellner 6bceff14ec Add proxy api to get non approved app listings 2015-07-27 14:00:44 +02:00
Girish Ramakrishnan 6b62561706 Add mandatory addons object 2015-07-24 06:59:34 -07:00
Girish Ramakrishnan d558c06803 Add missing semicolon 2015-07-24 06:53:07 -07:00
Girish Ramakrishnan ef9508ccc5 Use BOX_ENV instead of NODE_ENV
Let NODE_ENV be used by node modules and always be set to production

Fixes #453
2015-07-24 01:42:28 -07:00
Girish Ramakrishnan ec8342c2ce Better progress messages 2015-07-23 22:50:58 -07:00
Girish Ramakrishnan 6839f47f99 Fix typo 2015-07-23 14:30:15 -07:00
Girish Ramakrishnan d32990d0e5 Set server_names_hash_bucket_size
e2e tests fail like so when the hostnames are long

Thu, 23 Jul 2015 20:40:23 GMT box:apptask test8629 writing config to /home/yellowtent/data/nginx/applications/a3822f18-2f95-4b73-b8e9-2983dfcaae31.conf
Thu, 23 Jul 2015 20:40:23 GMT box:shell.js reloadNginx execFile: /usr/bin/sudo -S /home/yellowtent/box/src/scripts/reloadnginx.sh
Thu, 23 Jul 2015 20:40:24 GMT box:shell.js reloadNginx (stderr): nginx: [emerg] could not build the server_names_hash, you should increase server_names_hash_bucket_size: 64

Thu, 23 Jul 2015 20:40:24 GMT box:shell.js reloadNginx code: 1, signal: null
Thu, 23 Jul 2015 20:40:24 GMT box:apptask test8629 error installing app: Error: Exited with error 1 signal null
Thu, 23 Jul 2015 20:40:24 GMT box:apptask test8629 installationState: pending_install progress: 15, Configure nginx
^[[1m^[[31mERROR^[[39m^[[22m Exited with error 1 signal null ^[[1m[ /home/yellowtent/box/src/apptask.js:909:32 ]^[[22m
^[[32mstack: ^[[39m
  """
    Error: Exited with error 1 signal null
        at ChildProcess.<anonymous> (/home/yellowtent/box/src/shell.js:38:53)
        at ChildProcess.emit (events.js:110:17)
        at Process.ChildProcess._handle.onexit (child_process.js:1074:12)
  """
^[[32mmessage: ^[[39mExited with error 1 signal null
2015-07-23 13:55:46 -07:00
Johannes Zellner 71dbe21fc3 Set no-cache for the avatar 2015-07-23 16:34:44 +02:00
Johannes Zellner f36616abbb Remove developerMode from update provisioning data
Finally fixes #442
2015-07-23 13:31:39 +02:00
Johannes Zellner db6d6d565f Remove developerMode from config.js 2015-07-23 13:26:30 +02:00
Johannes Zellner 5f3fc68b5e Fixup developers test with new developer mode setting 2015-07-23 13:19:51 +02:00
Johannes Zellner bdca5e343b Fixup clients test with new developer mode setting 2015-07-23 13:16:36 +02:00
Johannes Zellner 58cf712e71 Fix apps-test to use settings developerMode 2015-07-23 12:59:47 +02:00
Johannes Zellner ca7e67ea4f Use developerMode from settings instead of config 2015-07-23 12:52:04 +02:00
Johannes Zellner b202043019 Add developerMode to settings
Part of #442
2015-07-23 12:42:56 +02:00
Johannes Zellner 19fef4c337 Add missing appId key to access app updateInfo 2015-07-23 07:21:05 +02:00
Johannes Zellner 7b864fed04 Only log error if NOOP_CALLBACK got an error 2015-07-23 07:17:30 +02:00
37 changed files with 596 additions and 461 deletions
+36 -35
View File
@@ -107,20 +107,21 @@
"cloudron-manifestformat": {
"version": "1.4.0",
"from": "cloudron-manifestformat@1.4.0",
"resolved": "https://registry.npmjs.org/cloudron-manifestformat/-/cloudron-manifestformat-1.4.0.tgz",
"dependencies": {
"java-packagename-regex": {
"version": "1.0.0",
"from": "java-packagename-regex@>=1.0.0 <2.0.0",
"from": "https://registry.npmjs.org/java-packagename-regex/-/java-packagename-regex-1.0.0.tgz",
"resolved": "https://registry.npmjs.org/java-packagename-regex/-/java-packagename-regex-1.0.0.tgz"
},
"safetydance": {
"version": "0.0.15",
"from": "safetydance@0.0.15",
"from": "http://registry.npmjs.org/safetydance/-/safetydance-0.0.15.tgz",
"resolved": "http://registry.npmjs.org/safetydance/-/safetydance-0.0.15.tgz"
},
"tv4": {
"version": "1.1.12",
"from": "tv4@>=1.1.9 <2.0.0",
"from": "http://registry.npmjs.org/tv4/-/tv4-1.1.12.tgz",
"resolved": "http://registry.npmjs.org/tv4/-/tv4-1.1.12.tgz"
}
}
@@ -161,7 +162,7 @@
"dependencies": {
"inherits": {
"version": "2.0.1",
"from": "http://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
"from": "inherits@>=2.0.0 <3.0.0",
"resolved": "http://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
},
"statuses": {
@@ -304,7 +305,7 @@
"dependencies": {
"inherits": {
"version": "2.0.1",
"from": "http://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
"from": "inherits@>=2.0.0 <3.0.0",
"resolved": "http://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
},
"statuses": {
@@ -1288,69 +1289,69 @@
},
"dockerode": {
"version": "2.2.2",
"from": "dockerode@2.2.2",
"from": "https://registry.npmjs.org/dockerode/-/dockerode-2.2.2.tgz",
"resolved": "https://registry.npmjs.org/dockerode/-/dockerode-2.2.2.tgz",
"dependencies": {
"docker-modem": {
"version": "0.2.6",
"from": "docker-modem@>=0.2.0 <0.3.0",
"from": "https://registry.npmjs.org/docker-modem/-/docker-modem-0.2.6.tgz",
"resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-0.2.6.tgz",
"dependencies": {
"debug": {
"version": "0.7.4",
"from": "debug@>=0.7.4 <0.8.0",
"from": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz",
"resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz"
},
"follow-redirects": {
"version": "0.0.3",
"from": "follow-redirects@0.0.3",
"from": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-0.0.3.tgz",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-0.0.3.tgz"
},
"JSONStream": {
"version": "0.10.0",
"from": "JSONStream@0.10.0",
"from": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.10.0.tgz",
"resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.10.0.tgz",
"dependencies": {
"jsonparse": {
"version": "0.0.5",
"from": "jsonparse@0.0.5",
"from": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz",
"resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz"
},
"through": {
"version": "2.3.8",
"from": "through@>=2.2.7 <3.0.0",
"from": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz"
}
}
},
"querystring": {
"version": "0.2.0",
"from": "querystring@0.2.0",
"from": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz"
},
"readable-stream": {
"version": "1.0.33",
"from": "readable-stream@>=1.0.26-4 <1.1.0",
"from": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz",
"dependencies": {
"core-util-is": {
"version": "1.0.1",
"from": "core-util-is@>=1.0.0 <1.1.0",
"from": "http://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz",
"resolved": "http://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz"
},
"isarray": {
"version": "0.0.1",
"from": "isarray@0.0.1",
"from": "http://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"resolved": "http://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
},
"string_decoder": {
"version": "0.10.31",
"from": "string_decoder@>=0.10.0 <0.11.0",
"from": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
},
"inherits": {
"version": "2.0.1",
"from": "inherits@>=2.0.1 <2.1.0",
"from": "http://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
"resolved": "http://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
}
}
@@ -1691,88 +1692,88 @@
},
"ldapjs": {
"version": "0.7.1",
"from": "ldapjs@*",
"from": "https://registry.npmjs.org/ldapjs/-/ldapjs-0.7.1.tgz",
"resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-0.7.1.tgz",
"dependencies": {
"asn1": {
"version": "0.2.1",
"from": "asn1@0.2.1",
"from": "https://registry.npmjs.org/asn1/-/asn1-0.2.1.tgz",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.1.tgz"
},
"assert-plus": {
"version": "0.1.5",
"from": "assert-plus@0.1.5",
"from": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz"
},
"bunyan": {
"version": "0.22.1",
"from": "bunyan@0.22.1",
"from": "https://registry.npmjs.org/bunyan/-/bunyan-0.22.1.tgz",
"resolved": "https://registry.npmjs.org/bunyan/-/bunyan-0.22.1.tgz",
"dependencies": {
"mv": {
"version": "0.0.5",
"from": "mv@0.0.5",
"from": "https://registry.npmjs.org/mv/-/mv-0.0.5.tgz",
"resolved": "https://registry.npmjs.org/mv/-/mv-0.0.5.tgz"
}
}
},
"nopt": {
"version": "2.1.1",
"from": "nopt@2.1.1",
"from": "https://registry.npmjs.org/nopt/-/nopt-2.1.1.tgz",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-2.1.1.tgz",
"dependencies": {
"abbrev": {
"version": "1.0.7",
"from": "abbrev@>=1.0.0 <2.0.0",
"from": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.7.tgz",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.7.tgz"
}
}
},
"pooling": {
"version": "0.4.6",
"from": "pooling@0.4.6",
"from": "https://registry.npmjs.org/pooling/-/pooling-0.4.6.tgz",
"resolved": "https://registry.npmjs.org/pooling/-/pooling-0.4.6.tgz",
"dependencies": {
"once": {
"version": "1.3.0",
"from": "once@1.3.0",
"from": "https://registry.npmjs.org/once/-/once-1.3.0.tgz",
"resolved": "https://registry.npmjs.org/once/-/once-1.3.0.tgz"
},
"vasync": {
"version": "1.4.0",
"from": "vasync@1.4.0",
"from": "https://registry.npmjs.org/vasync/-/vasync-1.4.0.tgz",
"resolved": "https://registry.npmjs.org/vasync/-/vasync-1.4.0.tgz",
"dependencies": {
"jsprim": {
"version": "0.3.0",
"from": "jsprim@0.3.0",
"from": "https://registry.npmjs.org/jsprim/-/jsprim-0.3.0.tgz",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-0.3.0.tgz",
"dependencies": {
"extsprintf": {
"version": "1.0.0",
"from": "extsprintf@1.0.0",
"from": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.0.tgz",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.0.tgz"
},
"json-schema": {
"version": "0.2.2",
"from": "json-schema@0.2.2",
"from": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.2.tgz",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.2.tgz"
},
"verror": {
"version": "1.3.3",
"from": "verror@1.3.3",
"from": "https://registry.npmjs.org/verror/-/verror-1.3.3.tgz",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.3.3.tgz"
}
}
},
"verror": {
"version": "1.1.0",
"from": "verror@1.1.0",
"from": "https://registry.npmjs.org/verror/-/verror-1.1.0.tgz",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.1.0.tgz",
"dependencies": {
"extsprintf": {
"version": "1.0.0",
"from": "extsprintf@1.0.0",
"from": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.0.tgz",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.0.tgz"
}
}
@@ -1783,7 +1784,7 @@
},
"dtrace-provider": {
"version": "0.2.8",
"from": "dtrace-provider@0.2.8",
"from": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.2.8.tgz",
"resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.2.8.tgz"
}
}
+4 -4
View File
@@ -35,7 +35,7 @@
"express-session": "^1.11.3",
"hat": "0.0.3",
"json": "^9.0.3",
"ldapjs": "^0.7.1",
"ldapjs": "git+https://github.com/mcavage/node-ldapjs.git#acc1ca8f4314fd9d67561feabc8ce4c235076a5e",
"memorystream": "^0.3.0",
"mime": "^1.3.4",
"morgan": "^1.6.0",
@@ -92,9 +92,9 @@
"yargs": "^3.15.0"
},
"scripts": {
"migrate_local": "NODE_ENV=local DATABASE_URL=mysql://root:@localhost/box node_modules/.bin/db-migrate up",
"migrate_test": "NODE_ENV=test DATABASE_URL=mysql://root:@localhost/boxtest node_modules/.bin/db-migrate up",
"test": "npm run migrate_test && src/test/setupTest && NODE_ENV=test ./node_modules/istanbul/lib/cli.js test $1 ./node_modules/mocha/bin/_mocha -- -R spec ./src/test ./src/routes/test",
"migrate_local": "DATABASE_URL=mysql://root:@localhost/box node_modules/.bin/db-migrate up",
"migrate_test": "BOX_ENV=test DATABASE_URL=mysql://root:@localhost/boxtest node_modules/.bin/db-migrate up",
"test": "npm run migrate_test && src/test/setupTest && BOX_ENV=test ./node_modules/istanbul/lib/cli.js test $1 ./node_modules/mocha/bin/_mocha -- -R spec ./src/test ./src/routes/test",
"postmerge": "/bin/true",
"precommit": "/bin/true",
"prepush": "npm test",
-5
View File
@@ -14,7 +14,6 @@ arg_fqdn=""
arg_token=""
arg_version=""
arg_is_custom_domain="false"
arg_developer_mode=""
arg_retire="false"
arg_model=""
@@ -36,9 +35,6 @@ EOF
arg_tls_cert=$(echo "$2" | $json tlsCert)
arg_tls_key=$(echo "$2" | $json tlsKey)
arg_developer_mode=$(echo "$2" | $json developerMode)
[[ "${arg_developer_mode}" == "" ]] && arg_developer_mode="false"
arg_restore_url=$(echo "$2" | $json restoreUrl)
[[ "${arg_restore_url}" == "null" ]] && arg_restore_url=""
@@ -67,5 +63,4 @@ echo "version: ${arg_version}"
echo "custom domain: ${arg_is_custom_domain}"
echo "tls cert: ${arg_tls_cert}"
echo "tls key: ${arg_tls_key}"
echo "developer mode: ${arg_developer_mode}"
echo "model: ${arg_model}"
+9 -9
View File
@@ -1,26 +1,26 @@
Defaults!/home/yellowtent/box/src/scripts/createappdir.sh env_keep="HOME NODE_ENV"
Defaults!/home/yellowtent/box/src/scripts/createappdir.sh env_keep="HOME BOX_ENV"
yellowtent ALL=(root) NOPASSWD: /home/yellowtent/box/src/scripts/createappdir.sh
Defaults!/home/yellowtent/box/src/scripts/rmappdir.sh env_keep="HOME NODE_ENV"
Defaults!/home/yellowtent/box/src/scripts/rmappdir.sh env_keep="HOME BOX_ENV"
yellowtent ALL=(root) NOPASSWD: /home/yellowtent/box/src/scripts/rmappdir.sh
Defaults!/home/yellowtent/box/src/scripts/reloadnginx.sh env_keep="HOME NODE_ENV"
Defaults!/home/yellowtent/box/src/scripts/reloadnginx.sh env_keep="HOME BOX_ENV"
yellowtent ALL=(root) NOPASSWD: /home/yellowtent/box/src/scripts/reloadnginx.sh
Defaults!/home/yellowtent/box/src/scripts/backupbox.sh env_keep="HOME NODE_ENV"
Defaults!/home/yellowtent/box/src/scripts/backupbox.sh env_keep="HOME BOX_ENV"
yellowtent ALL=(root) NOPASSWD: /home/yellowtent/box/src/scripts/backupbox.sh
Defaults!/home/yellowtent/box/src/scripts/backupapp.sh env_keep="HOME NODE_ENV"
Defaults!/home/yellowtent/box/src/scripts/backupapp.sh env_keep="HOME BOX_ENV"
yellowtent ALL=(root) NOPASSWD: /home/yellowtent/box/src/scripts/backupapp.sh
Defaults!/home/yellowtent/box/src/scripts/restoreapp.sh env_keep="HOME NODE_ENV"
Defaults!/home/yellowtent/box/src/scripts/restoreapp.sh env_keep="HOME BOX_ENV"
yellowtent ALL=(root) NOPASSWD: /home/yellowtent/box/src/scripts/restoreapp.sh
Defaults!/home/yellowtent/box/src/scripts/reboot.sh env_keep="HOME NODE_ENV"
Defaults!/home/yellowtent/box/src/scripts/reboot.sh env_keep="HOME BOX_ENV"
yellowtent ALL=(root) NOPASSWD: /home/yellowtent/box/src/scripts/reboot.sh
Defaults!/home/yellowtent/box/src/scripts/reloadcollectd.sh env_keep="HOME NODE_ENV"
Defaults!/home/yellowtent/box/src/scripts/reloadcollectd.sh env_keep="HOME BOX_ENV"
yellowtent ALL=(root) NOPASSWD: /home/yellowtent/box/src/scripts/reloadcollectd.sh
Defaults!/home/yellowtent/box/src/scripts/backupswap.sh env_keep="HOME NODE_ENV"
Defaults!/home/yellowtent/box/src/scripts/backupswap.sh env_keep="HOME BOX_ENV"
yellowtent ALL=(root) NOPASSWD: /home/yellowtent/box/src/scripts/backupswap.sh
@@ -7,4 +7,4 @@ stdout_logfile=/var/log/supervisor/apphealthtask.log
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=2
user=yellowtent
environment=HOME="/home/yellowtent",USER="yellowtent",DEBUG="box*",NODE_ENV="cloudron"
environment=HOME="/home/yellowtent",USER="yellowtent",DEBUG="box*",BOX_ENV="cloudron",NODE_ENV="production"
+1 -1
View File
@@ -7,4 +7,4 @@ stdout_logfile=/var/log/supervisor/box.log
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=2
user=yellowtent
environment=HOME="/home/yellowtent",USER="yellowtent",DEBUG="box*,connect-lastmile",NODE_ENV="cloudron"
environment=HOME="/home/yellowtent",USER="yellowtent",DEBUG="box*,connect-lastmile",BOX_ENV="cloudron",NODE_ENV="production"
@@ -8,4 +8,4 @@ stderr_logfile=/var/log/supervisor/crashnotifier.log
stderr_logfile_maxbytes=50MB
stderr_logfile_backups=2
user=yellowtent
environment=HOME="/home/yellowtent",USER="yellowtent",NODE_ENV="cloudron"
environment=HOME="/home/yellowtent",USER="yellowtent",BOX_ENV="cloudron",NODE_ENV="production"
@@ -7,4 +7,4 @@ stdout_logfile=/var/log/supervisor/janitor.log
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=2
user=yellowtent
environment=HOME="/home/yellowtent",USER="yellowtent",DEBUG="box*",NODE_ENV="cloudron"
environment=HOME="/home/yellowtent",USER="yellowtent",DEBUG="box*",BOX_ENV="cloudron",NODE_ENV="production"
@@ -7,4 +7,4 @@ stdout_logfile=/var/log/supervisor/oauthproxy.log
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=2
user=yellowtent
environment=HOME="/home/yellowtent",USER="yellowtent",DEBUG="box*",NODE_ENV="cloudron"
environment=HOME="/home/yellowtent",USER="yellowtent",DEBUG="box*",BOX_ENV="cloudron",NODE_ENV="production"
+2 -3
View File
@@ -77,7 +77,7 @@ set_progress "25" "Migrating data"
sudo -u "${USER}" -H bash <<EOF
set -eu
cd "${BOX_SRC_DIR}"
NODE_ENV=cloudron DATABASE_URL=mysql://root:${mysql_root_password}@localhost/box "${BOX_SRC_DIR}/node_modules/.bin/db-migrate" up
BOX_ENV=cloudron DATABASE_URL=mysql://root:${mysql_root_password}@localhost/box "${BOX_SRC_DIR}/node_modules/.bin/db-migrate" up
EOF
set_progress "28" "Setup collectd"
@@ -129,8 +129,7 @@ cat > "${CONFIG_DIR}/cloudron.conf" <<CONF_END
"port": 3306,
"name": "box"
},
"model": "${arg_model}",
"developerMode": ${arg_developer_mode}
"model": "${arg_model}"
}
CONF_END
+3
View File
@@ -17,6 +17,9 @@ http {
'"$request" $status $body_bytes_sent $request_time '
'"$http_referer" "$http_user_agent"';
# required for long host names
server_names_hash_bucket_size 128;
access_log access.log combined2;
sendfile on;
+2 -2
View File
@@ -660,11 +660,11 @@ function autoupdateApps(updateInfo, callback) { // updateInfo is { appId -> { ma
async.eachSeries(Object.keys(updateInfo), function iterator(appId, iteratorDone) {
get(appId, function (error, app) {
if (!canAutoupdateApp(app, updateInfo.manifest)) {
if (!canAutoupdateApp(app, updateInfo[appId].manifest)) {
return iteratorDone();
}
update(appId, updateInfo.manifest, app.portBindings, null /* icon */, function (error) {
update(appId, updateInfo[appId].manifest, app.portBindings, null /* icon */, function (error) {
if (error) debug('Error initiating autoupdate of %s', appId);
iteratorDone(null);
+24 -20
View File
@@ -108,7 +108,7 @@ CloudronError.NOT_FOUND = 'Not found';
function initialize(callback) {
assert.strictEqual(typeof callback, 'function');
if (process.env.NODE_ENV !== 'test') {
if (process.env.BOX_ENV !== 'test') {
addMailDnsRecords();
}
@@ -231,6 +231,7 @@ function getCloudronDetails(callback) {
function getConfig(callback) {
assert.strictEqual(typeof callback, 'function');
// TODO avoid pyramid of awesomeness with async
getCloudronDetails(function (error, result) {
if (error) {
console.error('Failed to fetch cloudron details.', error);
@@ -245,20 +246,24 @@ function getConfig(callback) {
settings.getCloudronName(function (error, cloudronName) {
if (error) return callback(new CloudronError(CloudronError.INTERNAL_ERROR, error));
callback(null, {
apiServerOrigin: config.apiServerOrigin(),
webServerOrigin: config.webServerOrigin(),
isDev: config.isDev(),
fqdn: config.fqdn(),
ip: sysinfo.getIp(),
version: config.version(),
update: updateChecker.getUpdateInfo(),
progress: progress.get(),
isCustomDomain: config.isCustomDomain(),
developerMode: config.developerMode(),
region: result.region,
size: result.size,
cloudronName: cloudronName
settings.getDeveloperMode(function (error, developerMode) {
if (error) return callback(new CloudronError(CloudronError.INTERNAL_ERROR, error));
callback(null, {
apiServerOrigin: config.apiServerOrigin(),
webServerOrigin: config.webServerOrigin(),
isDev: config.isDev(),
fqdn: config.fqdn(),
ip: sysinfo.getIp(),
version: config.version(),
update: updateChecker.getUpdateInfo(),
progress: progress.get(),
isCustomDomain: config.isCustomDomain(),
developerMode: developerMode,
region: result.region,
size: result.size,
cloudronName: cloudronName
});
});
});
});
@@ -485,8 +490,7 @@ function doUpdate(boxUpdateInfo, callback) {
tlsKey: fs.readFileSync(path.join(paths.NGINX_CERT_DIR, 'host.key'), 'utf8'),
isCustomDomain: config.isCustomDomain(),
restoreUrl: null,
restoreKey: null,
developerMode: config.developerMode() // this survives updates but not upgrades
restoreKey: null
}
};
@@ -569,7 +573,7 @@ function backupBox(callback) {
if (error) return callback(new CloudronError(CloudronError.INTERNAL_ERROR, error));
var appBackupIds = allApps.map(function (app) { return app.lastBackupId; });
appBackupIds = appBackupIds.filter(function (id) { return id !== null }); // remove apps that were never backed up
appBackupIds = appBackupIds.filter(function (id) { return id !== null; }); // remove apps that were never backed up
backupBoxWithAppBackupIds(appBackupIds, callback);
});
@@ -591,10 +595,10 @@ function backupBoxAndApps(callback) {
++processed;
apps.backupApp(app, app.manifest.addons, function (error, backupId) {
progress.set(progress.BACKUP, step * processed, app.location);
progress.set(progress.BACKUP, step * processed, 'Backing up app at ' + app.location);
if (error && error.reason === AppsError.BAD_STATE) {
debugApp(app, 'Skipping backup (istate:%s health%s). Reusing %s', app.installationState, app.health, app.lastBackupId);
debugApp(app, 'Skipping backup (istate:%s health:%s). using lastBackupId:%s', app.installationState, app.health, app.lastBackupId);
backupId = app.lastBackupId;
}
+2 -9
View File
@@ -11,8 +11,8 @@ exports = module.exports = {
set: set,
// ifdefs to check environment
CLOUDRON: process.env.NODE_ENV === 'cloudron',
TEST: process.env.NODE_ENV === 'test',
CLOUDRON: process.env.BOX_ENV === 'cloudron',
TEST: process.env.BOX_ENV === 'test',
// convenience getters
apiServerOrigin: apiServerOrigin,
@@ -22,7 +22,6 @@ exports = module.exports = {
version: version,
isCustomDomain: isCustomDomain,
database: database,
developerMode: developerMode,
// these values are derived
adminOrigin: adminOrigin,
@@ -76,7 +75,6 @@ function initConfig() {
data.port = 3000;
data.apiServerOrigin = null;
data.database = null;
data.developerMode = false;
} else if (exports.TEST) {
data.port = 5454;
data.apiServerOrigin = 'http://localhost:6060'; // hock doesn't support https
@@ -88,7 +86,6 @@ function initConfig() {
name: 'boxtest'
};
data.token = 'APPSTORE_TOKEN';
data.developerMode = false;
} else {
assert(false, 'Unknown environment. This should not happen!');
}
@@ -171,10 +168,6 @@ function database() {
return get('database');
}
function developerMode() {
return get('developerMode');
}
function isDev() {
return /dev/i.test(get('boxVersionsUrl'));
}
+1 -1
View File
@@ -21,7 +21,7 @@ var gAutoupdaterJob = null,
var gInitialized = false;
var NOOP_CALLBACK = function (error) { console.error(error); };
var NOOP_CALLBACK = function (error) { if (error) console.error(error); };
// cron format
// Seconds: 0-59
+25 -6
View File
@@ -7,12 +7,15 @@ exports = module.exports = {
enabled: enabled,
setEnabled: setEnabled,
issueDeveloperToken: issueDeveloperToken
issueDeveloperToken: issueDeveloperToken,
getNonApprovedApps: getNonApprovedApps
};
var assert = require('assert'),
tokendb = require('./tokendb.js'),
config = require('./config.js'),
tokendb = require('./tokendb.js'),
settings = require('./settings.js'),
superagent = require('superagent'),
util = require('util');
function DeveloperError(reason, errorOrMessage) {
@@ -39,16 +42,20 @@ DeveloperError.INTERNAL_ERROR = 'Internal Error';
function enabled(callback) {
assert.strictEqual(typeof callback, 'function');
callback(null, config.developerMode());
settings.getDeveloperMode(function (error, enabled) {
if (error) return callback(new DeveloperError(DeveloperError.INTERNAL_ERROR, error));
callback(null, enabled);
});
}
function setEnabled(enabled, callback) {
assert.strictEqual(typeof enabled, 'boolean');
assert.strictEqual(typeof callback, 'function');
config.set('developerMode', enabled);
callback(null);
settings.setDeveloperMode(enabled, function (error) {
if (error) return callback(new DeveloperError(DeveloperError.INTERNAL_ERROR, error));
callback(null);
});
}
function issueDeveloperToken(user, callback) {
@@ -64,3 +71,15 @@ function issueDeveloperToken(user, callback) {
callback(null, { token: token, expiresAt: expiresAt });
});
}
function getNonApprovedApps(callback) {
assert.strictEqual(typeof callback, 'function');
var url = config.apiServerOrigin() + '/api/v1/boxes/' + config.fqdn() + '/apps';
superagent.get(url).query({ token: config.token(), boxVersion: config.version() }).end(function (error, result) {
if (error) return callback(new DeveloperError(DeveloperError.INTERNAL_ERROR, error));
if (result.status !== 200) return callback(new DeveloperError(DeveloperError.INTERNAL_ERROR, util.format('App listing failed. %s %j', result.status, result.body)));
callback(null, result.body.apps || []);
});
}
+1 -1
View File
@@ -10,7 +10,7 @@ exports = module.exports = (function () {
var docker;
var options = connectOptions(); // the real docker
if (process.env.NODE_ENV === 'test') {
if (process.env.BOX_ENV === 'test') {
// test code runs a docker proxy on this port
docker = new Docker({ host: 'http://localhost', port: 5687 });
} else {
+1 -1
View File
@@ -117,7 +117,7 @@ function installApp(req, res, next) {
if ('icon' in data && typeof data.icon !== 'string') return next(new HttpError(400, 'icon is not a string'));
// allow tests to provide an appId for testing
var appId = (process.env.NODE_ENV === 'test' && typeof data.appId === 'string') ? data.appId : uuid.v4();
var appId = (process.env.BOX_ENV === 'test' && typeof data.appId === 'string') ? data.appId : uuid.v4();
debug('Installing app id:%s storeid:%s loc:%s port:%j restrict:%s manifest:%j', appId, data.appStoreId, data.location, data.portBindings, data.accessRestriction, data.manifest);
+9 -1
View File
@@ -6,7 +6,8 @@ exports = module.exports = {
enabled: enabled,
setEnabled: setEnabled,
status: status,
login: login
login: login,
apps: apps
};
var developer = require('../developer.js'),
@@ -46,3 +47,10 @@ function login(req, res, next) {
});
})(req, res, next);
}
function apps(req, res, next) {
developer.getNonApprovedApps(function (error, result) {
if (error) return next(new HttpError(500, error));
next(new HttpSuccess(200, { apps: result }));
});
}
+3
View File
@@ -76,6 +76,9 @@ function getCloudronAvatar(req, res, next) {
settings.getCloudronAvatar(function (error, avatar) {
if (error) return next(new HttpError(500, error));
// avoid caching the avatar on the client to see avatar changes immediately
res.set('Cache-Control', 'no-cache');
res.set('Content-Type', 'image/png');
res.status(200).send(avatar);
});
+22 -19
View File
@@ -30,6 +30,7 @@ var appdb = require('../../appdb.js'),
request = require('superagent'),
safe = require('safetydance'),
server = require('../../server.js'),
settings = require('../../settings.js'),
sysinfo = require('../../sysinfo.js'),
tokendb = require('../../tokendb.js'),
url = require('url'),
@@ -431,28 +432,30 @@ describe('App API', function () {
it('app install succeeds without password but developer token', function (done) {
var fake = nock(config.apiServerOrigin()).post('/api/v1/apps/test/purchase?token=APPSTORE_TOKEN').reply(201, {});
config.set('developerMode', true);
settings.setDeveloperMode(true, function (error) {
expect(error).to.be(null);
request.post(SERVER_URL + '/api/v1/developer/login')
.send({ username: USERNAME, password: PASSWORD })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(200);
expect(result.body.expiresAt).to.be.a('number');
expect(result.body.token).to.be.a('string');
request.post(SERVER_URL + '/api/v1/developer/login')
.send({ username: USERNAME, password: PASSWORD })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(200);
expect(result.body.expiresAt).to.be.a('number');
expect(result.body.token).to.be.a('string');
// overwrite non dev token
token = result.body.token;
// overwrite non dev token
token = result.body.token;
request.post(SERVER_URL + '/api/v1/apps/install')
.query({ access_token: token })
.send({ appStoreId: APP_STORE_ID, manifest: APP_MANIFEST, location: APP_LOCATION+APP_LOCATION, portBindings: null, accessRestriction: '' })
.end(function (err, res) {
expect(res.statusCode).to.equal(202);
expect(res.body.id).to.be.a('string');
expect(fake.isDone()).to.be.ok();
APP_ID = res.body.id;
done(err);
request.post(SERVER_URL + '/api/v1/apps/install')
.query({ access_token: token })
.send({ appStoreId: APP_STORE_ID, manifest: APP_MANIFEST, location: APP_LOCATION+APP_LOCATION, portBindings: null, accessRestriction: '' })
.end(function (err, res) {
expect(res.statusCode).to.equal(202);
expect(res.body.id).to.be.a('string');
expect(fake.isDone()).to.be.ok();
APP_ID = res.body.id;
done(err);
});
});
});
});
+1 -1
View File
@@ -50,7 +50,7 @@ function setup(done) {
},
function addApp(callback) {
var manifest = { version: '0.0.1', manifestVersion: 1, dockerImage: 'foo', healthCheckPath: '/', httpPort: 3, title: 'ok' };
var manifest = { version: '0.0.1', manifestVersion: 1, dockerImage: 'foo', healthCheckPath: '/', httpPort: 3, title: 'ok', addons: { } };
appdb.add('appid', 'appStoreId', manifest, 'location', [ ] /* portBindings */, '' /* accessRestriction */, callback);
}
], done);
+305 -305
View File
@@ -15,7 +15,8 @@ var async = require('async'),
nock = require('nock'),
hat = require('hat'),
superagent = require('superagent'),
server = require('../../server.js');
server = require('../../server.js'),
settings = require('../../settings.js');
var SERVER_URL = 'http://localhost:' + config.get('port');
@@ -62,137 +63,129 @@ describe('OAuth Clients API', function () {
after(cleanup);
it('fails without token', function (done) {
config.set('developerMode', true);
describe('without developer mode', function () {
before(function (done) {
settings.setDeveloperMode(false, done);
});
superagent.post(SERVER_URL + '/api/v1/oauth/clients')
.send({ appId: 'someApp', redirectURI: 'http://foobar.com', scope: 'profile,roleUser' })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(401);
done();
it('fails', function (done) {
superagent.post(SERVER_URL + '/api/v1/oauth/clients')
.query({ access_token: token })
.send({ appId: 'someApp', redirectURI: 'http://foobar.com', scope: 'profile,roleUser' })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(412);
done();
});
});
});
it('fails if not in developerMode', function (done) {
config.set('developerMode', false);
superagent.post(SERVER_URL + '/api/v1/oauth/clients')
.query({ access_token: token })
.send({ appId: 'someApp', redirectURI: 'http://foobar.com', scope: 'profile,roleUser' })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(412);
done();
describe('with developer mode', function () {
before(function (done) {
settings.setDeveloperMode(true, done);
});
});
it('fails without appId', function (done) {
config.set('developerMode', true);
superagent.post(SERVER_URL + '/api/v1/oauth/clients')
.query({ access_token: token })
.send({ redirectURI: 'http://foobar.com', scope: 'profile,roleUser' })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(400);
done();
it('fails without token', function (done) {
superagent.post(SERVER_URL + '/api/v1/oauth/clients')
.send({ appId: 'someApp', redirectURI: 'http://foobar.com', scope: 'profile,roleUser' })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(401);
done();
});
});
});
it('fails with empty appId', function (done) {
config.set('developerMode', true);
superagent.post(SERVER_URL + '/api/v1/oauth/clients')
.query({ access_token: token })
.send({ appId: '', redirectURI: 'http://foobar.com', scope: 'profile,roleUser' })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(400);
done();
it('fails without appId', function (done) {
superagent.post(SERVER_URL + '/api/v1/oauth/clients')
.query({ access_token: token })
.send({ redirectURI: 'http://foobar.com', scope: 'profile,roleUser' })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(400);
done();
});
});
});
it('fails without scope', function (done) {
config.set('developerMode', true);
superagent.post(SERVER_URL + '/api/v1/oauth/clients')
.query({ access_token: token })
.send({ appId: 'someApp', redirectURI: 'http://foobar.com' })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(400);
done();
it('fails with empty appId', function (done) {
superagent.post(SERVER_URL + '/api/v1/oauth/clients')
.query({ access_token: token })
.send({ appId: '', redirectURI: 'http://foobar.com', scope: 'profile,roleUser' })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(400);
done();
});
});
});
it('fails with empty scope', function (done) {
config.set('developerMode', true);
superagent.post(SERVER_URL + '/api/v1/oauth/clients')
.query({ access_token: token })
.send({ appId: 'someApp', redirectURI: 'http://foobar.com', scope: '' })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(400);
done();
it('fails without scope', function (done) {
superagent.post(SERVER_URL + '/api/v1/oauth/clients')
.query({ access_token: token })
.send({ appId: 'someApp', redirectURI: 'http://foobar.com' })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(400);
done();
});
});
});
it('fails without redirectURI', function (done) {
config.set('developerMode', true);
superagent.post(SERVER_URL + '/api/v1/oauth/clients')
.query({ access_token: token })
.send({ appId: 'someApp', scope: 'profile,roleUser' })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(400);
done();
it('fails with empty scope', function (done) {
superagent.post(SERVER_URL + '/api/v1/oauth/clients')
.query({ access_token: token })
.send({ appId: 'someApp', redirectURI: 'http://foobar.com', scope: '' })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(400);
done();
});
});
});
it('fails with empty redirectURI', function (done) {
config.set('developerMode', true);
superagent.post(SERVER_URL + '/api/v1/oauth/clients')
.query({ access_token: token })
.send({ appId: 'someApp', redirectURI: '', scope: 'profile,roleUser' })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(400);
done();
it('fails without redirectURI', function (done) {
superagent.post(SERVER_URL + '/api/v1/oauth/clients')
.query({ access_token: token })
.send({ appId: 'someApp', scope: 'profile,roleUser' })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(400);
done();
});
});
});
it('fails with malformed redirectURI', function (done) {
config.set('developerMode', true);
superagent.post(SERVER_URL + '/api/v1/oauth/clients')
.query({ access_token: token })
.send({ appId: 'someApp', redirectURI: 'foobar', scope: 'profile,roleUser' })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(400);
done();
it('fails with empty redirectURI', function (done) {
superagent.post(SERVER_URL + '/api/v1/oauth/clients')
.query({ access_token: token })
.send({ appId: 'someApp', redirectURI: '', scope: 'profile,roleUser' })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(400);
done();
});
});
});
it('succeeds', function (done) {
config.set('developerMode', true);
it('fails with malformed redirectURI', function (done) {
superagent.post(SERVER_URL + '/api/v1/oauth/clients')
.query({ access_token: token })
.send({ appId: 'someApp', redirectURI: 'foobar', scope: 'profile,roleUser' })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(400);
done();
});
});
superagent.post(SERVER_URL + '/api/v1/oauth/clients')
.query({ access_token: token })
.send({ appId: 'someApp', redirectURI: 'http://foobar.com', scope: 'profile,roleUser' })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(201);
expect(result.body.id).to.be.a('string');
expect(result.body.appId).to.be.a('string');
expect(result.body.redirectURI).to.be.a('string');
expect(result.body.clientSecret).to.be.a('string');
expect(result.body.scope).to.be.a('string');
done();
it('succeeds', function (done) {
superagent.post(SERVER_URL + '/api/v1/oauth/clients')
.query({ access_token: token })
.send({ appId: 'someApp', redirectURI: 'http://foobar.com', scope: 'profile,roleUser' })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(201);
expect(result.body.id).to.be.a('string');
expect(result.body.appId).to.be.a('string');
expect(result.body.redirectURI).to.be.a('string');
expect(result.body.clientSecret).to.be.a('string');
expect(result.body.scope).to.be.a('string');
done();
});
});
});
});
@@ -230,9 +223,9 @@ describe('OAuth Clients API', function () {
});
},
function (callback) {
config.set('developerMode', true);
settings.setDeveloperMode.bind(null, true),
function (callback) {
superagent.post(SERVER_URL + '/api/v1/oauth/clients')
.query({ access_token: token })
.send({ appId: CLIENT_0.appId, redirectURI: CLIENT_0.redirectURI, scope: CLIENT_0.scope })
@@ -250,51 +243,56 @@ describe('OAuth Clients API', function () {
after(cleanup);
it('fails without token', function (done) {
config.set('developerMode', true);
describe('without developer mode', function () {
before(function (done) {
settings.setDeveloperMode(false, done);
});
superagent.get(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(401);
done();
it('fails', function (done) {
superagent.get(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.query({ access_token: token })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(412);
done();
});
});
});
it('fails if not in developerMode', function (done) {
config.set('developerMode', false);
superagent.get(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.query({ access_token: token })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(412);
done();
describe('with developer mode', function () {
before(function (done) {
settings.setDeveloperMode(true, done);
});
});
it('fails with unknown id', function (done) {
config.set('developerMode', true);
superagent.get(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id.toUpperCase())
.query({ access_token: token })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(404);
done();
it('fails without token', function (done) {
superagent.get(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(401);
done();
});
});
});
it('succeeds', function (done) {
config.set('developerMode', true);
superagent.get(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.query({ access_token: token })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(200);
expect(result.body).to.eql(CLIENT_0);
done();
it('fails with unknown id', function (done) {
superagent.get(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id.toUpperCase())
.query({ access_token: token })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(404);
done();
});
});
it('succeeds', function (done) {
superagent.get(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.query({ access_token: token })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(200);
expect(result.body).to.eql(CLIENT_0);
done();
});
});
});
});
@@ -339,9 +337,9 @@ describe('OAuth Clients API', function () {
});
},
function (callback) {
config.set('developerMode', true);
settings.setDeveloperMode.bind(null, true),
function (callback) {
superagent.post(SERVER_URL + '/api/v1/oauth/clients')
.query({ access_token: token })
.send({ appId: CLIENT_0.appId, redirectURI: CLIENT_0.redirectURI, scope: CLIENT_0.scope })
@@ -359,116 +357,113 @@ describe('OAuth Clients API', function () {
after(cleanup);
it('fails without token', function (done) {
config.set('developerMode', true);
superagent.put(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.send({ appId: 'someApp', redirectURI: 'http://foobar.com' })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(401);
done();
describe('without developer mode', function () {
before(function (done) {
settings.setDeveloperMode(false, done);
});
});
it('fails if not in developerMode', function (done) {
config.set('developerMode', false);
superagent.put(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.query({ access_token: token })
.send({ appId: CLIENT_1.appId, redirectURI: CLIENT_1.redirectURI })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(412);
done();
});
});
it('fails without appId', function (done) {
config.set('developerMode', true);
superagent.put(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.query({ access_token: token })
.send({ redirectURI: CLIENT_1.redirectURI })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(400);
done();
});
});
it('fails with empty appId', function (done) {
config.set('developerMode', true);
superagent.put(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.query({ access_token: token })
.send({ appId: '', redirectURI: CLIENT_1.redirectURI })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(400);
done();
});
});
it('fails without redirectURI', function (done) {
config.set('developerMode', true);
superagent.put(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.query({ access_token: token })
.send({ appId: CLIENT_1.appId })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(400);
done();
});
});
it('fails with empty redirectURI', function (done) {
config.set('developerMode', true);
superagent.put(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.query({ access_token: token })
.send({ appId: CLIENT_1.appId, redirectURI: '' })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(400);
done();
});
});
it('fails with malformed redirectURI', function (done) {
config.set('developerMode', true);
superagent.put(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.query({ access_token: token })
.send({ appId: CLIENT_1.appId, redirectURI: 'foobar' })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(400);
done();
});
});
it('succeeds', function (done) {
config.set('developerMode', true);
superagent.put(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.query({ access_token: token })
.send({ appId: CLIENT_1.appId, redirectURI: CLIENT_1.redirectURI })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(202);
superagent.get(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
it('fails', function (done) {
superagent.put(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.query({ access_token: token })
.send({ appId: CLIENT_1.appId, redirectURI: CLIENT_1.redirectURI })
.end(function (error, result) {
expect(error).to.be(null);
expect(result.statusCode).to.equal(200);
expect(result.body.appId).to.equal(CLIENT_1.appId);
expect(result.body.redirectURI).to.equal(CLIENT_1.redirectURI);
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(412);
done();
});
});
});
});
describe('with developer mode', function () {
before(function (done) {
settings.setDeveloperMode(true, done);
});
it('fails without token', function (done) {
superagent.put(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.send({ appId: 'someApp', redirectURI: 'http://foobar.com' })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(401);
done();
});
});
it('fails without appId', function (done) {
superagent.put(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.query({ access_token: token })
.send({ redirectURI: CLIENT_1.redirectURI })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(400);
done();
});
});
it('fails with empty appId', function (done) {
superagent.put(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.query({ access_token: token })
.send({ appId: '', redirectURI: CLIENT_1.redirectURI })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(400);
done();
});
});
it('fails without redirectURI', function (done) {
superagent.put(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.query({ access_token: token })
.send({ appId: CLIENT_1.appId })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(400);
done();
});
});
it('fails with empty redirectURI', function (done) {
superagent.put(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.query({ access_token: token })
.send({ appId: CLIENT_1.appId, redirectURI: '' })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(400);
done();
});
});
it('fails with malformed redirectURI', function (done) {
superagent.put(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.query({ access_token: token })
.send({ appId: CLIENT_1.appId, redirectURI: 'foobar' })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(400);
done();
});
});
it('succeeds', function (done) {
superagent.put(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.query({ access_token: token })
.send({ appId: CLIENT_1.appId, redirectURI: CLIENT_1.redirectURI })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(202);
superagent.get(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.query({ access_token: token })
.end(function (error, result) {
expect(error).to.be(null);
expect(result.statusCode).to.equal(200);
expect(result.body.appId).to.equal(CLIENT_1.appId);
expect(result.body.redirectURI).to.equal(CLIENT_1.redirectURI);
done();
});
});
});
});
});
@@ -506,9 +501,9 @@ describe('OAuth Clients API', function () {
});
},
function (callback) {
config.set('developerMode', true);
settings.setDeveloperMode.bind(null, true),
function (callback) {
superagent.post(SERVER_URL + '/api/v1/oauth/clients')
.query({ access_token: token })
.send({ appId: CLIENT_0.appId, redirectURI: CLIENT_0.redirectURI, scope: CLIENT_0.scope })
@@ -526,58 +521,63 @@ describe('OAuth Clients API', function () {
after(cleanup);
it('fails without token', function (done) {
config.set('developerMode', true);
superagent.del(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(401);
done();
describe('without developer mode', function () {
before(function (done) {
settings.setDeveloperMode(false, done);
});
});
it('fails if not in developerMode', function (done) {
config.set('developerMode', false);
superagent.del(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.query({ access_token: token })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(412);
done();
});
});
it('fails with unknown id', function (done) {
config.set('developerMode', true);
superagent.del(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id.toUpperCase())
.query({ access_token: token })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(404);
done();
});
});
it('succeeds', function (done) {
config.set('developerMode', true);
superagent.del(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.query({ access_token: token })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(204);
superagent.get(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
it('fails', function (done) {
superagent.del(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.query({ access_token: token })
.end(function (error, result) {
expect(error).to.be(null);
expect(result.statusCode).to.equal(404);
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(412);
done();
});
});
});
});
describe('with developer mode', function () {
before(function (done) {
settings.setDeveloperMode(true, done);
});
it('fails without token', function (done) {
superagent.del(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(401);
done();
});
});
it('fails with unknown id', function (done) {
superagent.del(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id.toUpperCase())
.query({ access_token: token })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(404);
done();
});
});
it('succeeds', function (done) {
superagent.del(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.query({ access_token: token })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(204);
superagent.get(SERVER_URL + '/api/v1/oauth/clients/' + CLIENT_0.id)
.query({ access_token: token })
.end(function (error, result) {
expect(error).to.be(null);
expect(result.statusCode).to.equal(404);
done();
});
});
});
});
});
+30 -23
View File
@@ -12,7 +12,8 @@ var async = require('async'),
expect = require('expect.js'),
nock = require('nock'),
request = require('superagent'),
server = require('../../server.js');
server = require('../../server.js'),
settings = require('../../settings.js');
var SERVER_URL = 'http://localhost:' + config.get('port');
@@ -63,37 +64,43 @@ describe('Developer API', function () {
after(cleanup);
it('fails without token', function (done) {
config.set('developerMode', true);
settings.setDeveloperMode(true, function (error) {
expect(error).to.be(null);
request.get(SERVER_URL + '/api/v1/developer')
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(401);
done();
request.get(SERVER_URL + '/api/v1/developer')
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(401);
done();
});
});
});
it('succeeds (enabled)', function (done) {
config.set('developerMode', true);
settings.setDeveloperMode(true, function (error) {
expect(error).to.be(null);
request.get(SERVER_URL + '/api/v1/developer')
.query({ access_token: token })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(200);
done();
request.get(SERVER_URL + '/api/v1/developer')
.query({ access_token: token })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(200);
done();
});
});
});
it('succeeds (not enabled)', function (done) {
config.set('developerMode', false);
settings.setDeveloperMode(false, function (error) {
expect(error).to.be(null);
request.get(SERVER_URL + '/api/v1/developer')
.query({ access_token: token })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(412);
done();
request.get(SERVER_URL + '/api/v1/developer')
.query({ access_token: token })
.end(function (error, result) {
expect(error).to.not.be.ok();
expect(result.statusCode).to.equal(412);
done();
});
});
});
});
@@ -231,11 +238,11 @@ describe('Developer API', function () {
describe('login', function () {
before(function (done) {
config.set('developerMode', true);
async.series([
setup,
settings.setDeveloperMode.bind(null, true),
function (callback) {
var scope1 = nock(config.apiServerOrigin()).get('/api/v1/boxes/' + config.fqdn() + '/setup/verify?setupToken=somesetuptoken').reply(200, {});
var scope2 = nock(config.apiServerOrigin()).post('/api/v1/boxes/' + config.fqdn() + '/setup/done?setupToken=somesetuptoken').reply(201, {});
+1 -1
View File
@@ -17,7 +17,7 @@ if [[ "$1" == "--check" ]]; then
exit 0
fi
if [[ "${NODE_ENV}" == "cloudron" ]]; then
if [[ "${BOX_ENV}" == "cloudron" ]]; then
readonly app_data_dir="${HOME}/data/$1"
btrfs subvolume create "${app_data_dir}"
mkdir -p "${app_data_dir}/data"
+1 -1
View File
@@ -12,7 +12,7 @@ if [[ $# == 1 && "$1" == "--check" ]]; then
exit 0
fi
if [[ "${NODE_ENV}" == "cloudron" ]]; then
if [[ "${BOX_ENV}" == "cloudron" ]]; then
shutdown -r now
fi
+1 -1
View File
@@ -12,7 +12,7 @@ if [[ $# == 1 && "$1" == "--check" ]]; then
exit 0
fi
if [[ "${NODE_ENV}" == "cloudron" ]]; then
if [[ "${BOX_ENV}" == "cloudron" ]]; then
/etc/init.d/collectd restart
fi
+1 -1
View File
@@ -17,7 +17,7 @@ if [[ "${OSTYPE}" == "darwin"* ]]; then
export PATH=$PATH:/usr/local/bin
fi
if [[ "${NODE_ENV}" == "cloudron" ]]; then
if [[ "${BOX_ENV}" == "cloudron" ]]; then
nginx -s reload
fi
+1 -1
View File
@@ -17,7 +17,7 @@ if [[ "$1" == "--check" ]]; then
exit 0
fi
if [[ "${NODE_ENV}" == "cloudron" ]]; then
if [[ "${BOX_ENV}" == "cloudron" ]]; then
readonly app_data_dir="${HOME}/data/$1"
if [[ -d "${app_data_dir}" ]]; then
find "${app_data_dir}" -mindepth 1 -delete
+2 -1
View File
@@ -43,7 +43,7 @@ function initializeExpressSync() {
app.set('view options', { layout: true, debug: true });
app.set('view engine', 'ejs');
if (process.env.NODE_ENV === 'test') {
if (process.env.BOX_ENV === 'test') {
app.use(express.static(path.join(__dirname, '/../webadmin')));
} else {
app.use(middleware.morgan('dev', { immediate: false }));
@@ -92,6 +92,7 @@ function initializeExpressSync() {
router.post('/api/v1/developer', developerScope, routes.user.requireAdmin, routes.user.verifyPassword, routes.developer.setEnabled);
router.get ('/api/v1/developer', developerScope, routes.developer.enabled, routes.developer.status);
router.post('/api/v1/developer/login', routes.developer.enabled, routes.developer.login);
router.get ('/api/v1/developer/apps', developerScope, routes.developer.enabled, routes.developer.apps);
// private routes
router.get ('/api/v1/cloudron/config', rootScope, routes.cloudron.getConfig);
+31
View File
@@ -17,12 +17,16 @@ exports = module.exports = {
getCloudronAvatar: getCloudronAvatar,
setCloudronAvatar: setCloudronAvatar,
getDeveloperMode: getDeveloperMode,
setDeveloperMode: setDeveloperMode,
getDefaultSync: getDefaultSync,
getAll: getAll,
AUTOUPDATE_PATTERN_KEY: 'autoupdate_pattern',
TIME_ZONE_KEY: 'time_zone',
CLOUDRON_NAME_KEY: 'cloudron_name',
DEVELOPER_MODE_KEY: 'developer_mode',
events: new (require('events').EventEmitter)()
};
@@ -45,6 +49,7 @@ var gDefaults = (function () {
result[exports.AUTOUPDATE_PATTERN_KEY] = '00 00 1,3,5,23 * * *';
result[exports.TIME_ZONE_KEY] = tz;
result[exports.CLOUDRON_NAME_KEY] = 'Cloudron';
result[exports.DEVELOPER_MODE_KEY] = false;
return result;
})();
@@ -179,6 +184,32 @@ function setCloudronAvatar(avatar, callback) {
return callback(null);
}
function getDeveloperMode(callback) {
assert.strictEqual(typeof callback, 'function');
settingsdb.get(exports.DEVELOPER_MODE_KEY, function (error, enabled) {
if (error && error.reason === DatabaseError.NOT_FOUND) return callback(null, gDefaults[exports.DEVELOPER_MODE_KEY]);
if (error) return callback(new SettingsError(SettingsError.INTERNAL_ERROR, error));
// settingsdb holds string values only
callback(null, !!enabled);
});
}
function setDeveloperMode(enabled, callback) {
assert.strictEqual(typeof enabled, 'boolean');
assert.strictEqual(typeof callback, 'function');
// settingsdb takes string values only
settingsdb.set(exports.DEVELOPER_MODE_KEY, enabled ? 'enabled' : '', function (error) {
if (error) return callback(new SettingsError(SettingsError.INTERNAL_ERROR, error));
exports.events.emit(exports.DEVELOPER_MODE_KEY, enabled);
return callback(null);
});
}
function getDefaultSync(name) {
assert.strictEqual(typeof name, 'string');
+1 -1
View File
@@ -24,7 +24,7 @@ for script in "${scripts[@]}"; do
echo "${script} does not have sudo access."
echo "You have to add the lines below to /etc/sudoers.d/yellowtent."
echo ""
echo "Defaults!${script} env_keep=\"HOME NODE_ENV\""
echo "Defaults!${script} env_keep=\"HOME BOX_ENV\""
echo "${USER} ALL=(ALL) NOPASSWD: ${script}"
echo ""
exit 1
+27
View File
@@ -33,6 +33,7 @@ describe('Settings', function () {
done();
});
});
it('can get default autoupdate_pattern', function (done) {
settings.getAutoupdatePattern(function (error, pattern) {
expect(error).to.be(null);
@@ -40,6 +41,7 @@ describe('Settings', function () {
done();
});
});
it ('can get default cloudron name', function (done) {
settings.getCloudronName(function (error, name) {
expect(error).to.be(null);
@@ -47,6 +49,7 @@ describe('Settings', function () {
done();
});
});
it('can get default cloudron avatar', function (done) {
settings.getCloudronAvatar(function (error, gravatar) {
expect(error).to.be(null);
@@ -54,6 +57,30 @@ describe('Settings', function () {
done();
});
});
it('can get default developer mode', function (done) {
settings.getDeveloperMode(function (error, enabled) {
expect(error).to.be(null);
expect(enabled).to.equal(false);
done();
});
});
it('can set developer mode', function (done) {
settings.setDeveloperMode(true, function (error) {
expect(error).to.be(null);
done();
});
});
it('can get developer mode', function (done) {
settings.getDeveloperMode(function (error, enabled) {
expect(error).to.be(null);
expect(enabled).to.equal(true);
done();
});
});
it('can get all values', function (done) {
settings.getAll(function (error, allSettings) {
expect(error).to.be(null);
+9
View File
@@ -324,6 +324,15 @@ angular.module('Application').service('Client', ['$http', 'md5', 'Notification',
}).error(defaultErrorHandler(callback));
};
Client.prototype.getNonApprovedApps = function (callback) {
if (!this._config.developerMode) return callback(null, []);
$http.get(client.apiOrigin + '/api/v1/developer/apps').success(function (data, status) {
if (status !== 200 || typeof data !== 'object') return callback(new ClientError(status, data));
callback(null, data.apps || []);
}).error(defaultErrorHandler(callback));
};
Client.prototype.getApp = function (appId, callback) {
var appFound = null;
this._installedApps.some(function (app) {
+6 -1
View File
@@ -92,7 +92,6 @@ html {
}
.highlight {
transition: background-color 500ms;
}
.highlight:hover {
@@ -147,6 +146,12 @@ html {
cursor: pointer;
}
.appstore-item-content-testing {
background-color: rgb(201, 201, 201);
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
background-size: 64px 64px;
}
.appstore-item-content-title {
text-overflow: ellipsis;
white-space: nowrap;
+3 -1
View File
@@ -94,11 +94,13 @@
<a href="" class="appstore-category-link" ng-click="showCategory($event);" ng-class="{'category-active': category === 'sync' }" category="sync">Media Sync</a>
<a href="" class="appstore-category-link" ng-click="showCategory($event);" ng-class="{'category-active': category === 'git' }" category="git">Code Hosting</a>
<a href="" class="appstore-category-link" ng-click="showCategory($event);" ng-class="{'category-active': category === 'wiki' }" category="wiki">Wiki</a>
<br/>
<a href="" class="appstore-category-link" ng-click="showCategory($event);" ng-class="{'category-active': category === 'testing' }" category="testing" ng-show="config.developerMode">Testing</a>
</div>
<div class="col-md-10" ng-show="ready && apps.length">
<div class="row-no-margin">
<div class="col-sm-1 appstore-item" ng-repeat="app in apps">
<div class="appstore-item-content highlight" ng-click="showInstall(app)">
<div class="appstore-item-content highlight" ng-click="showInstall(app)" ng-class="{ 'appstore-item-content-testing': app.publishState === 'testing' }">
<div class="appstore-item-content-icon col-same-height">
<img ng-src="{{app.iconUrl}}" onerror="this.onerror=null;this.src='img/appicon_fallback.png'" class="app-icon"/>
</div>
+27 -2
View File
@@ -20,6 +20,31 @@ angular.module('Application').controller('AppStoreController', ['$scope', '$loca
mediaLinks: []
};
function getAppList(callback) {
AppStore.getApps(function (error, apps) {
if (error) return callback(error);
// ensure we have a tags property for further use
apps.forEach(function (app) {
if (!app.manifest.tags) app.manifest.tags = [];
});
Client.getNonApprovedApps(function (error, result) {
if (error) return callback(error);
// add testing tag to the manifest for UI and search reasons
result.forEach(function (app) {
if (!app.manifest.tags) app.manifest.tags = [];
app.manifest.tags.push('testing');
});
callback(null, apps.concat(result));
});
});
}
// TODO does not support testing apps in search
$scope.search = function () {
if (!$scope.searchString) return $scope.showCategory(null, $scope.cachedCategory);
@@ -49,7 +74,7 @@ angular.module('Application').controller('AppStoreController', ['$scope', '$loca
$scope.ready = false;
AppStore.getApps(function (error, apps) {
getAppList(function (error, apps) {
if (error) return $timeout($scope.showCategory.bind(null, event), 1000);
if (!$scope.category) {
@@ -156,7 +181,7 @@ angular.module('Application').controller('AppStoreController', ['$scope', '$loca
function refresh() {
$scope.ready = false;
AppStore.getApps(function (error, apps) {
getAppList(function (error, apps) {
if (error) {
console.error(error);
return $timeout(refresh, 1000);