Fix subpath support

This commit is contained in:
Quentin Machu
2025-05-21 18:02:22 -04:00
parent 9a5c6883af
commit b60c15543b
29 changed files with 179 additions and 162 deletions

View File

@@ -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'

View File

@@ -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'

View File

@@ -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');

View File

@@ -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}`);

View File

@@ -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'

View File

@@ -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 => {

View File

@@ -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');

View File

@@ -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';

View File

@@ -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}`);

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -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';
}
};
}

View File

@@ -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');

View File

@@ -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>

View File

@@ -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

View File

@@ -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"

View File

@@ -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}`);
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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',

View File

@@ -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>

View File

@@ -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'

View File

@@ -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'

View File

@@ -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/"):

View File

@@ -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')

View File

@@ -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')