Add nginx proxy support with X-Forwarded-Prefix header handling

- Updated ProxyFix configuration to handle all proxy headers (x_for, x_proto, x_host, x_port, x_prefix)
- Added template context processor to inject script_root and url_root into all templates
- Created JavaScript helper functions in base template for proxy-aware URL generation:
  - buildUrl() - Build URLs with correct proxy prefix
  - proxyFetch() - Make fetch requests with proxy-aware URLs
  - navigateTo() - Navigate to URLs with proxy prefix
  - getCurrentUrl() - Get current URL with proxy prefix
  - buildUrlWithParams() - Build URLs with query parameters
- Updated all templates with hardcoded JavaScript URLs to use proxyFetch():
  - admin_settings.html
  - admin_users.html
  - admin_migrations.html
  - match_detail.html
  - fixture_detail.html
  - upload/zip.html
  - user_tokens.html
- Updated templates with window.location.href to use navigateTo():
  - clients.html
  - reports.html
  - client_report_detail.html
- Updated templates using window.location.href for URL manipulation to use getCurrentUrl():
  - match_report_detail.html
  - bet_detail.html
  - reports.html
  - client_report_detail.html
- Created PROXY_SUPPORT.md documentation file

This implementation ensures all URLs generated and managed in the web dashboard correctly handle the root path when behind an nginx proxy with X-Forwarded-Prefix header (e.g., /mbetterc).
parent d68f5b8d
# Proxy Support Documentation
## Overview
The mbetterd web dashboard now supports running behind an nginx reverse proxy with the `X-Forwarded-Prefix` header. This allows the application to be served from a subpath (e.g., `/mbetterc`) while maintaining correct URL generation for all links, forms, and JavaScript requests.
## Nginx Configuration
The nginx proxy should be configured with the following headers:
```nginx
# Headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Prefix /mbetterc;
proxy_set_header Connection "";
```
## Implementation Details
### 1. Flask ProxyFix Configuration
The application uses Werkzeug's `ProxyFix` middleware to handle proxy headers:
```python
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_port=1, x_prefix=1)
```
This configuration:
- `x_for=1`: Trusts `X-Forwarded-For` header (1 proxy)
- `x_proto=1`: Trusts `X-Forwarded-Proto` header (1 proxy)
- `x_host=1`: Trusts `X-Forwarded-Host` header (1 proxy)
- `x_port=1`: Trusts `X-Forwarded-Port` header (1 proxy)
- `x_prefix=1`: Trusts `X-Forwarded-Prefix` header (1 proxy)
### 2. Template Context Processor
A template context processor makes `script_root` and `url_root` available in all templates:
```python
@app.context_processor
def inject_script_root():
"""Make script_root available in all templates for proxy support"""
return {
'script_root': request.script_root if request else '',
'url_root': request.url_root if request else ''
}
```
### 3. JavaScript URL Helper Functions
The base template includes JavaScript helper functions for proxy-aware URL generation:
```javascript
// Get the script root from Flask (includes X-Forwarded-Prefix if behind proxy)
const SCRIPT_ROOT = {{ request.script_root | tojson | safe }};
// Build a URL with the correct proxy prefix
function buildUrl(path) { ... }
// Make a fetch request with proxy-aware URL
function proxyFetch(path, options = {}) { ... }
// Navigate to a URL with proxy prefix
function navigateTo(path) { ... }
// Get current URL with proxy prefix
function getCurrentUrl() { ... }
// Build URL with query parameters
function buildUrlWithParams(path, params = {}) { ... }
```
## Usage
### In Templates (Jinja2)
Flask's `url_for()` function automatically handles the proxy prefix:
```jinja2
<a href="{{ url_for('main.dashboard') }}">Dashboard</a>
<form action="{{ url_for('upload.upload_fixture') }}" method="POST">
```
### In JavaScript
Use the provided helper functions instead of hardcoded URLs:
```javascript
// Instead of: fetch('/api/dashboard-data')
// Use:
proxyFetch('/api/dashboard-data')
// Instead of: window.location.href = '/clients'
// Use:
navigateTo('/clients')
// Instead of: window.location.href
// Use:
getCurrentUrl()
// Build URLs with parameters:
const url = buildUrlWithParams('/reports', { page: 2, per_page: 20 });
```
## Updated Templates
The following templates have been updated to use proxy-aware URL generation:
- `app/templates/base.html` - Added JavaScript helper functions
- `app/templates/main/admin_settings.html` - Updated fetch calls
- `app/templates/main/admin_users.html` - Updated fetch calls
- `app/templates/main/admin_migrations.html` - Updated fetch calls
- `app/templates/main/match_detail.html` - Updated fetch calls
- `app/templates/main/fixture_detail.html` - Updated fetch calls
- `app/templates/upload/zip.html` - Updated fetch calls
- `app/templates/main/user_tokens.html` - Updated fetch calls
- `app/templates/main/clients.html` - Updated URL building
- `app/templates/main/reports.html` - Updated URL building
- `app/templates/main/client_report_detail.html` - Updated URL building
- `app/templates/main/match_report_detail.html` - Updated URL building
- `app/templates/main/bet_detail.html` - Updated URL building
## Testing
To test the proxy support:
1. Configure nginx with the `X-Forwarded-Prefix` header
2. Access the application through the proxy URL (e.g., `https://example.com/mbetterc`)
3. Verify that:
- All navigation links work correctly
- Forms submit to the correct URLs
- JavaScript fetch requests work properly
- Pagination links work correctly
- Export functionality works
## Troubleshooting
### URLs not working behind proxy
1. Verify nginx is sending the `X-Forwarded-Prefix` header
2. Check that `ProxyFix` is configured with `x_prefix=1`
3. Ensure JavaScript code uses `proxyFetch()` instead of `fetch()`
### Static files not loading
1. Verify nginx is configured to serve static files or proxy them correctly
2. Check that the `X-Forwarded-Host` header is set correctly
### Redirects not working
1. Flask's `redirect()` and `url_for()` should work automatically
2. If using manual redirects, use `buildUrl()` or `navigateTo()` functions
## Notes
- The `SCRIPT_ROOT` variable is injected into the JavaScript from Flask's `request.script_root`
- This value automatically includes the `X-Forwarded-Prefix` when behind a proxy
- All new JavaScript code should use the provided helper functions for URL generation
- The implementation is backward compatible - it works with or without a proxy
## References
- [Flask ProxyFix Documentation](https://werkzeug.palletsprojects.com/en/stable/middleware/proxy_fix/)
- [Nginx Proxy Headers](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_set_header)
\ No newline at end of file
...@@ -22,10 +22,25 @@ def create_app(config_name=None): ...@@ -22,10 +22,25 @@ def create_app(config_name=None):
config_name = os.environ.get('FLASK_ENV', 'default') config_name = os.environ.get('FLASK_ENV', 'default')
app = Flask(__name__) app = Flask(__name__)
# Configure ProxyFix to handle nginx proxy headers
# x_for=1: trust X-Forwarded-For header (1 proxy)
# x_proto=1: trust X-Forwarded-Proto header (1 proxy)
# x_host=1: trust X-Forwarded-Host header (1 proxy)
# x_port=1: trust X-Forwarded-Port header (1 proxy)
# x_prefix=1: trust X-Forwarded-Prefix header (1 proxy)
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_port=1, x_prefix=1) app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_port=1, x_prefix=1)
app.config.from_object(config[config_name]) app.config.from_object(config[config_name])
config[config_name].init_app(app) config[config_name].init_app(app)
# Add template context processor for script root (for proxy support)
@app.context_processor
def inject_script_root():
"""Make script_root available in all templates for proxy support"""
return {
'script_root': request.script_root if request else '',
'url_root': request.url_root if request else ''
}
# Initialize extensions # Initialize extensions
db.init_app(app) db.init_app(app)
login_manager.init_app(app) login_manager.init_app(app)
......
...@@ -445,6 +445,71 @@ ...@@ -445,6 +445,71 @@
<!-- Bootstrap JS --> <!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<!-- Proxy URL Helper for JavaScript -->
<script>
// Get the script root from Flask (includes X-Forwarded-Prefix if behind proxy)
const SCRIPT_ROOT = {{ request.script_root | tojson | safe }};
/**
* Build a URL with the correct proxy prefix
* @param {string} path - The path to append (e.g., '/api/dashboard-data')
* @returns {string} The full URL with proxy prefix
*/
function buildUrl(path) {
// Ensure path starts with /
if (!path.startsWith('/')) {
path = '/' + path;
}
// Remove leading slash from script_root if present
const root = SCRIPT_ROOT.startsWith('/') ? SCRIPT_ROOT.slice(1) : SCRIPT_ROOT;
// Combine script root and path
return '/' + root + path;
}
/**
* Make a fetch request with proxy-aware URL
* @param {string} path - The API path
* @param {object} options - Fetch options
* @returns {Promise} Fetch promise
*/
function proxyFetch(path, options = {}) {
return fetch(buildUrl(path), options);
}
/**
* Navigate to a URL with proxy prefix
* @param {string} path - The path to navigate to
*/
function navigateTo(path) {
window.location.href = buildUrl(path);
}
/**
* Get current URL with proxy prefix
* @returns {string} Current full URL
*/
function getCurrentUrl() {
return window.location.href;
}
/**
* Build URL with query parameters
* @param {string} path - The base path
* @param {object} params - Query parameters object
* @returns {string} URL with query parameters
*/
function buildUrlWithParams(path, params = {}) {
const url = new URL(buildUrl(path), window.location.origin);
Object.keys(params).forEach(key => {
if (params[key] !== null && params[key] !== undefined) {
url.searchParams.set(key, params[key]);
}
});
return url.toString();
}
</script>
{% block extra_js %}{% endblock %} {% block extra_js %}{% endblock %}
</body> </body>
</html> </html>
\ No newline at end of file
...@@ -222,7 +222,7 @@ function addToLog(message, type = 'info') { ...@@ -222,7 +222,7 @@ function addToLog(message, type = 'info') {
function refreshMigrationStatus() { function refreshMigrationStatus() {
addToLog('Refreshing migration status...', 'info'); addToLog('Refreshing migration status...', 'info');
fetch('{{ url_for("main.admin_migration_status") }}') proxyFetch('{{ url_for("main.admin_migration_status") }}')
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
if (data.success) { if (data.success) {
...@@ -284,7 +284,7 @@ function runMigrations() { ...@@ -284,7 +284,7 @@ function runMigrations() {
addToLog('Starting migration process...', 'info'); addToLog('Starting migration process...', 'info');
fetch('{{ url_for("main.admin_run_migrations") }}', { proxyFetch('{{ url_for("main.admin_run_migrations") }}', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
......
...@@ -237,7 +237,7 @@ ...@@ -237,7 +237,7 @@
<script> <script>
// Toggle registration setting // Toggle registration setting
function toggleRegistration(enabled) { function toggleRegistration(enabled) {
fetch('/admin/settings/registration', { proxyFetch('/admin/settings/registration', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
...@@ -266,7 +266,7 @@ function toggleRegistration(enabled) { ...@@ -266,7 +266,7 @@ function toggleRegistration(enabled) {
// Toggle maintenance mode // Toggle maintenance mode
function toggleMaintenance(enabled) { function toggleMaintenance(enabled) {
fetch('/admin/settings/maintenance_mode', { proxyFetch('/admin/settings/maintenance_mode', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
...@@ -303,7 +303,7 @@ function editSetting(key, value) { ...@@ -303,7 +303,7 @@ function editSetting(key, value) {
// Delete setting // Delete setting
function deleteSetting(key) { function deleteSetting(key) {
if (confirm('Are you sure you want to delete the setting "' + key + '"?')) { if (confirm('Are you sure you want to delete the setting "' + key + '"?')) {
fetch('/admin/settings/' + key, { proxyFetch('/admin/settings/' + key, {
method: 'DELETE' method: 'DELETE'
}) })
.then(response => response.json()) .then(response => response.json())
...@@ -330,7 +330,7 @@ document.getElementById('addSettingForm').addEventListener('submit', function(e) ...@@ -330,7 +330,7 @@ document.getElementById('addSettingForm').addEventListener('submit', function(e)
const type = document.getElementById('settingType').value; const type = document.getElementById('settingType').value;
const description = document.getElementById('settingDescription').value; const description = document.getElementById('settingDescription').value;
fetch('/admin/settings/' + key, { proxyFetch('/admin/settings/' + key, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
...@@ -362,7 +362,7 @@ document.getElementById('editSettingForm').addEventListener('submit', function(e ...@@ -362,7 +362,7 @@ document.getElementById('editSettingForm').addEventListener('submit', function(e
const key = document.getElementById('editSettingKey').value; const key = document.getElementById('editSettingKey').value;
const value = document.getElementById('editSettingValue').value; const value = document.getElementById('editSettingValue').value;
fetch('/admin/settings/' + key, { proxyFetch('/admin/settings/' + key, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
......
...@@ -481,7 +481,7 @@ ...@@ -481,7 +481,7 @@
// Registration toggle functionality // Registration toggle functionality
function loadRegistrationSettings() { function loadRegistrationSettings() {
fetch('/admin/settings/registration') proxyFetch('/admin/settings/registration')
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
const toggle = document.getElementById('registrationToggle'); const toggle = document.getElementById('registrationToggle');
...@@ -503,7 +503,7 @@ ...@@ -503,7 +503,7 @@
function toggleRegistration(enabled) { function toggleRegistration(enabled) {
const data = { registration_enabled: enabled }; const data = { registration_enabled: enabled };
fetch('/admin/settings/registration', { proxyFetch('/admin/settings/registration', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
...@@ -560,7 +560,7 @@ ...@@ -560,7 +560,7 @@
if (confirm(`Are you sure you want to ${action} this user?`)) { if (confirm(`Are you sure you want to ${action} this user?`)) {
const data = { is_active: activate }; const data = { is_active: activate };
fetch(`/admin/users/${userId}/edit`, { proxyFetch(`/admin/users/${userId}/edit`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
...@@ -585,7 +585,7 @@ ...@@ -585,7 +585,7 @@
function deleteUser(userId) { function deleteUser(userId) {
if (confirm('Are you sure you want to delete this user? This action cannot be undone.')) { if (confirm('Are you sure you want to delete this user? This action cannot be undone.')) {
fetch(`/admin/users/${userId}/delete`, { proxyFetch(`/admin/users/${userId}/delete`, {
method: 'DELETE' method: 'DELETE'
}) })
.then(response => response.json()) .then(response => response.json())
...@@ -623,7 +623,7 @@ ...@@ -623,7 +623,7 @@
return; return;
} }
fetch('/admin/users/create', { proxyFetch('/admin/users/create', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
...@@ -658,7 +658,7 @@ ...@@ -658,7 +658,7 @@
is_admin: formData.get('is_admin') === 'on' is_admin: formData.get('is_admin') === 'on'
}; };
fetch(`/admin/users/${userId}/edit`, { proxyFetch(`/admin/users/${userId}/edit`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
...@@ -697,7 +697,7 @@ ...@@ -697,7 +697,7 @@
const data = { new_password: newPassword }; const data = { new_password: newPassword };
fetch(`/admin/users/${userId}/reset-password`, { proxyFetch(`/admin/users/${userId}/reset-password`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
......
...@@ -288,7 +288,7 @@ ...@@ -288,7 +288,7 @@
<script> <script>
function changeTimezone() { function changeTimezone() {
const timezone = document.getElementById('timezone').value; const timezone = document.getElementById('timezone').value;
const url = new URL(window.location.href); const url = new URL(getCurrentUrl());
url.searchParams.set('timezone', timezone); url.searchParams.set('timezone', timezone);
window.location.href = url.toString(); window.location.href = url.toString();
} }
......
...@@ -541,12 +541,12 @@ document.addEventListener('DOMContentLoaded', function() { ...@@ -541,12 +541,12 @@ document.addEventListener('DOMContentLoaded', function() {
}); });
function resetFilters() { function resetFilters() {
window.location.href = "{{ url_for('main.client_report_detail', client_id=client_id) }}"; navigateTo("{{ url_for('main.client_report_detail', client_id=client_id) }}");
} }
function changePerPage() { function changePerPage() {
const perPage = document.getElementById('perPage').value; const perPage = document.getElementById('perPage').value;
const url = new URL(window.location.href); const url = new URL(getCurrentUrl());
url.searchParams.set('per_page', perPage); url.searchParams.set('per_page', perPage);
url.searchParams.set('page', '1'); url.searchParams.set('page', '1');
window.location.href = url.toString(); window.location.href = url.toString();
......
...@@ -196,7 +196,7 @@ document.addEventListener('DOMContentLoaded', function() { ...@@ -196,7 +196,7 @@ document.addEventListener('DOMContentLoaded', function() {
const searchQuery = searchInput ? searchInput.value.trim() : ''; const searchQuery = searchInput ? searchInput.value.trim() : '';
const statusValue = statusFilter ? statusFilter.value : ''; const statusValue = statusFilter ? statusFilter.value : '';
let url = '{{ url_for("main.clients") }}?'; let url = buildUrl('{{ url_for("main.clients") }}') + '?';
if (searchQuery) { if (searchQuery) {
url += 'search=' + encodeURIComponent(searchQuery) + '&'; url += 'search=' + encodeURIComponent(searchQuery) + '&';
} }
...@@ -238,7 +238,7 @@ document.addEventListener('DOMContentLoaded', function() { ...@@ -238,7 +238,7 @@ document.addEventListener('DOMContentLoaded', function() {
if (document.getElementById('resetFilterButton')) { if (document.getElementById('resetFilterButton')) {
document.getElementById('resetFilterButton').addEventListener('click', function() { document.getElementById('resetFilterButton').addEventListener('click', function() {
console.log('Reset filter button clicked'); console.log('Reset filter button clicked');
window.location.href = '{{ url_for("main.clients") }}'; navigateTo('{{ url_for("main.clients") }}');
}); });
} }
......
...@@ -566,7 +566,7 @@ ...@@ -566,7 +566,7 @@
formData.append('fileName', file.name); formData.append('fileName', file.name);
formData.append('matchId', matchId); formData.append('matchId', matchId);
return fetch('/upload/chunk', { return proxyFetch('/upload/chunk', {
method: 'POST', method: 'POST',
body: formData body: formData
}) })
...@@ -630,7 +630,7 @@ ...@@ -630,7 +630,7 @@
function finalizeUpload(uploadId, fileName, matchId) { function finalizeUpload(uploadId, fileName, matchId) {
const statusDiv = document.getElementById(`status_${matchId}`); const statusDiv = document.getElementById(`status_${matchId}`);
fetch('/upload/finalize', { proxyFetch('/upload/finalize', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
...@@ -824,7 +824,7 @@ ...@@ -824,7 +824,7 @@
formData.append('uploadId', uploadId); formData.append('uploadId', uploadId);
formData.append('fileName', file.name); formData.append('fileName', file.name);
return fetch('/upload/chunk', { return proxyFetch('/upload/chunk', {
method: 'POST', method: 'POST',
body: formData body: formData
}) })
...@@ -889,7 +889,7 @@ ...@@ -889,7 +889,7 @@
function finalizeFixtureUpload(uploadId, fileName) { function finalizeFixtureUpload(uploadId, fileName) {
const statusDiv = document.getElementById('fixture_upload_status'); const statusDiv = document.getElementById('fixture_upload_status');
fetch('/upload/finalize', { proxyFetch('/upload/finalize', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
......
...@@ -798,7 +798,7 @@ ...@@ -798,7 +798,7 @@
formData.append('fileName', file.name); formData.append('fileName', file.name);
formData.append('matchId', matchId); formData.append('matchId', matchId);
return fetch('/upload/chunk', { return proxyFetch('/upload/chunk', {
method: 'POST', method: 'POST',
body: formData body: formData
}) })
...@@ -862,7 +862,7 @@ ...@@ -862,7 +862,7 @@
function finalizeUpload(uploadId, fileName, matchId) { function finalizeUpload(uploadId, fileName, matchId) {
const statusDiv = document.getElementById(`status_${matchId}`); const statusDiv = document.getElementById(`status_${matchId}`);
fetch('/upload/finalize', { proxyFetch('/upload/finalize', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
......
...@@ -438,7 +438,7 @@ ...@@ -438,7 +438,7 @@
<script> <script>
function changePerPage() { function changePerPage() {
const perPage = document.getElementById('perPage').value; const perPage = document.getElementById('perPage').value;
const url = new URL(window.location.href); const url = new URL(getCurrentUrl());
url.searchParams.set('per_page', perPage); url.searchParams.set('per_page', perPage);
url.searchParams.set('page', '1'); url.searchParams.set('page', '1');
window.location.href = url.toString(); window.location.href = url.toString();
...@@ -446,7 +446,7 @@ function changePerPage() { ...@@ -446,7 +446,7 @@ function changePerPage() {
function changeTimezone() { function changeTimezone() {
const timezone = document.getElementById('timezone').value; const timezone = document.getElementById('timezone').value;
const url = new URL(window.location.href); const url = new URL(getCurrentUrl());
url.searchParams.set('timezone', timezone); url.searchParams.set('timezone', timezone);
window.location.href = url.toString(); window.location.href = url.toString();
} }
......
...@@ -546,12 +546,12 @@ document.addEventListener('DOMContentLoaded', function() { ...@@ -546,12 +546,12 @@ document.addEventListener('DOMContentLoaded', function() {
}); });
function resetFilters() { function resetFilters() {
window.location.href = "{{ url_for('main.reports') }}"; navigateTo("{{ url_for('main.reports') }}");
} }
function changePerPage() { function changePerPage() {
const perPage = document.getElementById('perPage').value; const perPage = document.getElementById('perPage').value;
const url = new URL(window.location.href); const url = new URL(getCurrentUrl());
url.searchParams.set('per_page', perPage); url.searchParams.set('per_page', perPage);
url.searchParams.set('page', '1'); url.searchParams.set('page', '1');
window.location.href = url.toString(); window.location.href = url.toString();
......
...@@ -585,7 +585,7 @@ document.getElementById('createTokenForm').addEventListener('submit', async func ...@@ -585,7 +585,7 @@ document.getElementById('createTokenForm').addEventListener('submit', async func
} }
try { try {
const response = await fetch('{{ url_for("main.create_api_token") }}', { const response = await proxyFetch('{{ url_for("main.create_api_token") }}', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
...@@ -622,7 +622,7 @@ document.getElementById('extendTokenForm').addEventListener('submit', async func ...@@ -622,7 +622,7 @@ document.getElementById('extendTokenForm').addEventListener('submit', async func
const days = parseInt(formData.get('days')); const days = parseInt(formData.get('days'));
try { try {
const response = await fetch(`{{ url_for("main.extend_api_token", token_id=0) }}`.replace('0', currentTokenId), { const response = await proxyFetch(`{{ url_for("main.extend_api_token", token_id=0) }}`.replace('0', currentTokenId), {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
...@@ -652,7 +652,7 @@ async function revokeToken(tokenId, tokenName) { ...@@ -652,7 +652,7 @@ async function revokeToken(tokenId, tokenName) {
} }
try { try {
const response = await fetch(`{{ url_for("main.revoke_api_token", token_id=0) }}`.replace('0', tokenId), { const response = await proxyFetch(`{{ url_for("main.revoke_api_token", token_id=0) }}`.replace('0', tokenId), {
method: 'POST' method: 'POST'
}); });
...@@ -676,7 +676,7 @@ async function deleteToken(tokenId, tokenName) { ...@@ -676,7 +676,7 @@ async function deleteToken(tokenId, tokenName) {
} }
try { try {
const response = await fetch(`{{ url_for("main.delete_api_token", token_id=0) }}`.replace('0', tokenId), { const response = await proxyFetch(`{{ url_for("main.delete_api_token", token_id=0) }}`.replace('0', tokenId), {
method: 'DELETE' method: 'DELETE'
}); });
......
...@@ -178,7 +178,7 @@ ...@@ -178,7 +178,7 @@
formData.append('fileName', file.name); formData.append('fileName', file.name);
formData.append('matchId', matchId || ''); formData.append('matchId', matchId || '');
fetch('/upload/chunk', { proxyFetch('/upload/chunk', {
method: 'POST', method: 'POST',
body: formData body: formData
}) })
...@@ -204,7 +204,7 @@ ...@@ -204,7 +204,7 @@
} }
function finalizeUpload(uploadId, fileName, matchId) { function finalizeUpload(uploadId, fileName, matchId) {
fetch('/upload/finalize', { proxyFetch('/upload/finalize', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment