'use strict'; exports = module.exports = { start, stop }; const assert = require('assert'), AuditSource = require('./auditsource.js'), constants = require('./constants.js'), debug = require('debug')('box:server'), eventlog = require('./eventlog.js'), express = require('express'), http = require('http'), HttpError = require('connect-lastmile').HttpError, middleware = require('./middleware'), platform = require('./platform.js'), routes = require('./routes/index.js'), safe = require('safetydance'), users = require('./users.js'), util = require('util'), ws = require('ws'); let gHttpServer = null; function notFoundHandler(req, res, next) { const cleanUrl = req.url.replace(/(access_token=)[^&]+/, '$1' + ''); debug(`no such route: ${req.method} ${cleanUrl}`); return next(new HttpError(404, 'No such route')); } async function initializeExpressSync() { const app = express(); // disable slowloris prevention: https://github.com/nodejs/node/issues/47421 const httpServer = http.createServer({ headersTimeout: 0, requestTimeout: 0 }, app); // see also nginx client_header_timeout (30s) const wsServer = new ws.Server({ noServer: true }); // in noServer mode, we have to handle 'upgrade' and call handleUpgrade const QUERY_LIMIT = '2mb', // max size for json and urlencoded queries (see also client_max_body_size in nginx) FIELD_LIMIT = 2 * 1024 * 1024; // max fields that can appear in multipart const REQUEST_TIMEOUT = 20000; // timeout for all requests (see also setTimeout on the httpServer) const json = middleware.json({ strict: true, limit: QUERY_LIMIT }), // application/json urlencoded = middleware.urlencoded({ extended: false, limit: QUERY_LIMIT }); // application/x-www-form-urlencoded app.set('json spaces', 2); // pretty json // for rate limiting app.enable('trust proxy'); const router = new express.Router(); router.del = router.delete; // amend router.del for readability further on app // the timeout middleware will respond with a 503. the request itself cannot be 'aborted' and will continue // search for req.clearTimeout in route handlers to see places where this timeout is reset .use(middleware.timeout(REQUEST_TIMEOUT, { respond: true })) .use(urlencoded) .use(middleware.cors({ origins: [ '*' ], allowCredentials: false })) .use(router) .use(notFoundHandler) .use(middleware.lastMile()); // NOTE: routes that use multi-part have to be whitelisted in the reverse proxy const FILE_SIZE_LIMIT = '256mb', // max file size that can be uploaded (see also client_max_body_size in nginx) FILE_TIMEOUT = 60 * 1000; // increased timeout for file uploads (1 min) const multipart = middleware.multipart({ maxFieldsSize: FIELD_LIMIT, limit: FILE_SIZE_LIMIT, timeout: FILE_TIMEOUT }); // authentication const password = routes.accesscontrol.passwordAuth; const token = routes.accesscontrol.tokenAuth; // authorization const authorizeOwner = routes.accesscontrol.authorize(users.ROLE_OWNER); const authorizeAdmin = routes.accesscontrol.authorize(users.ROLE_ADMIN); const authorizeOperator = routes.accesscontrol.authorizeOperator; const authorizeUserManager = routes.accesscontrol.authorize(users.ROLE_USER_MANAGER); const authorizeMailManager = routes.accesscontrol.authorize(users.ROLE_MAIL_MANAGER); const authorizeUser = routes.accesscontrol.authorize(users.ROLE_USER); // boostrapping/provision routes router.post('/api/v1/provision/setup', json, routes.provision.setupTokenAuth, routes.provision.providerTokenAuth, routes.provision.setup); // only available until no-domain router.post('/api/v1/provision/restore', json, routes.provision.setupTokenAuth, routes.provision.restore); // only available until activated router.post('/api/v1/provision/activate', json, routes.provision.setupTokenAuth, routes.provision.activate); router.get ('/api/v1/provision/block_devices', routes.provision.getBlockDevices); router.get ('/api/v1/provision/status', routes.provision.getStatus); // auth routes router.post('/api/v1/auth/login', json, password, routes.auth.login); router.get ('/api/v1/auth/logout', token, routes.auth.logout); // this will invalidate the token if any and redirect to / always router.post('/api/v1/auth/password_reset_request', json, routes.auth.passwordResetRequest); router.post('/api/v1/auth/password_reset', json, routes.auth.passwordReset); router.post('/api/v1/auth/setup_account', json, routes.auth.setupAccount); router.get ('/api/v1/auth/branding', routes.auth.getBranding); // temp route until we server side render password_reset and setup_account router.get ('/api/v1/cloudron/status', routes.cloudron.getStatus); // healthcheck router.get ('/api/v1/cloudron/avatar', routes.branding.getCloudronAvatar); // public route for dashboard, email router.get ('/api/v1/cloudron/languages', routes.cloudron.listLanguages); router.get ('/api/v1/cloudron/language', token, authorizeAdmin, routes.cloudron.getLanguage); router.post('/api/v1/cloudron/language', json, token, authorizeAdmin, routes.cloudron.setLanguage); router.get ('/api/v1/cloudron/time_zone', token, authorizeAdmin, routes.cloudron.getTimeZone); router.post('/api/v1/cloudron/time_zone', json, token, authorizeAdmin, routes.cloudron.setTimeZone); // config route for dashboard that any auth user (not just admin) can access router.get ('/api/v1/dashboard/config', token, authorizeUser, routes.dashboard.getConfig); router.post('/api/v1/dashboard/prepare_location', json, token, authorizeAdmin, routes.dashboard.startPrepareLocation); router.post('/api/v1/dashboard/location', json, token, authorizeAdmin, routes.dashboard.changeLocation); // system (vm/server) router.get ('/api/v1/system/reboot', token, authorizeAdmin, routes.system.isRebootRequired); router.post('/api/v1/system/reboot', json, token, authorizeAdmin, routes.system.reboot); router.get ('/api/v1/system/graphs', token, authorizeAdmin, routes.system.getSystemGraphs); router.get ('/api/v1/system/disks', token, authorizeAdmin, routes.system.getDisks); router.get ('/api/v1/system/disk_usage', token, authorizeAdmin, routes.system.getDiskUsage); router.post('/api/v1/system/disk_usage', token, authorizeAdmin, routes.system.updateDiskUsage); router.get ('/api/v1/system/block_devices', token, authorizeAdmin, routes.system.getBlockDevices); router.get ('/api/v1/system/memory', token, authorizeAdmin, routes.system.getMemory); router.get ('/api/v1/system/logs/:unit', token, authorizeAdmin, routes.system.getLogs); router.get ('/api/v1/system/logstream/:unit', token, authorizeAdmin, routes.system.getLogStream); // eventlog router.get ('/api/v1/eventlog', token, authorizeAdmin, routes.eventlog.list); router.get ('/api/v1/eventlog/:eventId', token, authorizeAdmin, routes.eventlog.get); // updater router.get ('/api/v1/updater/updates', token, authorizeAdmin, routes.updater.getUpdateInfo); router.post('/api/v1/updater/update', json, token, authorizeAdmin, routes.updater.update); router.post('/api/v1/updater/check_for_updates', json, token, authorizeAdmin, routes.updater.checkForUpdates); router.get ('/api/v1/updater/autoupdate_pattern', token, authorizeAdmin, routes.updater.getAutoupdatePattern); router.post('/api/v1/updater/autoupdate_pattern', json, token, authorizeAdmin, routes.updater.setAutoupdatePattern); // task routes router.get ('/api/v1/tasks', token, authorizeAdmin, routes.tasks.list); router.get ('/api/v1/tasks/:taskId', token, authorizeAdmin, routes.tasks.load, routes.tasks.get); router.get ('/api/v1/tasks/:taskId/logs', token, authorizeAdmin, routes.tasks.load, routes.tasks.getLogs); router.get ('/api/v1/tasks/:taskId/logstream', token, authorizeAdmin, routes.tasks.load, routes.tasks.getLogStream); router.post('/api/v1/tasks/:taskId/stop', json, token, authorizeAdmin, routes.tasks.load, routes.tasks.stopTask); // notification routes (these are server level) router.get ('/api/v1/notifications', token, authorizeAdmin, routes.notifications.list); router.get ('/api/v1/notifications/:notificationId', token, authorizeAdmin, routes.notifications.load, routes.notifications.get); router.post('/api/v1/notifications/:notificationId', json, token, authorizeAdmin, routes.notifications.load, routes.notifications.update); // backup routes router.get ('/api/v1/backups', token, authorizeAdmin, routes.backups.list); router.get ('/api/v1/backups/mount_status', token, authorizeAdmin, routes.backups.getMountStatus); router.post('/api/v1/backups/create', token, authorizeAdmin, routes.backups.create); router.post('/api/v1/backups/cleanup', json, token, authorizeAdmin, routes.backups.cleanup); router.post('/api/v1/backups/remount', json, token, authorizeAdmin, routes.backups.remount); router.get ('/api/v1/backups/config', token, authorizeAdmin, routes.backups.getConfig); router.post('/api/v1/backups/config/storage', json, token, authorizeOwner, routes.backups.setStorage); router.post('/api/v1/backups/config/limits', json, token, authorizeOwner, routes.backups.setLimits); router.get ('/api/v1/backups/policy', token, authorizeAdmin, routes.backups.getPolicy); router.post('/api/v1/backups/policy', json, token, authorizeOwner, routes.backups.setPolicy); router.post('/api/v1/backups/:backupId', json, token, authorizeAdmin, routes.backups.update); // working off the user behind the provided token router.get ('/api/v1/profile', token, authorizeUser, routes.profile.get); router.post('/api/v1/profile', json, token, authorizeUser, routes.profile.authorize, routes.profile.update); router.get ('/api/v1/profile/avatar/:identifier', routes.profile.getAvatar); // this is not scoped so it can used directly in img tag router.post('/api/v1/profile/avatar', json, token, authorizeUser, (req, res, next) => { return typeof req.body.avatar === 'string' ? next() : multipart(req, res, next); }, routes.profile.setAvatar); // avatar is not exposed in LDAP. so it's personal and not locked router.get ('/api/v1/profile/background_image', token, authorizeUser, routes.profile.getBackgroundImage); router.post('/api/v1/profile/background_image', token, authorizeUser, multipart, routes.profile.setBackgroundImage); // backgroundImage is not exposed in LDAP. so it's personal and not locked router.post('/api/v1/profile/password', json, token, authorizeUser, routes.users.verifyPassword, routes.profile.setPassword); router.post('/api/v1/profile/twofactorauthentication_secret', json, token, authorizeUser, routes.profile.setTwoFactorAuthenticationSecret); router.post('/api/v1/profile/twofactorauthentication_enable', json, token, authorizeUser, routes.profile.enableTwoFactorAuthentication); router.post('/api/v1/profile/twofactorauthentication_disable', json, token, authorizeUser, routes.users.verifyPassword, routes.profile.disableTwoFactorAuthentication); // app password routes router.get ('/api/v1/app_passwords', token, authorizeUser, routes.appPasswords.list); router.post('/api/v1/app_passwords', json, token, authorizeUser, routes.appPasswords.add); router.get ('/api/v1/app_passwords/:id', token, authorizeUser, routes.appPasswords.get); router.del ('/api/v1/app_passwords/:id', token, authorizeUser, routes.appPasswords.del); // access tokens router.get ('/api/v1/tokens', token, authorizeUser, routes.tokens.list); router.post('/api/v1/tokens', json, token, authorizeUser, routes.tokens.add); router.get ('/api/v1/tokens/:id', token, authorizeUser, routes.tokens.verifyOwnership, routes.tokens.get); router.del ('/api/v1/tokens/:id', token, authorizeUser, routes.tokens.verifyOwnership, routes.tokens.del); // user routes router.get ('/api/v1/users', token, authorizeUserManager, routes.users.list); router.post('/api/v1/users', json, token, authorizeUserManager, routes.users.add); router.get ('/api/v1/users/:userId', token, authorizeUserManager, routes.users.load, routes.users.get); // this is manage scope because it returns non-restricted fields router.del ('/api/v1/users/:userId', token, authorizeUserManager, routes.users.load, routes.users.del); router.post('/api/v1/users/:userId', json, token, authorizeUserManager, routes.users.load, routes.users.update); router.post('/api/v1/users/:userId/password', json, token, authorizeUserManager, routes.users.load, routes.users.setPassword); router.post('/api/v1/users/:userId/ghost', json, token, authorizeAdmin, routes.users.load, routes.users.setGhost); router.put ('/api/v1/users/:userId/groups', json, token, authorizeUserManager, routes.users.load, routes.users.setGroups); router.post('/api/v1/users/:userId/make_local', json, token, authorizeUserManager, routes.users.load, routes.users.makeLocal); router.get ('/api/v1/users/:userId/password_reset_link', json, token, authorizeUserManager, routes.users.load, routes.users.getPasswordResetLink); router.post('/api/v1/users/:userId/send_password_reset_email', json, token, authorizeUserManager, routes.users.load, routes.users.sendPasswordResetEmail); router.get ('/api/v1/users/:userId/invite_link', json, token, authorizeUserManager, routes.users.load, routes.users.getInviteLink); router.post('/api/v1/users/:userId/send_invite_email', json, token, authorizeUserManager, routes.users.load, routes.users.sendInviteEmail); router.post('/api/v1/users/:userId/twofactorauthentication_disable', json, token, authorizeUserManager, routes.users.load, routes.users.disableTwoFactorAuthentication); // Group management router.get ('/api/v1/groups', token, authorizeUserManager, routes.groups.list); router.post('/api/v1/groups', json, token, authorizeUserManager, routes.groups.add); router.get ('/api/v1/groups/:groupId', token, authorizeUserManager, routes.groups.get); router.put ('/api/v1/groups/:groupId/members', json, token, authorizeUserManager, routes.groups.setMembers); router.post('/api/v1/groups/:groupId', json, token, authorizeUserManager, routes.groups.update); router.del ('/api/v1/groups/:groupId', token, authorizeUserManager, routes.groups.remove); // User directory router.get ('/api/v1/user_directory/profile_config', token, authorizeAdmin, routes.users.getProfileConfig); router.post('/api/v1/user_directory/profile_config', json, token, authorizeAdmin, routes.users.setProfileConfig); // External LDAP router.get ('/api/v1/external_ldap/config', token, authorizeAdmin, routes.externalLdap.getConfig); router.post('/api/v1/external_ldap/config', json, token, authorizeAdmin, routes.externalLdap.setConfig); router.post('/api/v1/external_ldap/sync', json, token, authorizeAdmin, routes.externalLdap.sync); // Directory Server router.get ('/api/v1/directory_server/config', token, authorizeAdmin, routes.directoryServer.getConfig); router.post('/api/v1/directory_server/config', json, token, authorizeAdmin, routes.directoryServer.setConfig); // appstore and subscription routes router.post('/api/v1/appstore/register_cloudron', json, token, authorizeOwner, routes.appstore.registerCloudron); router.get ('/api/v1/appstore/web_token', json, token, authorizeOwner, routes.appstore.getWebToken); router.get ('/api/v1/appstore/subscription', token, authorizeUser, routes.appstore.getSubscription); // for all users router.get ('/api/v1/appstore/apps', token, authorizeAdmin, routes.appstore.getApps); router.get ('/api/v1/appstore/apps/:appstoreId', token, authorizeAdmin, routes.appstore.getApp); router.get ('/api/v1/appstore/apps/:appstoreId/versions/:versionId', token, authorizeAdmin, routes.appstore.getAppVersion); // app routes router.post('/api/v1/apps/install', json, token, authorizeAdmin, routes.apps.install); router.get ('/api/v1/apps', token, authorizeUser, routes.apps.listByUser); router.get ('/api/v1/apps/:id', token, routes.apps.load, authorizeOperator, routes.apps.getApp); router.get ('/api/v1/apps/:id/icon', routes.apps.load, routes.apps.getAppIcon); router.post('/api/v1/apps/:id/uninstall', json, token, routes.apps.load, authorizeAdmin, routes.apps.uninstall); router.post('/api/v1/apps/:id/configure/access_restriction', json, token, routes.apps.load, authorizeAdmin, routes.apps.setAccessRestriction); router.post('/api/v1/apps/:id/configure/operators', json, token, routes.apps.load, authorizeAdmin, routes.apps.setOperators); router.post('/api/v1/apps/:id/configure/label', json, token, routes.apps.load, authorizeOperator, routes.apps.setLabel); router.post('/api/v1/apps/:id/configure/tags', json, token, routes.apps.load, authorizeOperator, routes.apps.setTags); router.post('/api/v1/apps/:id/configure/icon', json, token, routes.apps.load, authorizeOperator, routes.apps.setIcon); router.post('/api/v1/apps/:id/configure/memory_limit', json, token, routes.apps.load, authorizeOperator, routes.apps.setMemoryLimit); router.post('/api/v1/apps/:id/configure/cpu_shares', json, token, routes.apps.load, authorizeOperator, routes.apps.setCpuShares); router.post('/api/v1/apps/:id/configure/automatic_backup', json, token, routes.apps.load, authorizeOperator, routes.apps.setAutomaticBackup); router.post('/api/v1/apps/:id/configure/automatic_update', json, token, routes.apps.load, authorizeOperator, routes.apps.setAutomaticUpdate); router.post('/api/v1/apps/:id/configure/reverse_proxy', json, token, routes.apps.load, authorizeOperator, routes.apps.setReverseProxyConfig); router.post('/api/v1/apps/:id/configure/cert', json, token, routes.apps.load, authorizeOperator, routes.apps.setCertificate); router.post('/api/v1/apps/:id/configure/debug_mode', json, token, routes.apps.load, authorizeOperator, routes.apps.setDebugMode); router.post('/api/v1/apps/:id/configure/mailbox', json, token, routes.apps.load, authorizeAdmin, routes.apps.setMailbox); router.post('/api/v1/apps/:id/configure/inbox', json, token, routes.apps.load, authorizeAdmin, routes.apps.setInbox); router.post('/api/v1/apps/:id/configure/turn', json, token, routes.apps.load, authorizeAdmin, routes.apps.setTurn); router.post('/api/v1/apps/:id/configure/redis', json, token, routes.apps.load, authorizeAdmin, routes.apps.setRedis); router.post('/api/v1/apps/:id/configure/env', json, token, routes.apps.load, authorizeOperator, routes.apps.setEnvironment); router.post('/api/v1/apps/:id/configure/storage', json, token, routes.apps.load, authorizeAdmin, routes.apps.setStorage); router.post('/api/v1/apps/:id/configure/location', json, token, routes.apps.load, authorizeAdmin, routes.apps.setLocation); router.post('/api/v1/apps/:id/configure/mounts', json, token, routes.apps.load, authorizeAdmin, routes.apps.setMounts); router.post('/api/v1/apps/:id/configure/crontab', json, token, routes.apps.load, authorizeOperator, routes.apps.setCrontab); router.post('/api/v1/apps/:id/configure/upstream_uri', json, token, routes.apps.load, authorizeOperator, routes.apps.setUpstreamUri); router.post('/api/v1/apps/:id/repair', json, token, routes.apps.load, authorizeOperator, routes.apps.repair); router.post('/api/v1/apps/:id/check_for_updates', json, token, routes.apps.load, authorizeOperator, routes.apps.checkForUpdates); router.post('/api/v1/apps/:id/update', json, token, routes.apps.load, authorizeOperator, routes.apps.update); router.post('/api/v1/apps/:id/restore', json, token, routes.apps.load, authorizeOperator, routes.apps.restore); router.post('/api/v1/apps/:id/import', json, token, routes.apps.load, authorizeOperator, routes.apps.importApp); router.post('/api/v1/apps/:id/export', json, token, routes.apps.load, authorizeOperator, routes.apps.exportApp); router.post('/api/v1/apps/:id/backup', json, token, routes.apps.load, authorizeOperator, routes.apps.backup); router.get ('/api/v1/apps/:id/backups', token, routes.apps.load, authorizeOperator, routes.apps.listBackups); router.post('/api/v1/apps/:id/backups/:backupId', json, token, routes.apps.load, authorizeOperator, routes.apps.updateBackup); router.get ('/api/v1/apps/:id/backups/:backupId/download', token, routes.apps.load, authorizeOperator, routes.apps.downloadBackup); router.post('/api/v1/apps/:id/start', json, token, routes.apps.load, authorizeOperator, routes.apps.start); router.post('/api/v1/apps/:id/stop', json, token, routes.apps.load, authorizeOperator, routes.apps.stop); router.post('/api/v1/apps/:id/restart', json, token, routes.apps.load, authorizeOperator, routes.apps.restart); router.get ('/api/v1/apps/:id/logstream', token, routes.apps.load, authorizeOperator, routes.apps.getLogStream); router.get ('/api/v1/apps/:id/logs', token, routes.apps.load, authorizeOperator, routes.apps.getLogs); router.get ('/api/v1/apps/:id/eventlog', token, routes.apps.load, authorizeOperator, routes.apps.listEventlog); router.get ('/api/v1/apps/:id/limits', token, routes.apps.load, authorizeOperator, routes.apps.getLimits); router.get ('/api/v1/apps/:id/task', token, routes.apps.load, authorizeOperator, routes.apps.getTask); router.get ('/api/v1/apps/:id/graphs', token, routes.apps.load, authorizeOperator, routes.apps.getGraphs); router.post('/api/v1/apps/:id/clone', json, token, routes.apps.load, authorizeAdmin, routes.apps.clone); router.get ('/api/v1/apps/:id/download', token, routes.apps.load, authorizeOperator, routes.apps.downloadFile); router.post('/api/v1/apps/:id/upload', json, token, multipart, routes.apps.load, authorizeOperator, routes.apps.uploadFile); router.use ('/api/v1/apps/:id/files/*', token, routes.apps.load, authorizeOperator, routes.filemanager.proxy('app')); router.post('/api/v1/apps/:id/exec', json, token, routes.apps.load, authorizeOperator, routes.apps.createExec); router.get ('/api/v1/apps/:id/exec/:execId/start', token, routes.apps.load, authorizeOperator, routes.apps.startExec); router.get ('/api/v1/apps/:id/exec/:execId', token, routes.apps.load, authorizeOperator, routes.apps.getExec); // websocket cannot do bearer authentication router.get ('/api/v1/apps/:id/exec/:execId/startws', token, routes.apps.load, authorizeOperator, routes.apps.startExecWebSocket); // app links in dashboard router.get ('/api/v1/applinks', token, authorizeUser, routes.applinks.listByUser); router.post('/api/v1/applinks', json, token, authorizeAdmin, routes.applinks.add); router.get ('/api/v1/applinks/:id', token, authorizeAdmin, routes.applinks.get); router.post('/api/v1/applinks/:id', json, token, authorizeAdmin, routes.applinks.update); router.del ('/api/v1/applinks/:id', token, authorizeAdmin, routes.applinks.remove); router.get ('/api/v1/applinks/:id/icon', token, authorizeUser, routes.applinks.getIcon); // branding routes router.get ('/api/v1/branding/cloudron_name', token, authorizeOwner, routes.branding.getCloudronName); router.post('/api/v1/branding/cloudron_name', json, token, authorizeOwner, routes.branding.setCloudronName); router.get ('/api/v1/branding/cloudron_avatar', token, authorizeOwner, routes.branding.getCloudronAvatar); router.post('/api/v1/branding/cloudron_avatar', json, token, authorizeOwner, multipart, routes.branding.setCloudronAvatar); router.get ('/api/v1/branding/footer', token, authorizeOwner, routes.branding.getFooter); router.post('/api/v1/branding/footer', json, token, authorizeOwner, routes.branding.setFooter); // reverseproxy routes router.post('/api/v1/reverseproxy/renew_certs', json, token, authorizeAdmin, routes.reverseProxy.renewCerts); router.get ('/api/v1/reverseproxy/trusted_ips', token, authorizeAdmin, routes.reverseProxy.getTrustedIps); router.post('/api/v1/reverseproxy/trusted_ips', json, token, authorizeAdmin, routes.reverseProxy.setTrustedIps); // network routes router.get ('/api/v1/network/blocklist', token, authorizeOwner, routes.network.getBlocklist); router.post('/api/v1/network/blocklist', json, token, authorizeOwner, routes.network.setBlocklist); router.get ('/api/v1/network/dynamic_dns', token, authorizeAdmin, routes.network.getDynamicDns); router.post('/api/v1/network/dynamic_dns', json, token, authorizeAdmin, routes.network.setDynamicDns); router.get ('/api/v1/network/ipv4_config', token, authorizeAdmin, routes.network.getIPv4Config); router.post('/api/v1/network/ipv4_config', json, token, authorizeAdmin, routes.network.setIPv4Config); router.get ('/api/v1/network/ipv6_config', token, authorizeAdmin, routes.network.getIPv6Config); router.post('/api/v1/network/ipv6_config', json, token, authorizeAdmin, routes.network.setIPv6Config); router.get ('/api/v1/network/ipv4', token, authorizeAdmin, routes.network.getIPv4); router.get ('/api/v1/network/ipv6', token, authorizeAdmin, routes.network.getIPv6); // docker router.get ('/api/v1/docker/registry_config', token, authorizeAdmin, routes.docker.getRegistryConfig); router.post('/api/v1/docker/registry_config', json, token, authorizeAdmin, routes.docker.setRegistryConfig); // email routes router.get ('/api/v1/mailserver/eventlog', token, authorizeOwner, routes.mailserver.proxy); router.post('/api/v1/mailserver/clear_eventlog', token, authorizeOwner, routes.mailserver.proxy); router.use ('/api/v1/mailserver/files/*', token, authorizeOwner, routes.filemanager.proxy('mail')); router.get ('/api/v1/mailserver/location', token, authorizeAdmin, routes.mailserver.getLocation); router.post('/api/v1/mailserver/location', json, token, authorizeAdmin, routes.mailserver.setLocation); router.get ('/api/v1/mailserver/max_email_size', token, authorizeAdmin, routes.mailserver.proxy); router.post('/api/v1/mailserver/max_email_size', token, authorizeAdmin, routes.mailserver.proxy); router.get ('/api/v1/mailserver/spam_acl', token, authorizeAdmin, routes.mailserver.proxy); router.post('/api/v1/mailserver/spam_acl', token, authorizeAdmin, routes.mailserver.proxy); router.get ('/api/v1/mailserver/spam_custom_config', token, authorizeAdmin, routes.mailserver.proxy); router.post('/api/v1/mailserver/spam_custom_config', token, authorizeAdmin, routes.mailserver.proxy); router.get ('/api/v1/mailserver/dnsbl_config', token, authorizeAdmin, routes.mailserver.proxy); router.post('/api/v1/mailserver/dnsbl_config', token, authorizeAdmin, routes.mailserver.proxy); router.get ('/api/v1/mailserver/solr_config', token, authorizeAdmin, routes.mailserver.proxy); router.post('/api/v1/mailserver/solr_config', token, authorizeAdmin, routes.mailserver.proxy, routes.mailserver.restart); router.get ('/api/v1/mailserver/mailbox_sharing', token, authorizeAdmin, routes.mailserver.proxy); router.post('/api/v1/mailserver/mailbox_sharing', token, authorizeAdmin, routes.mailserver.proxy, routes.mailserver.restart); router.get ('/api/v1/mailserver/usage', token, authorizeMailManager, routes.mailserver.proxy); router.use ('/api/v1/mailserver/queue', token, authorizeAdmin, routes.mailserver.queueProxy); router.get ('/api/v1/mail/:domain', token, authorizeMailManager, routes.mail.getDomain); router.post('/api/v1/mail/:domain/enable', json, token, authorizeAdmin, routes.mail.setMailEnabled); router.get ('/api/v1/mail/:domain/status', token, authorizeMailManager, routes.mail.getStatus); router.post('/api/v1/mail/:domain/mail_from_validation', json, token, authorizeMailManager, routes.mail.setMailFromValidation); router.post('/api/v1/mail/:domain/catch_all', json, token, authorizeMailManager, routes.mail.setCatchAllAddress); router.post('/api/v1/mail/:domain/relay', json, token, authorizeAdmin, routes.mail.setMailRelay); router.post('/api/v1/mail/:domain/banner', json, token, authorizeMailManager, routes.mail.setBanner); router.post('/api/v1/mail/:domain/send_test_mail', json, token, authorizeMailManager, routes.mail.sendTestMail); router.get ('/api/v1/mail/:domain/mailbox_count', token, authorizeMailManager, routes.mail.getMailboxCount); router.get ('/api/v1/mail/:domain/mailboxes', token, authorizeMailManager, routes.mail.listMailboxes); router.get ('/api/v1/mail/:domain/mailboxes/:name', token, authorizeMailManager, routes.mail.getMailbox); router.post('/api/v1/mail/:domain/mailboxes', json, token, authorizeMailManager, routes.mail.addMailbox); router.post('/api/v1/mail/:domain/mailboxes/:name', json, token, authorizeMailManager, routes.mail.updateMailbox); router.del ('/api/v1/mail/:domain/mailboxes/:name', json, token, authorizeMailManager, routes.mail.delMailbox); router.get ('/api/v1/mail/:domain/mailboxes/:name/aliases', token, authorizeMailManager, routes.mail.getAliases); router.put ('/api/v1/mail/:domain/mailboxes/:name/aliases', json, token, authorizeMailManager, routes.mail.setAliases); router.get ('/api/v1/mail/:domain/lists', token, authorizeMailManager, routes.mail.getLists); router.post('/api/v1/mail/:domain/lists', json, token, authorizeMailManager, routes.mail.addList); router.get ('/api/v1/mail/:domain/lists/:name', token, authorizeMailManager, routes.mail.getList); router.post('/api/v1/mail/:domain/lists/:name', json, token, authorizeMailManager, routes.mail.updateList); router.del ('/api/v1/mail/:domain/lists/:name', token, authorizeMailManager, routes.mail.delList); // support routes router.post('/api/v1/support/ticket', json, token, authorizeOwner, routes.support.canCreateTicket, routes.support.createTicket); router.get ('/api/v1/support/config', token, authorizeOwner, routes.support.getConfig); router.get ('/api/v1/support/remote_support', token, authorizeOwner, routes.support.getRemoteSupport); router.post('/api/v1/support/remote_support', json, token, authorizeOwner, routes.support.canEnableRemoteSupport, routes.support.enableRemoteSupport); // domain routes router.post('/api/v1/domains', json, token, authorizeAdmin, routes.domains.add); router.post('/api/v1/domains/sync_dns', json, token, authorizeAdmin, routes.domains.syncDnsRecords); router.get ('/api/v1/domains', token, authorizeUser, routes.domains.list); router.get ('/api/v1/domains/:domain', token, authorizeAdmin, routes.domains.get); // this is manage scope because it returns non-restricted fields router.post('/api/v1/domains/:domain/config', json, token, authorizeAdmin, routes.domains.setConfig); router.post('/api/v1/domains/:domain/wellknown', json, token, authorizeAdmin, routes.domains.setWellKnown); router.del ('/api/v1/domains/:domain', token, authorizeAdmin, routes.domains.del); router.get ('/api/v1/domains/:domain/dns_check', token, authorizeAdmin, routes.domains.checkDnsRecords); // volume routes router.post('/api/v1/volumes', json, token, authorizeAdmin, routes.volumes.add); router.get ('/api/v1/volumes', token, authorizeAdmin, routes.volumes.list); router.get ('/api/v1/volumes/:id', token, authorizeAdmin, routes.volumes.load, routes.volumes.get); router.post('/api/v1/volumes/:id', json, token, authorizeAdmin, routes.volumes.load, routes.volumes.update); router.del ('/api/v1/volumes/:id', token, authorizeAdmin, routes.volumes.load, routes.volumes.del); router.get ('/api/v1/volumes/:id/status', token, authorizeAdmin, routes.volumes.load, routes.volumes.getStatus); router.post('/api/v1/volumes/:id/remount', token, authorizeAdmin, routes.volumes.load, routes.volumes.remount); router.use ('/api/v1/volumes/:id/files/*', token, authorizeAdmin, routes.filemanager.proxy('volume')); // service routes router.get ('/api/v1/services', token, authorizeAdmin, routes.services.list); router.get ('/api/v1/services/platform_status', token, authorizeUser, routes.services.getPlatformStatus); router.get ('/api/v1/services/:service', token, authorizeAdmin, routes.services.get); router.post('/api/v1/services/:service', json, token, authorizeAdmin, routes.services.configure); router.get ('/api/v1/services/:service/graphs', token, authorizeAdmin, routes.services.getGraphs); router.get ('/api/v1/services/:service/logs', token, authorizeAdmin, routes.services.getLogs); router.get ('/api/v1/services/:service/logstream', token, authorizeAdmin, routes.services.getLogStream); router.post('/api/v1/services/:service/restart', json, token, authorizeAdmin, routes.services.restart); router.post('/api/v1/services/:service/rebuild', json, token, authorizeAdmin, routes.services.rebuild); // well known router.get ('/well-known-handler/*', routes.wellknown.get); // OpenID connect clients router.get ('/api/v1/oidc/clients', token, authorizeAdmin, routes.oidc.clients.list); router.post('/api/v1/oidc/clients', json, token, authorizeAdmin, routes.oidc.clients.add); router.get ('/api/v1/oidc/clients/:clientId', token, authorizeAdmin, routes.oidc.clients.get); router.post('/api/v1/oidc/clients/:clientId', json, token, authorizeAdmin, routes.oidc.clients.update); router.del ('/api/v1/oidc/clients/:clientId', token, authorizeAdmin, routes.oidc.clients.del); // OpenID connect sessions router.del ('/api/v1/oidc/sessions', token, authorizeUser, routes.oidc.destroyUserSession); // upgrade handler httpServer.on('upgrade', function (req, socket, head) { // create a node response object for express const res = new http.ServerResponse({}); res.assignSocket(socket); if (req.headers.upgrade === 'websocket') { res.handleUpgrade = function (callback) { wsServer.handleUpgrade(req, socket, head, callback); }; } else { res.sendUpgradeHandshake = function () { // could extend express.response as well socket.write('HTTP/1.1 101 TCP Handshake\r\n' + 'Upgrade: tcp\r\n' + 'Connection: Upgrade\r\n' + '\r\n'); }; } // route through express middleware. if we provide no callback, express will provide a 'finalhandler' // TODO: it's not clear if socket needs to be destroyed app(req, res); }); return httpServer; } async function start() { assert.strictEqual(gHttpServer, null, 'Server is already up and running.'); debug('=========================================='); debug(` Cloudron ${constants.VERSION} `); debug('=========================================='); await platform.initialize(); gHttpServer = await initializeExpressSync(); await util.promisify(gHttpServer.listen.bind(gHttpServer))(constants.PORT, '127.0.0.1'); await safe(eventlog.add(eventlog.ACTION_START, AuditSource.BOOT, { version: constants.VERSION })); // can fail if db down } async function stop() { if (!gHttpServer) return; await platform.uninitialize(); await util.promisify(gHttpServer.close.bind(gHttpServer))(); gHttpServer = null; }