diff --git a/client/pages/config/index.vue b/client/pages/config/index.vue index 7083c789..3d030bb3 100644 --- a/client/pages/config/index.vue +++ b/client/pages/config/index.vue @@ -131,35 +131,26 @@
- +

{{ $strings.LabelExample }}: {{ dateExample }}

- +

{{ $strings.LabelExample }}: {{ timeExample }}

- +
- - +
+ +
@@ -323,6 +314,27 @@ export default { updateServerLanguage(val) { this.updateSettingsKey('language', val) }, + updateCorsOrigins(val) { + const validOrigins = [] + const invalidOrigins = [] + + val.forEach((origin) => { + const trimmedOrigin = origin.trim().toLowerCase() + try { + new URL(trimmedOrigin) + validOrigins.push(trimmedOrigin) + } catch { + invalidOrigins.push(trimmedOrigin) + } + }) + + if (invalidOrigins.length > 0) { + this.$toast.error(this.$strings.ToastInvalidUrls) + } + + this.newServerSettings.allowedOrigins = validOrigins + this.updateSettingsKey('allowedOrigins', validOrigins) + }, updateSettingsKey(key, val) { if (key === 'scannerDisableWatcher') { this.newServerSettings.scannerDisableWatcher = val @@ -352,6 +364,7 @@ export default { initServerSettings() { this.newServerSettings = this.serverSettings ? { ...this.serverSettings } : {} this.newServerSettings.sortingPrefixes = [...(this.newServerSettings.sortingPrefixes || [])] + this.newServerSettings.allowedOrigins = [...(this.newServerSettings.allowedOrigins || [])] this.scannerEnableWatcher = !this.newServerSettings.scannerDisableWatcher this.homepageUseBookshelfView = this.newServerSettings.homeBookshelfView != this.$constants.BookshelfView.DETAIL diff --git a/client/strings/en-us.json b/client/strings/en-us.json index f31a8a1d..70ac874d 100644 --- a/client/strings/en-us.json +++ b/client/strings/en-us.json @@ -199,6 +199,7 @@ "HeaderSettingsExperimental": "Experimental Features", "HeaderSettingsGeneral": "General", "HeaderSettingsScanner": "Scanner", + "HeaderSettingsSecurity": "Security", "HeaderSettingsWebClient": "Web Client", "HeaderSleepTimer": "Sleep Timer", "HeaderStatsLargestItems": "Largest Items", @@ -293,6 +294,7 @@ "LabelContinueListening": "Continue Listening", "LabelContinueReading": "Continue Reading", "LabelContinueSeries": "Continue Series", + "LabelCorsAllowed": "Allowed CORS Origins", "LabelCover": "Cover", "LabelCoverImageURL": "Cover Image URL", "LabelCoverProvider": "Cover Provider", @@ -1034,6 +1036,7 @@ "ToastInvalidImageUrl": "Invalid image URL", "ToastInvalidMaxEpisodesToDownload": "Invalid max episodes to download", "ToastInvalidUrl": "Invalid URL", + "ToastInvalidUrls": "One or more URLs are invalid", "ToastItemCoverUpdateSuccess": "Item cover updated", "ToastItemDeletedFailed": "Failed to delete item", "ToastItemDeletedSuccess": "Deleted item", diff --git a/server/Server.js b/server/Server.js index 1a8db406..a384b93c 100644 --- a/server/Server.js +++ b/server/Server.js @@ -240,8 +240,8 @@ class Server { * Running in development allows cors to allow testing the mobile apps in the browser * or env variable ALLOW_CORS = '1' */ - if (global.AllowCors || Logger.isDev || req.path.match(/\/api\/items\/([a-z0-9-]{36})\/(ebook|cover)(\/[0-9]+)?/)) { - const allowedOrigins = ['capacitor://localhost', 'http://localhost'] + if (global.AllowCors || Logger.isDev || req.path.match(/\/api\/items\/([a-z0-9-]{36})\/(ebook|cover)(\/[0-9]+)?/) || global.ServerSettings.allowedOrigins?.length) { + const allowedOrigins = ['capacitor://localhost', 'http://localhost', ...(global.ServerSettings.allowedOrigins ? global.ServerSettings.allowedOrigins : [])] if (global.AllowCors || Logger.isDev || allowedOrigins.some((o) => o === req.get('origin'))) { res.header('Access-Control-Allow-Origin', req.get('origin')) res.header('Access-Control-Allow-Methods', 'GET, POST, PATCH, PUT, DELETE, OPTIONS') diff --git a/server/objects/settings/ServerSettings.js b/server/objects/settings/ServerSettings.js index 4f0aa97b..a03e17c7 100644 --- a/server/objects/settings/ServerSettings.js +++ b/server/objects/settings/ServerSettings.js @@ -53,6 +53,7 @@ class ServerSettings { this.dateFormat = 'MM/dd/yyyy' this.timeFormat = 'HH:mm' this.language = 'en-us' + this.allowedOrigins = [] this.logLevel = Logger.logLevel @@ -120,6 +121,7 @@ class ServerSettings { this.dateFormat = settings.dateFormat || 'MM/dd/yyyy' this.timeFormat = settings.timeFormat || 'HH:mm' this.language = settings.language || 'en-us' + this.allowedOrigins = settings.allowedOrigins || [] this.logLevel = settings.logLevel || Logger.logLevel this.version = settings.version || null this.buildNumber = settings.buildNumber || 0 // Added v2.4.5 @@ -231,6 +233,7 @@ class ServerSettings { dateFormat: this.dateFormat, timeFormat: this.timeFormat, language: this.language, + allowedOrigins: this.allowedOrigins, logLevel: this.logLevel, version: this.version, buildNumber: this.buildNumber,