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 @@
-
updateSettingsKey('dateFormat', val)" />
+ updateSettingsKey('dateFormat', val)" />
{{ $strings.LabelExample }}: {{ dateExample }}
-
updateSettingsKey('timeFormat', val)" />
+ updateSettingsKey('timeFormat', val)" />
{{ $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,