diff --git a/server/package-lock.json b/server/package-lock.json index 3768fc551..d6babc345 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -17,7 +17,7 @@ "body-parser": "^1.19.0", "cookie-parser": "^1.4.5", "debug": "^4.3.1", - "engine.io": "^5.2.0", + "engine.io": "^6.2.0", "express": "^4.17.2", "http-auth": "^4.1.9", "level": "^6.0.1", @@ -50,7 +50,6 @@ "@types/adm-zip": "^0.4.33", "@types/cookie-parser": "^1.4.2", "@types/debug": "^4.1.5", - "@types/engine.io": "^3.1.5", "@types/express": "^4.17.11", "@types/http-auth": "^4.1.1", "@types/lodash": "^4.14.168", @@ -177,6 +176,11 @@ "@types/node": "*" } }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, "node_modules/@types/cookie-parser": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.2.tgz", @@ -186,6 +190,11 @@ "@types/express": "*" } }, + "node_modules/@types/cors": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", + "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" + }, "node_modules/@types/debug": { "version": "4.1.7", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", @@ -195,15 +204,6 @@ "@types/ms": "*" } }, - "node_modules/@types/engine.io": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@types/engine.io/-/engine.io-3.1.7.tgz", - "integrity": "sha512-qNjVXcrp+1sS8YpRUa714r0pgzOwESdW5UjHL7D/2ZFdBX0BXUXtg1LUrp+ylvqbvMcMWUy73YpRoxPN2VoKAQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/express": { "version": "4.17.13", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", @@ -291,8 +291,7 @@ "node_modules/@types/node": { "version": "17.0.15", "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.15.tgz", - "integrity": "sha512-zWt4SDDv1S9WRBNxLFxFRHxdD9tvH8f5/kg5/IaLFdnSNXsDY4eL3Q3XXN+VxUnWIhyVFDwcsmAprvwXoM/ClA==", - "dev": true + "integrity": "sha512-zWt4SDDv1S9WRBNxLFxFRHxdD9tvH8f5/kg5/IaLFdnSNXsDY4eL3Q3XXN+VxUnWIhyVFDwcsmAprvwXoM/ClA==" }, "node_modules/@types/node-dijkstra": { "version": "2.5.2", @@ -534,14 +533,6 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "node_modules/base64-arraybuffer": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", - "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=", - "engines": { - "node": ">= 0.6.0" - } - }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -895,39 +886,39 @@ } }, "node_modules/engine.io": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-5.2.1.tgz", - "integrity": "sha512-hyNxjVgWp619QMfqi/+/6/LQF+ueOIWeVOza3TeyvxUGjeT9U/xPkkHW/NJNuhbStrxMujEoMadoc2EY7DDEnw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.0.tgz", + "integrity": "sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg==", "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "2.0.0", "cookie": "~0.4.1", "cors": "~2.8.5", "debug": "~4.3.1", - "engine.io-parser": "~4.0.0", - "ws": "~7.4.2" + "engine.io-parser": "~5.0.3", + "ws": "~8.2.3" }, "engines": { "node": ">=10.0.0" } }, "node_modules/engine.io-parser": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.3.tgz", - "integrity": "sha512-xEAAY0msNnESNPc00e19y5heTPX4y/TJ36gr8t1voOaNmTojP9b3oK3BbJLFufW2XFPQaaijpFewm2g2Um3uqA==", - "dependencies": { - "base64-arraybuffer": "0.1.4" - }, + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", + "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==", "engines": { - "node": ">=8.0.0" + "node": ">=10.0.0" } }, "node_modules/engine.io/node_modules/ws": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", - "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", "engines": { - "node": ">=8.3.0" + "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", @@ -2630,6 +2621,11 @@ "@types/node": "*" } }, + "@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, "@types/cookie-parser": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.2.tgz", @@ -2639,6 +2635,11 @@ "@types/express": "*" } }, + "@types/cors": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", + "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" + }, "@types/debug": { "version": "4.1.7", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", @@ -2648,15 +2649,6 @@ "@types/ms": "*" } }, - "@types/engine.io": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@types/engine.io/-/engine.io-3.1.7.tgz", - "integrity": "sha512-qNjVXcrp+1sS8YpRUa714r0pgzOwESdW5UjHL7D/2ZFdBX0BXUXtg1LUrp+ylvqbvMcMWUy73YpRoxPN2VoKAQ==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, "@types/express": { "version": "4.17.13", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", @@ -2744,8 +2736,7 @@ "@types/node": { "version": "17.0.15", "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.15.tgz", - "integrity": "sha512-zWt4SDDv1S9WRBNxLFxFRHxdD9tvH8f5/kg5/IaLFdnSNXsDY4eL3Q3XXN+VxUnWIhyVFDwcsmAprvwXoM/ClA==", - "dev": true + "integrity": "sha512-zWt4SDDv1S9WRBNxLFxFRHxdD9tvH8f5/kg5/IaLFdnSNXsDY4eL3Q3XXN+VxUnWIhyVFDwcsmAprvwXoM/ClA==" }, "@types/node-dijkstra": { "version": "2.5.2", @@ -2957,11 +2948,6 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "base64-arraybuffer": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", - "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=" - }, "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -3223,34 +3209,34 @@ } }, "engine.io": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-5.2.1.tgz", - "integrity": "sha512-hyNxjVgWp619QMfqi/+/6/LQF+ueOIWeVOza3TeyvxUGjeT9U/xPkkHW/NJNuhbStrxMujEoMadoc2EY7DDEnw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.0.tgz", + "integrity": "sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg==", "requires": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "2.0.0", "cookie": "~0.4.1", "cors": "~2.8.5", "debug": "~4.3.1", - "engine.io-parser": "~4.0.0", - "ws": "~7.4.2" + "engine.io-parser": "~5.0.3", + "ws": "~8.2.3" }, "dependencies": { "ws": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", - "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", "requires": {} } } }, "engine.io-parser": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.3.tgz", - "integrity": "sha512-xEAAY0msNnESNPc00e19y5heTPX4y/TJ36gr8t1voOaNmTojP9b3oK3BbJLFufW2XFPQaaijpFewm2g2Um3uqA==", - "requires": { - "base64-arraybuffer": "0.1.4" - } + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", + "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==" }, "env-paths": { "version": "2.2.1", diff --git a/server/package.json b/server/package.json index 362c05bc4..8f8b166a1 100644 --- a/server/package.json +++ b/server/package.json @@ -11,7 +11,7 @@ "body-parser": "^1.19.0", "cookie-parser": "^1.4.5", "debug": "^4.3.1", - "engine.io": "^5.2.0", + "engine.io": "^6.2.0", "express": "^4.17.2", "http-auth": "^4.1.9", "level": "^6.0.1", @@ -44,7 +44,6 @@ "@types/adm-zip": "^0.4.33", "@types/cookie-parser": "^1.4.2", "@types/debug": "^4.1.5", - "@types/engine.io": "^3.1.5", "@types/express": "^4.17.11", "@types/http-auth": "^4.1.1", "@types/lodash": "^4.14.168", diff --git a/server/src/plugin/plugin-host-api.ts b/server/src/plugin/plugin-host-api.ts index f19d625c5..ab6c2addc 100644 --- a/server/src/plugin/plugin-host-api.ts +++ b/server/src/plugin/plugin-host-api.ts @@ -103,11 +103,13 @@ export class PluginHostAPI extends PluginAPIManagedListeners implements PluginAP } async ioClose(id: string) { + // @ts-ignore this.pluginHost.io.clients[id]?.close(); this.pluginHost.ws[id]?.close(); } async ioSend(id: string, message: string) { + // @ts-ignore this.pluginHost.io.clients[id]?.send(message); this.pluginHost.ws[id]?.send(message); } diff --git a/server/src/plugin/plugin-host.ts b/server/src/plugin/plugin-host.ts index 26bfd698a..9a7f94e80 100644 --- a/server/src/plugin/plugin-host.ts +++ b/server/src/plugin/plugin-host.ts @@ -1,13 +1,14 @@ import { Device, EngineIOHandler } from '@scrypted/types'; import AdmZip from 'adm-zip'; import crypto from 'crypto'; -import io, { Socket } from 'engine.io'; +import * as io from 'engine.io'; import fs from 'fs'; import mkdirp from 'mkdirp'; import path from 'path'; import rimraf from 'rimraf'; import WebSocket from 'ws'; import { Plugin } from '../db-types'; +import { IOServer, IOSocket } from '../io'; import { Logger } from '../logger'; import { RpcPeer } from '../rpc'; import { ScryptedRuntime } from '../runtime'; @@ -34,8 +35,15 @@ export class PluginHost { module: Promise; scrypted: ScryptedRuntime; remote: PluginRemote; - io = io(undefined, { + io: IOServer = new io.Server({ pingTimeout: 120000, + cors: (req, callback) => { + const header = this.scrypted.getAccessControlAllowOrigin(req.headers); + callback(undefined, { + origin: header, + credentials: true, + }) + }, }); ws: { [id: string]: WebSocket } = {}; api: PluginHostAPI; @@ -140,13 +148,13 @@ export class PluginHost { const handler = this.scrypted.getDevice(pluginDevice._id); socket.on('message', message => { - this.remote.ioEvent(socket.id, 'message', message) + this.remote.ioEvent(socket.transport.sid, 'message', message) }); socket.on('close', reason => { - this.remote.ioEvent(socket.id, 'close'); + this.remote.ioEvent(socket.transport.sid, 'close'); }); - await handler.onConnection(endpointRequest, `io://${socket.id}`); + await handler.onConnection(endpointRequest, `io://${socket.transport.sid}`); } catch (e) { console.error('engine.io plugin error', e); @@ -318,7 +326,7 @@ export class PluginHost { }; } - async createRpcIoPeer(socket: Socket) { + async createRpcIoPeer(socket: IOSocket) { let connected = true; const rpcPeer = new RpcPeer(`api/${this.pluginId}`, 'web', (message, reject) => { if (!connected) diff --git a/server/src/plugin/plugin-http.ts b/server/src/plugin/plugin-http.ts index 3b81cc8f4..71147b959 100644 --- a/server/src/plugin/plugin-http.ts +++ b/server/src/plugin/plugin-http.ts @@ -8,22 +8,25 @@ export abstract class PluginHttp { wss = new WebSocketServer({ noServer: true }); constructor(public app: Router) { - app.all(['/endpoint/@:owner/:pkg/public/engine.io/*', '/endpoint/:pkg/public/engine.io/*'], (req, res) => { + } + + addMiddleware() { + this.app.all(['/endpoint/@:owner/:pkg/public/engine.io/*', '/endpoint/:pkg/public/engine.io/*'], (req, res) => { this.endpointHandler(req, res, true, true, this.handleEngineIOEndpoint.bind(this)) }); - app.all(['/endpoint/@:owner/:pkg/engine.io/*', '/endpoint/@:owner/:pkg/engine.io/*'], (req, res) => { + this.app.all(['/endpoint/@:owner/:pkg/engine.io/*', '/endpoint/@:owner/:pkg/engine.io/*'], (req, res) => { this.endpointHandler(req, res, false, true, this.handleEngineIOEndpoint.bind(this)) }); // stringify all http endpoints - app.all(['/endpoint/@:owner/:pkg/public', '/endpoint/@:owner/:pkg/public/*', '/endpoint/:pkg', '/endpoint/:pkg/*'], bodyParser.text() as any); + this.app.all(['/endpoint/@:owner/:pkg/public', '/endpoint/@:owner/:pkg/public/*', '/endpoint/:pkg', '/endpoint/:pkg/*'], bodyParser.text() as any); - app.all(['/endpoint/@:owner/:pkg/public', '/endpoint/@:owner/:pkg/public/*', '/endpoint/:pkg/public', '/endpoint/:pkg/public/*'], (req, res) => { + this.app.all(['/endpoint/@:owner/:pkg/public', '/endpoint/@:owner/:pkg/public/*', '/endpoint/:pkg/public', '/endpoint/:pkg/public/*'], (req, res) => { this.endpointHandler(req, res, true, false, this.handleRequestEndpoint.bind(this)) }); - app.all(['/endpoint/@:owner/:pkg', '/endpoint/@:owner/:pkg/*', '/endpoint/:pkg', '/endpoint/:pkg/*'], (req, res) => { + this.app.all(['/endpoint/@:owner/:pkg', '/endpoint/@:owner/:pkg/*', '/endpoint/:pkg', '/endpoint/:pkg/*'], (req, res) => { this.endpointHandler(req, res, false, false, this.handleRequestEndpoint.bind(this)) }); } diff --git a/server/src/runtime.ts b/server/src/runtime.ts index 2b9c65ef2..4edbd00eb 100644 --- a/server/src/runtime.ts +++ b/server/src/runtime.ts @@ -6,7 +6,7 @@ import { Plugin, PluginDevice, ScryptedAlert } from './db-types'; import { getState, ScryptedStateManager, setState } from './state'; import { Request, Response } from 'express'; import { createResponseInterface } from './http-interfaces'; -import http, { ServerResponse } from 'http'; +import http, { ServerResponse, IncomingHttpHeaders } from 'http'; import https from 'https'; import express from 'express'; import { LogEntry, Logger, makeAlertId } from './logger'; @@ -25,13 +25,15 @@ import semver from 'semver'; import { ServiceControl } from './services/service-control'; import { Alerts } from './services/alerts'; import { Info } from './services/info'; -import io from 'engine.io'; +import * as io from 'engine.io'; import { spawn as ptySpawn } from 'node-pty'; import rimraf from 'rimraf'; import { getPluginVolume } from './plugin/plugin-volume'; import { PluginHttp } from './plugin/plugin-http'; import AdmZip from 'adm-zip'; import path from 'path'; +import { CORSControl, CORSServer } from './services/cors'; +import { IOServer } from './io'; interface DeviceProxyPair { handler: PluginDeviceProxyHandler; @@ -56,9 +58,17 @@ export class ScryptedRuntime extends PluginHttp { devicesLogger = this.logger.getLogger('device', 'Devices'); wss = new WebSocketServer({ noServer: true }); wsAtomic = 0; - shellio = io(undefined, { + shellio: IOServer = new io.Server({ pingTimeout: 120000, + cors: (req, callback) => { + const header = this.getAccessControlAllowOrigin(req.headers); + callback(undefined, { + origin: header, + credentials: true, + }) + }, }); + cors: CORSServer[] = []; constructor(datastore: Level, insecure: http.Server, secure: https.Server, app: express.Application) { super(app); @@ -67,6 +77,8 @@ export class ScryptedRuntime extends PluginHttp { app.disable('x-powered-by'); + this.addMiddleware(); + app.get('/web/oauth/callback', (req, res) => { this.oauthCallback(req, res); }); @@ -122,6 +134,27 @@ export class ScryptedRuntime extends PluginHttp { }, 60 * 60 * 1000); } + getAccessControlAllowOrigin(headers: IncomingHttpHeaders) { + let { origin, referer } = headers; + if (!origin && referer) { + try { + const u = new URL(headers.referer) + origin = u.origin; + } + catch (e) { + return; + } + } + if (!origin) + return; + const servers: string[] = process.env.SCRYPTED_ACCESS_CONTROL_ALLOW_ORIGINS?.split(',') || []; + servers.push(...Object.values(this.cors).map(entry => entry.server)); + if (!servers.includes(origin)) + return; + + return origin; + } + getDeviceLogger(device: PluginDevice): Logger { return this.devicesLogger.getLogger(device._id, getState(device, ScryptedInterfaceProperty.name)); } @@ -311,6 +344,8 @@ export class ScryptedRuntime extends PluginHttp { return this.logger; case 'alerts': return new Alerts(this); + case 'cors': + return new CORSControl(this); } } diff --git a/server/src/scrypted-server-main.ts b/server/src/scrypted-server-main.ts index c51e22e32..6a0c0082c 100644 --- a/server/src/scrypted-server-main.ts +++ b/server/src/scrypted-server-main.ts @@ -360,6 +360,19 @@ async function start() { let hasLogin = await db.getCount(ScryptedUser) > 0; + app.options('/login', (req, res) => { + res.setHeader('Vary', 'Origin,Referer'); + res.set('Access-Control-Allow-Credentials', 'true'); + + const header = scrypted.getAccessControlAllowOrigin(req.headers); + if (header) + res.setHeader('Access-Control-Allow-Origin', header); + + res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); + res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With'); + res.set('Access-Control-Allow-Credentials', 'true'); + res.send(200); + }); app.post('/login', async (req, res) => { const { username, password, change_password } = req.body; const timestamp = Date.now(); @@ -393,6 +406,7 @@ async function start() { secure: true, signed: true, httpOnly: true, + sameSite: 'none', }); if (change_password) { @@ -432,6 +446,7 @@ async function start() { secure: true, signed: true, httpOnly: true, + sameSite: 'none', }); res.send({ @@ -442,6 +457,13 @@ async function start() { app.get('/login', async (req, res) => { + res.setHeader('Vary', 'Origin,Referer'); + res.set('Access-Control-Allow-Credentials', 'true'); + + const header = scrypted.getAccessControlAllowOrigin(req.headers); + if (header) + res.setHeader('Access-Control-Allow-Origin', header); + if (req.protocol === 'https' && req.headers.authorization) { const username = await new Promise(resolve => { const basicChecker = basicAuth.check((req) => { diff --git a/server/src/services/cors.ts b/server/src/services/cors.ts new file mode 100644 index 000000000..792fea5a2 --- /dev/null +++ b/server/src/services/cors.ts @@ -0,0 +1,19 @@ +import { ScryptedRuntime } from "../runtime"; + +export interface CORSServer { + tag: string; + server: string; +} + +export class CORSControl { + constructor(public runtime: ScryptedRuntime) { + } + + async getCORS(): Promise { + return this.runtime.cors; + } + + async setCORS(servers: CORSServer[]) { + this.runtime.cors = servers; + } +}