Fixed CORS issue

Cette révision appartient à :
arabcoders
2025-11-01 20:40:12 +03:00
Parent 4f2617ae6e
révision 04e7687b4d
3 fichiers modifiés avec 78 ajouts et 5 suppressions
+6
Voir le fichier
@@ -1,5 +1,11 @@
# CHANGELOG # CHANGELOG
## 1.0.3 - 2025-11-01
- **Added**: Runtime optional host permission flow so users can authorize their own YTPTube servers without manual manifest edits.
- **Improved**: Options page now normalizes instance URLs, records granted origins, and cleans up old host permissions when the server changes.
- **Fixed**: Background requests to user-managed servers no longer hit CORS/address-space blocks after permissions are granted.
## 1.0.2 - 2025-08-12 ## 1.0.2 - 2025-08-12
- **Fixed**: Selected preset was not being sent to YTPTube API - now properly includes preset in requests - **Fixed**: Selected preset was not being sent to YTPTube API - now properly includes preset in requests
+5 -1
Voir le fichier
@@ -7,7 +7,7 @@
] ]
}, },
"name": "YTPTube Extension", "name": "YTPTube Extension",
"version": "1.0.2", "version": "1.0.3",
"description": "Add URLs to YTPTube instance", "description": "Add URLs to YTPTube instance",
"permissions": [ "permissions": [
"activeTab", "activeTab",
@@ -15,6 +15,10 @@
"storage", "storage",
"notifications" "notifications"
], ],
"optional_host_permissions": [
"http://*/*",
"https://*/*"
],
"homepage_url": "https://github.com/arabcoders/ytptube-extension", "homepage_url": "https://github.com/arabcoders/ytptube-extension",
"icons": { "icons": {
"16": "icons/icon-16.png", "16": "icons/icon-16.png",
+67 -4
Voir le fichier
@@ -1,6 +1,7 @@
// noinspection JSUnresolvedReference // noinspection JSUnresolvedReference
const str_keys = ["instance_url", "preset", "template", "folder", "username", "password"] const str_keys = ["instance_url", "preset", "template", "folder", "username", "password"]
let storedOriginPattern = null;
if (typeof chrome === 'undefined') { if (typeof chrome === 'undefined') {
let chrome = browser let chrome = browser
@@ -19,11 +20,46 @@ const notify = (message, no_inline) => {
}); });
} }
const buildOriginPattern = (instanceUrl) => {
try {
const url = new URL(instanceUrl);
return `${url.protocol}//${url.host}/*`;
} catch (error) {
return null;
}
};
const ensureOriginPermission = async (originPattern) => {
if (!originPattern) {
return false;
}
const hasPermission = await chrome.permissions.contains({origins: [originPattern]});
if (hasPermission) {
return true;
}
return await chrome.permissions.request({origins: [originPattern]});
};
const removeOriginPermission = async (originPattern) => {
if (!originPattern) {
return;
}
const hasPermission = await chrome.permissions.contains({origins: [originPattern]});
if (!hasPermission) {
return;
}
await chrome.permissions.remove({origins: [originPattern]});
};
const testConfig = async () => { const testConfig = async () => {
document.querySelector('#error_msg').innerText = ""; document.querySelector('#error_msg').innerText = "";
let instance_url = document.querySelector("#instance_url").value; let instance_url = document.querySelector("#instance_url").value.trim();
if (!instance_url) { if (!instance_url) {
notify("Please enter a valid YTPTube instance URL."); notify("Please enter a valid YTPTube instance URL.");
return false; return false;
@@ -33,6 +69,20 @@ const testConfig = async () => {
instance_url = instance_url.slice(0, -1); instance_url = instance_url.slice(0, -1);
} }
document.querySelector("#instance_url").value = instance_url;
const originPattern = buildOriginPattern(instance_url);
if (!originPattern) {
notify("Please enter a valid YTPTube instance URL.");
return false;
}
const granted = await ensureOriginPermission(originPattern);
if (!granted) {
notify("Permission to access the YTPTube instance was denied.");
return false;
}
let username = document.querySelector("#username").value; let username = document.querySelector("#username").value;
let password = document.querySelector("#password").value; let password = document.querySelector("#password").value;
@@ -69,7 +119,10 @@ document.addEventListener("DOMContentLoaded", () => {
chrome.storage.sync.get(k).then(r => document.querySelector(`#${k}`).value = r[k] || "", onError); chrome.storage.sync.get(k).then(r => document.querySelector(`#${k}`).value = r[k] || "", onError);
}); });
chrome.storage.sync.get("showContextMenu").then(r => document.querySelector("#showContextMenu").checked = r.showContextMenu || false); chrome.storage.sync.get(["showContextMenu", "instance_origin"]).then(r => {
document.querySelector("#showContextMenu").checked = r.showContextMenu || false;
storedOriginPattern = r.instance_origin || null;
}, onError);
}); });
document.getElementById("ytptube_options").addEventListener("submit", async e => { document.getElementById("ytptube_options").addEventListener("submit", async e => {
@@ -84,7 +137,16 @@ document.getElementById("ytptube_options").addEventListener("submit", async e =>
let data = {presets: {presets: ['default'], last_updated: 0}} let data = {presets: {presets: ['default'], last_updated: 0}}
str_keys.forEach(key => data[key] = document.querySelector(`#${key}`).value) str_keys.forEach(key => data[key] = document.querySelector(`#${key}`).value)
data["showContextMenu"] = showContextMenu data["showContextMenu"] = showContextMenu;
const newOriginPattern = buildOriginPattern(data.instance_url);
if (storedOriginPattern && storedOriginPattern !== newOriginPattern) {
await removeOriginPermission(storedOriginPattern);
}
data.instance_origin = newOriginPattern;
storedOriginPattern = newOriginPattern;
chrome.storage.sync.set(data); chrome.storage.sync.set(data);
chrome.contextMenus.update("send-to-ytptube", {visible: showContextMenu}); chrome.contextMenus.update("send-to-ytptube", {visible: showContextMenu});
@@ -101,4 +163,5 @@ document.getElementById("ytptube_options").addEventListener("submit", async e =>
document.getElementById("test_config").addEventListener("click", async e => { document.getElementById("test_config").addEventListener("click", async e => {
e.preventDefault(); e.preventDefault();
await testConfig(); await testConfig();
}); });