mirror of
https://github.com/plexguide/Huntarr.io.git
synced 2025-12-30 10:40:01 -06:00
Fix subpath support
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
// Ensure settings are properly loaded on component mount
|
||||
onMount(async () => {
|
||||
try {
|
||||
const response = await fetch('/api/settings');
|
||||
const response = await HuntarrUtils.fetchWithTimeout('/api/settings');
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
settings.set(ensureNumericValues(data));
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
async function loadSettings() {
|
||||
try {
|
||||
const response = await fetch('/api/settings');
|
||||
const response = await HuntarrUtils.fetchWithTimeout('/api/settings');
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
// Ensure all numeric values are properly handled
|
||||
@@ -73,7 +73,7 @@
|
||||
try {
|
||||
const settingsValue = $settings;
|
||||
|
||||
const response = await fetch('/api/settings', {
|
||||
const response = await HuntarrUtils.fetchWithTimeout('/api/settings', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
|
||||
@@ -223,7 +223,7 @@ const appsModule = {
|
||||
appPanel.innerHTML = '<div class="loading-panel"><i class="fas fa-spinner fa-spin"></i> Loading settings...</div>';
|
||||
|
||||
// Fetch settings for this app
|
||||
fetch(`/api/settings/${app}`)
|
||||
HuntarrUtils.fetchWithTimeout(`/api/settings/${app}`)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
@@ -588,7 +588,7 @@ const appsModule = {
|
||||
|
||||
// Send settings to the server
|
||||
console.log(`Sending ${appType} settings to server...`);
|
||||
fetch(`/api/settings/${appType}`, {
|
||||
HuntarrUtils.fetchWithTimeout(`/api/settings/${appType}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
|
||||
@@ -164,7 +164,7 @@ function getErosVersion() {
|
||||
*/
|
||||
function refreshErosStatusAndVersion() {
|
||||
// Try to get current connection status from the server
|
||||
fetch('/api/eros/status')
|
||||
HuntarrUtils.fetchWithTimeout('/api/eros/status')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const panel = document.getElementById('erosSettings');
|
||||
|
||||
@@ -27,7 +27,7 @@ function initCommunityResourcesVisibility() {
|
||||
}
|
||||
|
||||
// Fetch general settings to determine visibility
|
||||
fetch('/api/settings/general')
|
||||
HuntarrUtils.fetchWithTimeout('/api/settings/general')
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
|
||||
@@ -53,7 +53,7 @@ window.lastStatefulHoursValue = null;
|
||||
window.justCompletedStatefulReset = true;
|
||||
|
||||
// Make direct API call
|
||||
fetch('/api/stateful/reset', {
|
||||
HuntarrUtils.fetchWithTimeout('/api/stateful/reset', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
|
||||
@@ -177,7 +177,7 @@ const historyModule = {
|
||||
url += `&search=${encodeURIComponent(this.searchQuery)}`;
|
||||
}
|
||||
|
||||
fetch(url)
|
||||
HuntarrUtils.fetchWithTimeout(url)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
@@ -201,7 +201,7 @@ const historyModule = {
|
||||
clearHistory: function() {
|
||||
this.setLoading(true);
|
||||
|
||||
fetch(`/api/history/${this.currentApp}`, {
|
||||
HuntarrUtils.fetchWithTimeout(`/api/history/${this.currentApp}`, {
|
||||
method: 'DELETE',
|
||||
})
|
||||
.then(response => {
|
||||
|
||||
@@ -15,7 +15,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
* Load hourly API cap data from the server
|
||||
*/
|
||||
function loadHourlyCapData() {
|
||||
fetch('/api/hourly-caps')
|
||||
HuntarrUtils.fetchWithTimeout('/api/hourly-caps')
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
|
||||
@@ -30,7 +30,7 @@ let huntarrUI = {
|
||||
suppressUnsavedChangesCheck: false, // Flag to suppress unsaved changes dialog
|
||||
|
||||
// Logo URL
|
||||
logoUrl: '/static/logo/256.png',
|
||||
logoUrl: './static/logo/256.png',
|
||||
|
||||
// Element references
|
||||
elements: {},
|
||||
@@ -869,7 +869,7 @@ let huntarrUI = {
|
||||
|
||||
try {
|
||||
// Append the app type to the URL
|
||||
const eventSource = new EventSource(`/logs?app=${appType}`);
|
||||
const eventSource = new EventSource(`./logs?app=${appType}`);
|
||||
|
||||
eventSource.onopen = () => {
|
||||
this.elements.logConnectionStatus.textContent = 'Connected';
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('/api/user/change-username', {
|
||||
HuntarrUtils.fetchWithTimeout('/api/user/change-username', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
@@ -133,7 +133,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('/api/user/change-password', {
|
||||
HuntarrUtils.fetchWithTimeout('/api/user/change-password', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
@@ -161,7 +161,7 @@
|
||||
|
||||
// 2FA setup handler
|
||||
function handleEnableTwoFactor() {
|
||||
fetch('/api/user/2fa/setup', { method: 'POST' })
|
||||
HuntarrUtils.fetchWithTimeout('/api/user/2fa/setup', { method: 'POST' })
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
@@ -200,7 +200,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('/api/user/2fa/verify', {
|
||||
HuntarrUtils.fetchWithTimeout('/api/user/2fa/verify', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ code: code })
|
||||
@@ -240,7 +240,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('/api/user/2fa/disable', {
|
||||
HuntarrUtils.fetchWithTimeout('/api/user/2fa/disable', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
@@ -293,7 +293,7 @@
|
||||
|
||||
// Function to fetch user information
|
||||
function fetchUserInfo() {
|
||||
fetch('/api/user/info')
|
||||
HuntarrUtils.fetchWithTimeout('/api/user/info')
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
this.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Resetting...';
|
||||
|
||||
// Make API call
|
||||
fetch('/api/stateful/reset', {
|
||||
HuntarrUtils.fetchWithTimeout('/api/stateful/reset', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
|
||||
@@ -161,7 +161,7 @@ async function fetchAppInstances() {
|
||||
const listUrl = `/api/scheduling/list?nocache=${cacheBuster}`;
|
||||
|
||||
console.debug(`Loading app instances from ${listUrl}`);
|
||||
const response = await fetch(listUrl);
|
||||
const response = await HuntarrUtils.fetchWithTimeout(listUrl);
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
@@ -320,7 +320,7 @@ function loadSchedules() {
|
||||
console.debug('Loading schedules from server'); // DEBUG level per user preference
|
||||
|
||||
// Make API call to get schedules
|
||||
fetch('/api/scheduler/load')
|
||||
HuntarrUtils.fetchWithTimeout('/api/scheduler/load')
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to load schedules');
|
||||
@@ -463,7 +463,7 @@ function saveSchedules() {
|
||||
console.debug('Saving processed schedules:', schedulesCopy);
|
||||
|
||||
// Make API call to save schedules
|
||||
fetch('/api/scheduler/save', {
|
||||
HuntarrUtils.fetchWithTimeout('/api/scheduler/save', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
|
||||
@@ -908,7 +908,7 @@ const SettingsForms = {
|
||||
const resetStrikesBtn = container.querySelector('#reset_swaparr_strikes');
|
||||
const statusContainer = container.querySelector('#swaparr_status');
|
||||
|
||||
fetch('/api/swaparr/status')
|
||||
HuntarrUtils.fetchWithTimeout('/api/swaparr/status')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
let statusHTML = '';
|
||||
@@ -944,7 +944,7 @@ const SettingsForms = {
|
||||
if (confirm('Are you sure you want to reset all Swaparr strikes? This will clear the strike history for all apps.')) {
|
||||
statusContainer.innerHTML = '<p>Resetting strikes...</p>';
|
||||
|
||||
fetch('/api/swaparr/reset', {
|
||||
HuntarrUtils.fetchWithTimeout('/api/swaparr/reset', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
@@ -957,7 +957,7 @@ const SettingsForms = {
|
||||
statusContainer.innerHTML = `<p>Success: ${data.message}</p>`;
|
||||
// Reload status after a short delay
|
||||
setTimeout(() => {
|
||||
fetch('/api/swaparr/status')
|
||||
HuntarrUtils.fetchWithTimeout('/api/swaparr/status')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
let statusHTML = '';
|
||||
@@ -1473,7 +1473,7 @@ const SettingsForms = {
|
||||
}
|
||||
}
|
||||
|
||||
fetch('/api/stateful/info', {
|
||||
HuntarrUtils.fetchWithTimeout('/api/stateful/info', {
|
||||
cache: 'no-cache',
|
||||
headers: {
|
||||
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
||||
@@ -1550,7 +1550,7 @@ const SettingsForms = {
|
||||
|
||||
// Helper function to fetch data silently without updating UI
|
||||
function fetchStatefulInfoSilently() {
|
||||
fetch('/api/stateful/info', {
|
||||
HuntarrUtils.fetchWithTimeout('/api/stateful/info', {
|
||||
cache: 'no-cache',
|
||||
headers: {
|
||||
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
||||
@@ -1665,7 +1665,7 @@ const SettingsForms = {
|
||||
button.disabled = true;
|
||||
|
||||
// Make the API request
|
||||
fetch(`/api/${appType}/test-connection`, {
|
||||
HuntarrUtils.fetchWithTimeout(`/api/${appType}/test-connection`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
@@ -1950,7 +1950,7 @@ const SettingsForms = {
|
||||
}
|
||||
|
||||
// Make the API request
|
||||
fetch(`/api/${app}/test-connection`, {
|
||||
HuntarrUtils.fetchWithTimeout(`/api/${app}/test-connection`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
|
||||
@@ -61,7 +61,7 @@ function resetStatsAPI(appType = null) {
|
||||
const requestBody = appType ? { app_type: appType } : {};
|
||||
|
||||
// Use the public endpoint that doesn't require authentication
|
||||
return fetch('/api/stats/reset_public', {
|
||||
return HuntarrUtils.fetchWithTimeout('/api/stats/reset_public', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
(function() {
|
||||
// Store logo URL consistently across the app - use local path instead of GitHub
|
||||
const LOGO_URL = '/static/logo/256.png';
|
||||
const LOGO_URL = './static/logo/256.png';
|
||||
|
||||
// Create and preload image with local path
|
||||
const preloadImg = new Image();
|
||||
@@ -56,8 +56,8 @@
|
||||
img.onerror = function() {
|
||||
// Fallback if local path fails
|
||||
console.warn('Logo failed to load, trying alternate source');
|
||||
if (this.src !== '/logo/256.png') {
|
||||
this.src = '/logo/256.png';
|
||||
if (this.src !== './logo/256.png') {
|
||||
this.src = './logo/256.png';
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
// Function to load user information
|
||||
function loadUserInfo() {
|
||||
fetch('/api/user/info')
|
||||
HuntarrUtils.fetchWithTimeout('/api/user/info')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.username) {
|
||||
@@ -27,7 +27,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
// Function to check 2FA status
|
||||
function check2FAStatus() {
|
||||
fetch('/api/user/2fa-status')
|
||||
HuntarrUtils.fetchWithTimeout('/api/user/2fa-status')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const enable2FACheckbox = document.getElementById('enable2FA');
|
||||
|
||||
@@ -4,24 +4,28 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>My App</title>
|
||||
<link rel="stylesheet" href="/static/css/styles.css">
|
||||
<link rel="stylesheet" href="./static/css/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<!-- Pass base URL configuration to JavaScript -->
|
||||
<script>window.HUNTARR_BASE_URL = '{{ base_url|default("", true) }}';</script>
|
||||
<!-- Pass global scripts -->
|
||||
{% include 'components/scripts.html' %}
|
||||
<!-- ...existing code... -->
|
||||
<!-- Scripts -->
|
||||
<!-- <script src="/static/js/settings_sync_utility.js"></script> Removed -->
|
||||
<!-- <script src="/static/js/core.js"></script> Removed -->
|
||||
<!-- <script src="/static/js/settings_loader.js"></script> Removed -->
|
||||
<script src="/static/js/settings_forms.js"></script> <!-- Keep for now -->
|
||||
<!-- <script src="/static/js/settings_initializer.js"></script> Removed -->
|
||||
<!-- <script src="/static/js/settings_sync.js"></script> Removed -->
|
||||
<script src="/static/js/new-main.js"></script> <!-- Consolidated main script -->
|
||||
<!-- <script src="./static/js/settings_sync_utility.js"></script> Removed -->
|
||||
<!-- <script src="./static/js/core.js"></script> Removed -->
|
||||
<!-- <script src="./static/js/settings_loader.js"></script> Removed -->
|
||||
<script src="./static/js/settings_forms.js"></script> <!-- Keep for now -->
|
||||
<!-- <script src="./static/js/settings_initializer.js"></script> Removed -->
|
||||
<!-- <script src="./static/js/settings_sync.js"></script> Removed -->
|
||||
<script src="./static/js/new-main.js"></script> <!-- Consolidated main script -->
|
||||
<!-- App-specific scripts -->
|
||||
<script src="/static/js/apps/sonarr.js"></script>
|
||||
<script src="/static/js/apps/radarr.js"></script>
|
||||
<script src="/static/js/apps/lidarr.js"></script>
|
||||
<script src="/static/js/apps/readarr.js"></script>
|
||||
<script src="/static/js/apps/swaparr.js"></script>
|
||||
<script src="./static/js/apps/sonarr.js"></script>
|
||||
<script src="./static/js/apps/radarr.js"></script>
|
||||
<script src="./static/js/apps/lidarr.js"></script>
|
||||
<script src="./static/js/apps/readarr.js"></script>
|
||||
<script src="./static/js/apps/swaparr.js"></script>
|
||||
<!-- ...existing code... -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -12,7 +12,7 @@
|
||||
<div class="cleanuperr-info-box">
|
||||
<div class="cleanuperr-header">
|
||||
<div class="cleanuperr-logo">
|
||||
<img src="/static/logo/apps/cleanuperr.png" alt="Cleanuperr Logo" class="app-logo">
|
||||
<img src="./static/logo/apps/cleanuperr.png" alt="Cleanuperr Logo" class="app-logo">
|
||||
</div>
|
||||
<div class="cleanuperr-title">
|
||||
<h3>Cleanuperr by <a href="https://github.com/Flaminel" target="_blank">Flaminel</a></h3>
|
||||
@@ -392,7 +392,7 @@
|
||||
|
||||
// Load GitHub star count for Cleanuperr
|
||||
function loadCleanuperrStarCount() {
|
||||
fetch('https://api.github.com/repos/flmorg/cleanuperr')
|
||||
HuntarrUtils.fetchWithTimeout('https://api.github.com/repos/flmorg/cleanuperr')
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
// Handle rate limiting or other errors
|
||||
|
||||
@@ -1,21 +1,16 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<!-- Preload logo to prevent flashing -->
|
||||
<link rel="preload" href="/static/logo/256.png" as="image" fetchpriority="high">
|
||||
<link rel="preload" href="./static/logo/256.png" as="image" fetchpriority="high">
|
||||
<!-- Preload theme script to prevent flashing -->
|
||||
<script src="/static/js/theme-preload.js"></script>
|
||||
<!-- Pass base URL configuration to JavaScript -->
|
||||
<script>
|
||||
// Make base URL available to frontend JavaScript
|
||||
window.HUNTARR_BASE_URL = '{{ base_url|default("", true) }}';
|
||||
</script>
|
||||
<link rel="stylesheet" href="/static/css/new-style.css">
|
||||
<script src="./static/js/theme-preload.js"></script>
|
||||
<link rel="stylesheet" href="./static/css/new-style.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<link rel="icon" href="/static/logo/16.png">
|
||||
<link rel="stylesheet" href="/static/css/apps-double-scroll-fix.css">
|
||||
<link rel="stylesheet" href="/static/css/responsive-fix.css">
|
||||
<link rel="stylesheet" href="/static/css/hide-deny-icon.css">
|
||||
<link rel="stylesheet" href="/static/css/low-usage-mode.css">
|
||||
<link rel="icon" href="./static/logo/16.png">
|
||||
<link rel="stylesheet" href="./static/css/apps-double-scroll-fix.css">
|
||||
<link rel="stylesheet" href="./static/css/responsive-fix.css">
|
||||
<link rel="stylesheet" href="./static/css/hide-deny-icon.css">
|
||||
<link rel="stylesheet" href="./static/css/low-usage-mode.css">
|
||||
<!-- Better logo visibility handling -->
|
||||
<style>
|
||||
.logo, .login-logo {
|
||||
@@ -60,20 +55,20 @@
|
||||
</style>
|
||||
|
||||
<!-- Favicons -->
|
||||
<link rel="icon" href="/static/logo/huntarr.ico" type="image/x-icon">
|
||||
<link rel="shortcut icon" href="/static/logo/huntarr.ico" type="image/x-icon">
|
||||
<link rel="icon" href="/static/logo/16.png" type="image/png" sizes="16x16">
|
||||
<link rel="icon" href="/static/logo/32.png" type="image/png" sizes="32x32">
|
||||
<link rel="icon" href="/static/logo/48.png" type="image/png" sizes="48x48">
|
||||
<link rel="icon" href="/static/logo/64.png" type="image/png" sizes="64x64">
|
||||
<link rel="icon" href="/static/logo/128.png" type="image/png" sizes="128x128">
|
||||
<link rel="apple-touch-icon" href="/static/logo/128.png">
|
||||
<meta name="msapplication-TileImage" content="/static/logo/128.png">
|
||||
<link rel="icon" href="./static/logo/huntarr.ico" type="image/x-icon">
|
||||
<link rel="shortcut icon" href="./static/logo/huntarr.ico" type="image/x-icon">
|
||||
<link rel="icon" href="./static/logo/16.png" type="image/png" sizes="16x16">
|
||||
<link rel="icon" href="./static/logo/32.png" type="image/png" sizes="32x32">
|
||||
<link rel="icon" href="./static/logo/48.png" type="image/png" sizes="48x48">
|
||||
<link rel="icon" href="./static/logo/64.png" type="image/png" sizes="64x64">
|
||||
<link rel="icon" href="./static/logo/128.png" type="image/png" sizes="128x128">
|
||||
<link rel="apple-touch-icon" href="./static/logo/128.png">
|
||||
<meta name="msapplication-TileImage" content="./static/logo/128.png">
|
||||
<meta name="msapplication-TileColor" content="#3498db">
|
||||
|
||||
<!-- CSS -->
|
||||
<link rel="stylesheet" href="/static/css/variables.css">
|
||||
<link rel="stylesheet" href="/static/css/styles.css">
|
||||
<link rel="stylesheet" href="./static/css/variables.css">
|
||||
<link rel="stylesheet" href="./static/css/styles.css">
|
||||
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css"
|
||||
|
||||
@@ -335,7 +335,7 @@
|
||||
</div>
|
||||
<div class="app-content">
|
||||
<div class="app-icon-wrapper">
|
||||
<img src="/static/images/app-icons/sonarr.png" alt="Sonarr Logo" class="app-logo">
|
||||
<img src="./static/images/app-icons/sonarr.png" alt="Sonarr Logo" class="app-logo">
|
||||
</div>
|
||||
<h4>Sonarr</h4>
|
||||
</div>
|
||||
@@ -367,7 +367,7 @@
|
||||
</div>
|
||||
<div class="app-content">
|
||||
<div class="app-icon-wrapper">
|
||||
<img src="/static/images/app-icons/radarr.png" alt="Radarr Logo" class="app-logo">
|
||||
<img src="./static/images/app-icons/radarr.png" alt="Radarr Logo" class="app-logo">
|
||||
</div>
|
||||
<h4>Radarr</h4>
|
||||
</div>
|
||||
@@ -399,7 +399,7 @@
|
||||
</div>
|
||||
<div class="app-content">
|
||||
<div class="app-icon-wrapper">
|
||||
<img src="/static/images/app-icons/lidarr.png" alt="Lidarr Logo" class="app-logo">
|
||||
<img src="./static/images/app-icons/lidarr.png" alt="Lidarr Logo" class="app-logo">
|
||||
</div>
|
||||
<h4>Lidarr</h4>
|
||||
</div>
|
||||
@@ -431,7 +431,7 @@
|
||||
</div>
|
||||
<div class="app-content">
|
||||
<div class="app-icon-wrapper">
|
||||
<img src="/static/images/app-icons/readarr.png" alt="Readarr Logo" class="app-logo">
|
||||
<img src="./static/images/app-icons/readarr.png" alt="Readarr Logo" class="app-logo">
|
||||
</div>
|
||||
<h4>Readarr</h4>
|
||||
</div>
|
||||
@@ -463,7 +463,7 @@
|
||||
</div>
|
||||
<div class="app-content">
|
||||
<div class="app-icon-wrapper">
|
||||
<img src="/static/images/app-icons/whisparr.png" alt="Whisparr Logo" class="app-logo">
|
||||
<img src="./static/images/app-icons/whisparr.png" alt="Whisparr Logo" class="app-logo">
|
||||
</div>
|
||||
<h4>Whisparr</h4>
|
||||
</div>
|
||||
@@ -495,7 +495,7 @@
|
||||
</div>
|
||||
<div class="app-content">
|
||||
<div class="app-icon-wrapper">
|
||||
<img src="/static/images/app-icons/whisparr.png" alt="Whisparr V3 Logo" class="app-logo">
|
||||
<img src="./static/images/app-icons/whisparr.png" alt="Whisparr V3 Logo" class="app-logo">
|
||||
</div>
|
||||
<h4>Whisparr V3</h4>
|
||||
</div>
|
||||
@@ -1772,7 +1772,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
// Fetch sponsors from the API
|
||||
async function fetchSponsors() {
|
||||
try {
|
||||
const response = await fetch('/api/github_sponsors');
|
||||
const response = await HuntarrUtils.fetchWithTimeout('/api/github_sponsors');
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<!-- Existing scripts -->
|
||||
<script src="/static/js/utils.js"></script>
|
||||
<script src="/static/js/new-main.js"></script>
|
||||
<script src="/static/js/apps-scroll-fix.js"></script>
|
||||
<script src="/static/js/stats-tooltips.js"></script>
|
||||
<script src="/static/js/card-hover-effects.js"></script>
|
||||
<script src="/static/js/circular-progress.js"></script>
|
||||
<script src="/static/js/background-pattern.js"></script>
|
||||
<script src="./static/js/utils.js"></script>
|
||||
<script src="./static/js/new-main.js"></script>
|
||||
<script src="./static/js/apps-scroll-fix.js"></script>
|
||||
<script src="./static/js/stats-tooltips.js"></script>
|
||||
<script src="./static/js/card-hover-effects.js"></script>
|
||||
<script src="./static/js/circular-progress.js"></script>
|
||||
<script src="./static/js/background-pattern.js"></script>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<div class="sidebar" id="sidebar">
|
||||
<div class="sidebar-top-line"></div>
|
||||
<div class="logo-container">
|
||||
<img src="/static/logo/256.png" alt="Huntarr Logo" class="logo logo-large">
|
||||
<img src="/static/logo/32.png" alt="Huntarr Logo" class="logo logo-small">
|
||||
<img src="./static/logo/256.png" alt="Huntarr Logo" class="logo logo-large">
|
||||
<img src="./static/logo/32.png" alt="Huntarr Logo" class="logo logo-small">
|
||||
<h1>Huntarr</h1>
|
||||
</div>
|
||||
|
||||
@@ -17,19 +17,19 @@
|
||||
<!-- Core Navigation Group -->
|
||||
<div class="nav-group" style="margin-top: -10px;">
|
||||
<div class="nav-group-title">Core</div>
|
||||
<a href="/" class="nav-item" id="homeNav">
|
||||
<a href="./" class="nav-item" id="homeNav">
|
||||
<div class="nav-icon-wrapper">
|
||||
<i class="fas fa-home"></i>
|
||||
</div>
|
||||
<span>Home</span>
|
||||
</a>
|
||||
<a href="/#apps" class="nav-item" id="appsNav">
|
||||
<a href="#apps" class="nav-item" id="appsNav">
|
||||
<div class="nav-icon-wrapper">
|
||||
<i class="fas fa-tools"></i>
|
||||
</div>
|
||||
<span>Apps</span>
|
||||
</a>
|
||||
<a href="/#settings" class="nav-item" id="settingsNav">
|
||||
<a href="#settings" class="nav-item" id="settingsNav">
|
||||
<div class="nav-icon-wrapper">
|
||||
<i class="fas fa-cog"></i>
|
||||
</div>
|
||||
@@ -46,19 +46,19 @@
|
||||
</div>
|
||||
<span>Scheduling</span>
|
||||
</a>
|
||||
<a href="/#logs" class="nav-item" id="logsNav">
|
||||
<a href="#logs" class="nav-item" id="logsNav">
|
||||
<div class="nav-icon-wrapper">
|
||||
<i class="fas fa-list-alt"></i>
|
||||
</div>
|
||||
<span>Logs</span>
|
||||
</a>
|
||||
<a href="/#history" class="nav-item" id="historyNav">
|
||||
<a href="#history" class="nav-item" id="historyNav">
|
||||
<div class="nav-icon-wrapper">
|
||||
<i class="fas fa-history"></i>
|
||||
</div>
|
||||
<span>History</span>
|
||||
</a>
|
||||
<a href="/user" class="nav-item" id="userNav">
|
||||
<a href="./user" class="nav-item" id="userNav">
|
||||
<div class="nav-icon-wrapper">
|
||||
<i class="fas fa-user"></i>
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
<head>
|
||||
{% include 'components/head.html' %}
|
||||
<title>Huntarr - Home</title>
|
||||
|
||||
<!-- Pass base URL configuration to JavaScript -->
|
||||
<script>window.HUNTARR_BASE_URL = '{{ base_url|default("", true) }}';</script>
|
||||
<!-- Pass global scripts -->
|
||||
{% include 'components/scripts.html' %}
|
||||
</head>
|
||||
<body>
|
||||
<div class="app-container mobile-optimized">
|
||||
@@ -37,25 +42,24 @@
|
||||
|
||||
{% include 'components/footer.html' %}
|
||||
|
||||
{% include 'components/scripts.html' %}
|
||||
<!-- Load settings-related scripts -->
|
||||
<script src="/static/js/settings_forms.js"></script>
|
||||
<script src="./static/js/settings_forms.js"></script>
|
||||
<!-- Load history script -->
|
||||
<script src="/static/js/history.js"></script>
|
||||
<script src="./static/js/history.js"></script>
|
||||
<!-- Load apps script -->
|
||||
<script src="/static/js/apps.js"></script>
|
||||
<script src="./static/js/apps.js"></script>
|
||||
<!-- Load scheduling script -->
|
||||
<script src="/static/js/scheduling.js"></script>
|
||||
<script src="./static/js/scheduling.js"></script>
|
||||
<!-- Emergency reset button implementation -->
|
||||
<script src="/static/js/direct-reset.js"></script>
|
||||
<script src="./static/js/direct-reset.js"></script>
|
||||
<!-- Stats reset handler -->
|
||||
<script src="/static/js/stats-reset.js"></script>
|
||||
<script src="./static/js/stats-reset.js"></script>
|
||||
<!-- Hourly API cap handler -->
|
||||
<script src="/static/js/hourly-cap.js"></script>
|
||||
<script src="./static/js/hourly-cap.js"></script>
|
||||
<!-- Scheduling handler -->
|
||||
<script src="/static/js/scheduling.js"></script>
|
||||
<script src="./static/js/scheduling.js"></script>
|
||||
<!-- Community Resources visibility handler -->
|
||||
<script src="/static/js/community-resources.js"></script>
|
||||
<script src="./static/js/community-resources.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -17,12 +17,12 @@
|
||||
})();
|
||||
</script>
|
||||
<!-- Preload logo -->
|
||||
<link rel="preload" href="/static/logo/256.png" as="image" fetchpriority="high">
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
<link rel="preload" href="./static/logo/256.png" as="image" fetchpriority="high">
|
||||
<link rel="stylesheet" href="./static/css/style.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<link rel="icon" href="/static/logo/16.png">
|
||||
<link rel="icon" href="./static/logo/16.png">
|
||||
<!-- Preload script to prevent flashing -->
|
||||
<script src="/static/js/theme-preload.js"></script>
|
||||
<script src="./static/js/theme-preload.js"></script>
|
||||
<!-- Modern login styles -->
|
||||
<style>
|
||||
.login-page {
|
||||
@@ -215,7 +215,7 @@
|
||||
<body class="login-page">
|
||||
<div class="login-container">
|
||||
<div class="login-header">
|
||||
<img src="/static/logo/256.png" alt="Huntarr Logo" class="login-logo" onload="this.classList.add('loaded')">
|
||||
<img src="./static/logo/256.png" alt="Huntarr Logo" class="login-logo" onload="this.classList.add('loaded')">
|
||||
<h1>Huntarr</h1>
|
||||
</div>
|
||||
<div class="login-form">
|
||||
@@ -252,6 +252,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pass base URL configuration to JavaScript -->
|
||||
<script>window.HUNTARR_BASE_URL = '{{ base_url|default("", true) }}';</script>
|
||||
<!-- Pass global scripts -->
|
||||
{% include 'components/scripts.html' %}
|
||||
<script>
|
||||
const loginForm = document.getElementById('loginForm');
|
||||
const usernameInput = document.getElementById('username');
|
||||
@@ -318,7 +322,7 @@
|
||||
}
|
||||
|
||||
// Submit the form data
|
||||
fetch('/login', {
|
||||
HuntarrUtils.fetchWithTimeout('/login', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
||||
@@ -48,11 +48,11 @@
|
||||
|
||||
{% include 'components/head.html' %}
|
||||
<title>Huntarr - Scheduling</title>
|
||||
<link rel="stylesheet" href="/static/css/scheduler-history.css">
|
||||
<link rel="stylesheet" href="/static/css/dropdown-overrides.css">
|
||||
<link rel="stylesheet" href="./static/css/scheduler-history.css">
|
||||
<link rel="stylesheet" href="./static/css/dropdown-overrides.css">
|
||||
<!-- Add scrolling fix stylesheet -->
|
||||
<link rel="stylesheet" href="/static/css/scheduling-fix.css">
|
||||
<link rel="stylesheet" href="/static/css/emergency-scroll-fix.css">
|
||||
<link rel="stylesheet" href="./static/css/scheduling-fix.css">
|
||||
<link rel="stylesheet" href="./static/css/emergency-scroll-fix.css">
|
||||
|
||||
<style>
|
||||
/* Critical scrolling fixes applied directly to the page */
|
||||
@@ -285,17 +285,20 @@
|
||||
|
||||
{% include 'components/footer.html' %}
|
||||
|
||||
<!-- Pass base URL configuration to JavaScript -->
|
||||
<script>window.HUNTARR_BASE_URL = '{{ base_url|default("", true) }}';</script>
|
||||
<!-- Pass global scripts -->
|
||||
{% include 'components/scripts.html' %}
|
||||
|
||||
<!-- Load scheduling script -->
|
||||
<script src="/static/js/scheduling.js"></script>
|
||||
<script src="./static/js/scheduling.js"></script>
|
||||
|
||||
<!-- Add scrolling fix script -->
|
||||
<script src="/static/js/scheduling-fix.js"></script>
|
||||
<script src="./static/js/scheduling-fix.js"></script>
|
||||
|
||||
<!-- Add emergency scrolling fixes -->
|
||||
<link rel="stylesheet" href="/static/css/emergency-scroll-fix.css">
|
||||
<script src="/static/js/scroll-fix-override.js"></script>
|
||||
<link rel="stylesheet" href="./static/css/emergency-scroll-fix.css">
|
||||
<script src="./static/js/scroll-fix-override.js"></script>
|
||||
|
||||
<style>
|
||||
/* Scheduler Container */
|
||||
@@ -596,6 +599,10 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Pass base URL configuration to JavaScript -->
|
||||
<script>window.HUNTARR_BASE_URL = '{{ base_url|default("", true) }}';</script>
|
||||
<!-- Pass global scripts -->
|
||||
{% include 'components/scripts.html' %}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Make sure the navigation link is active
|
||||
@@ -672,7 +679,7 @@
|
||||
</style>
|
||||
|
||||
<!-- Add beautification styles and scripts -->
|
||||
<link rel="stylesheet" href="/static/css/scheduling-beautify.css">
|
||||
<script src="/static/js/scheduling-beautify.js"></script>
|
||||
<link rel="stylesheet" href="./static/css/scheduling-beautify.css">
|
||||
<script src="./static/js/scheduling-beautify.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
document.write('<style>body, html { background-color: #1a1d24 !important; color: #f8f9fa !important; } .login-container { background-color: #252a34 !important; } .login-header { background-color: #121212 !important; }</style>');
|
||||
</script>
|
||||
<!-- Preload logo -->
|
||||
<link rel="preload" href="/static/logo/256.png" as="image" fetchpriority="high">
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
<link rel="preload" href="./static/logo/256.png" as="image" fetchpriority="high">
|
||||
<link rel="stylesheet" href="./static/css/style.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
/* Match the heart icon pulsing animation from the sidebar */
|
||||
@@ -60,9 +60,9 @@
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
</style>
|
||||
<link rel="icon" href="/static/logo/16.png">
|
||||
<link rel="icon" href="./static/logo/16.png">
|
||||
<!-- Preload script to prevent flashing -->
|
||||
<script src="/static/js/theme-preload.js"></script>
|
||||
<script src="./static/js/theme-preload.js"></script>
|
||||
<style>
|
||||
/* Modern setup page styles */
|
||||
.login-page {
|
||||
@@ -449,7 +449,7 @@
|
||||
<body class="login-page dark-mode">
|
||||
<div class="login-container">
|
||||
<div class="login-header">
|
||||
<img src="/static/logo/256.png" alt="Huntarr Logo" class="login-logo huntarr-logo">
|
||||
<img src="./static/logo/256.png" alt="Huntarr Logo" class="login-logo huntarr-logo">
|
||||
<h1>Huntarr</h1>
|
||||
</div>
|
||||
<div class="login-form">
|
||||
@@ -592,6 +592,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pass base URL configuration to JavaScript -->
|
||||
<script>window.HUNTARR_BASE_URL = '{{ base_url|default("", true) }}';</script>
|
||||
<!-- Pass global scripts -->
|
||||
{% include 'components/scripts.html' %}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Elements
|
||||
@@ -733,7 +737,7 @@
|
||||
}
|
||||
|
||||
// Create user account with improved error handling
|
||||
fetch('/setup', { // Corrected endpoint from /api/setup to /setup
|
||||
HuntarrUtils.fetchWithTimeout('/setup', { // Corrected endpoint from /api/setup to /setup
|
||||
method: 'POST',
|
||||
redirect: 'error', // Add this line to prevent following redirects
|
||||
headers: {
|
||||
@@ -770,7 +774,7 @@
|
||||
console.log('Account created successfully. User credentials should be saved to credentials.json');
|
||||
|
||||
// Generate 2FA setup - Use the correct endpoint and method
|
||||
fetch('/api/user/2fa/setup', { method: 'POST' }) // Specify POST method
|
||||
HuntarrUtils.fetchWithTimeout('/api/user/2fa/setup', { method: 'POST' }) // Specify POST method
|
||||
.then(response => {
|
||||
// Check for unauthorized specifically
|
||||
if (response.status === 401) {
|
||||
@@ -829,7 +833,7 @@
|
||||
}
|
||||
|
||||
// Verify 2FA code - Use the correct endpoint
|
||||
fetch('/api/user/2fa/verify', { // Corrected endpoint
|
||||
HuntarrUtils.fetchWithTimeout('/api/user/2fa/verify', { // Corrected endpoint
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
@@ -862,12 +866,13 @@
|
||||
const selectedAuthMode = authModeSelect.value;
|
||||
|
||||
// Save the authentication mode settings
|
||||
fetch('/api/settings/general', {
|
||||
HuntarrUtils.fetchWithTimeout('/api/settings/general', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
base_url: window.HUNTARR_BASE_URL,
|
||||
auth_mode: selectedAuthMode,
|
||||
local_access_bypass: selectedAuthMode === 'local_bypass',
|
||||
proxy_auth_bypass: selectedAuthMode === 'no_login'
|
||||
|
||||
@@ -448,9 +448,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pass base URL configuration to JavaScript -->
|
||||
<script>window.HUNTARR_BASE_URL = '{{ base_url|default("", true) }}';</script>
|
||||
<!-- Pass global scripts -->
|
||||
{% include 'components/scripts.html' %}
|
||||
<!-- Add specific reference to new-user.js -->
|
||||
<script src="/static/js/new-user.js"></script>
|
||||
<script src="./static/js/new-user.js"></script>
|
||||
<script>
|
||||
// Initialize dark mode
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
@@ -459,7 +462,7 @@
|
||||
localStorage.setItem('huntarr-dark-mode', 'true');
|
||||
|
||||
// Update server setting to dark mode
|
||||
fetch('/api/settings/theme', {
|
||||
HuntarrUtils.fetchWithTimeout('/api/settings/theme', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
|
||||
@@ -258,28 +258,26 @@ def get_username_from_session(session_id: str) -> Optional[str]:
|
||||
|
||||
def authenticate_request():
|
||||
"""Flask route decorator to check if user is authenticated"""
|
||||
# If no user exists, redirect to setup
|
||||
if not user_exists():
|
||||
script_root = request.script_root
|
||||
setup_path = f"{script_root}/setup"
|
||||
static_path = f"{script_root}/static/"
|
||||
api_setup_path = f"{script_root}/api/setup"
|
||||
|
||||
if request.path != setup_path and not request.path.startswith((static_path, api_setup_path)):
|
||||
return redirect(setup_path)
|
||||
|
||||
# Skip authentication for static files and the login/setup pages
|
||||
static_path = "/static/"
|
||||
login_path = "/login"
|
||||
api_login_path = "/api/login"
|
||||
setup_path = "/setup"
|
||||
api_setup_path = "/api/setup"
|
||||
favicon_path = "/favicon.ico"
|
||||
health_check_path = "/api/health"
|
||||
|
||||
# Skip authentication for static files, setup pages and health check path
|
||||
if request.path.startswith((static_path, setup_path, api_setup_path)) or request.path in (favicon_path, health_check_path):
|
||||
return None
|
||||
|
||||
# Skip authentication for static files and the login/setup pages
|
||||
script_root = request.script_root
|
||||
static_path = f"{script_root}/static/"
|
||||
login_path = f"{script_root}/login"
|
||||
api_login_path = f"{script_root}/api/login"
|
||||
setup_path = f"{script_root}/setup"
|
||||
api_setup_path = f"{script_root}/api/setup"
|
||||
favicon_path = f"{script_root}/favicon.ico"
|
||||
health_check_path = f"{script_root}/api/health"
|
||||
# If no user exists, redirect to setup
|
||||
if not user_exists():
|
||||
return redirect(url_for("common.setup"))
|
||||
|
||||
if request.path.startswith((static_path, login_path, api_login_path, setup_path, api_setup_path)) or request.path in (favicon_path, health_check_path):
|
||||
# Skip authentication for login pages
|
||||
if request.path.startswith((login_path, api_login_path)):
|
||||
return None
|
||||
|
||||
# Load general settings
|
||||
@@ -378,12 +376,9 @@ def authenticate_request():
|
||||
return None
|
||||
|
||||
# No valid session, redirect to login
|
||||
script_root = request.script_root
|
||||
login_path = f"{script_root}/login"
|
||||
api_path = f"{script_root}/api/"
|
||||
|
||||
if request.path != login_path and not request.path.startswith(api_path):
|
||||
return redirect(login_path)
|
||||
api_path = "/api/"
|
||||
if not request.path.startswith(api_path):
|
||||
return redirect(url_for("common.login_route"))
|
||||
|
||||
# For API calls, return 401 Unauthorized
|
||||
if request.path.startswith("/api/"):
|
||||
|
||||
@@ -62,7 +62,7 @@ def login_route():
|
||||
# User is authenticated (password correct, and 2FA if needed was correct)
|
||||
session_token = create_session(username)
|
||||
session[SESSION_COOKIE_NAME] = session_token # Store token in Flask session immediately
|
||||
response = jsonify({"success": True, "redirect": "/"}) # Add redirect URL
|
||||
response = jsonify({"success": True, "redirect": "./"}) # Add redirect URL
|
||||
response.set_cookie(SESSION_COOKIE_NAME, session_token, httponly=True, samesite='Lax', path='/') # Add path
|
||||
logger.info(f"User '{username}' logged in successfully.")
|
||||
return response
|
||||
@@ -95,7 +95,7 @@ def login_route():
|
||||
# If user already exists, show login, otherwise redirect to setup
|
||||
if not user_exists():
|
||||
logger.info("No user exists, redirecting to setup.")
|
||||
return redirect(url_for('common.setup_route'))
|
||||
return redirect(url_for('common.setup'))
|
||||
logger.debug("Displaying login page.")
|
||||
return render_template('login.html')
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ def index():
|
||||
def user_page():
|
||||
"""User settings page with UI switching capability"""
|
||||
if get_ui_preference():
|
||||
return redirect('/user/new')
|
||||
return redirect(url_for("user_page_new"))
|
||||
else:
|
||||
return render_template('user.html')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user