add connections

This commit is contained in:
2025-06-11 10:29:58 +02:00
parent 468235662b
commit 04689797f7
8 changed files with 1622 additions and 4 deletions

View File

@@ -0,0 +1,89 @@
{% macro connection_modals() %}
<!-- Test Connection Modal -->
<div class="modal fade" id="testConnectionModal" tabindex="-1" aria-labelledby="testConnectionModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="testConnectionModalLabel">Testing Connection</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="text-center">
<div class="spinner-border text-primary mb-3" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p id="testConnectionMessage">Testing connection...</p>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<!-- Save Connection Modal -->
<div class="modal fade" id="saveConnectionModal" tabindex="-1" aria-labelledby="saveConnectionModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="saveConnectionModalLabel">Save Connection</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="text-center">
<div class="spinner-border text-primary mb-3" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p id="saveConnectionMessage">Saving connection settings...</p>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<!-- Error Modal -->
<div class="modal fade" id="errorModal" tabindex="-1" aria-labelledby="errorModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="errorModalLabel">Error</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="text-center">
<i class="fas fa-exclamation-circle text-danger fa-3x mb-3"></i>
<p id="errorMessage"></p>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<!-- Success Modal -->
<div class="modal fade" id="successModal" tabindex="-1" aria-labelledby="successModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="successModalLabel">Success</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="text-center">
<i class="fas fa-check-circle text-success fa-3x mb-3"></i>
<p id="successMessage"></p>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
{% endmacro %}

View File

@@ -8,6 +8,7 @@
{% from "settings/tabs/email_templates.html" import email_templates_tab %}
{% from "settings/tabs/mails.html" import mails_tab %}
{% from "settings/tabs/smtp_settings.html" import smtp_settings_tab %}
{% from "settings/tabs/connections.html" import connections_tab %}
{% from "settings/components/reset_colors_modal.html" import reset_colors_modal %}
{% block title %}Settings - DocuPulse{% endblock %}
@@ -75,6 +76,13 @@
<i class="fas fa-server me-2"></i>SMTP
</button>
</li>
{% if is_master %}
<li class="nav-item" role="presentation">
<button class="nav-link {% if active_tab == 'connections' %}active{% endif %}" id="connections-tab" data-bs-toggle="tab" data-bs-target="#connections" type="button" role="tab" aria-controls="connections" aria-selected="{{ 'true' if active_tab == 'connections' else 'false' }}">
<i class="fas fa-plug me-2"></i>Connections
</button>
</li>
{% endif %}
</ul>
</div>
<div class="card-body">
@@ -122,6 +130,13 @@
<div class="tab-pane fade {% if active_tab == 'smtp' %}show active{% endif %}" id="smtp" role="tabpanel" aria-labelledby="smtp-tab">
{{ smtp_settings_tab(smtp_settings, csrf_token) }}
</div>
{% if is_master %}
<!-- Connections Tab -->
<div class="tab-pane fade {% if active_tab == 'connections' %}show active{% endif %}" id="connections" role="tabpanel" aria-labelledby="connections-tab">
{{ connections_tab(portainer_settings, nginx_settings, site_settings, git_settings) }}
</div>
{% endif %}
</div>
</div>
</div>

View File

@@ -0,0 +1,732 @@
{% from "settings/components/connection_modals.html" import connection_modals %}
{% macro connections_tab(portainer_settings, nginx_settings, site_settings, git_settings) %}
<div class="container-fluid">
<div class="row">
<!-- Portainer Connection Card -->
<div class="col-md-6 mb-4">
<div class="card h-100">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">
<i class="fas fa-server me-2"></i>Portainer Connection
</h5>
<button class="btn btn-sm btn-outline-primary" onclick="testPortainerConnection()">
<i class="fas fa-plug me-1"></i>Test Connection
</button>
</div>
<div class="card-body">
<form id="portainerForm" onsubmit="savePortainerConnection(event)">
<div class="mb-3">
<label for="portainerUrl" class="form-label">Portainer URL</label>
<input type="url" class="form-control" id="portainerUrl" name="portainerUrl"
placeholder="https://portainer.example.com" required
value="{{ portainer_settings.url if portainer_settings and portainer_settings.url else '' }}">
<div class="form-text">The URL of your Portainer instance</div>
</div>
<div class="mb-3">
<label for="portainerApiKey" class="form-label">API Key</label>
<input type="password" class="form-control" id="portainerApiKey" name="portainerApiKey"
placeholder="Enter your Portainer API key" required
value="{{ portainer_settings.api_key if portainer_settings and portainer_settings.api_key else '' }}">
<div class="form-text">You can generate this in Portainer under Settings > API Keys</div>
</div>
<div class="d-flex justify-content-end">
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-1"></i>Save Portainer Settings
</button>
</div>
</form>
</div>
</div>
</div>
<!-- NGINX Connection Card -->
<div class="col-md-6 mb-4">
<div class="card h-100">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">
<i class="fas fa-network-wired me-2"></i>NGINX Proxy Manager Connection
</h5>
<button class="btn btn-sm btn-outline-primary" onclick="testNginxConnection()">
<i class="fas fa-plug me-1"></i>Test Connection
</button>
</div>
<div class="card-body">
<form id="nginxForm" onsubmit="saveNginxConnection(event)">
<div class="mb-3">
<label for="nginxUrl" class="form-label">NGINX Proxy Manager URL</label>
<input type="url" class="form-control" id="nginxUrl" name="nginxUrl"
placeholder="https://nginx.example.com" required
value="{{ nginx_settings.url if nginx_settings and nginx_settings.url else '' }}">
<div class="form-text">The URL of your NGINX Proxy Manager instance</div>
</div>
<div class="mb-3">
<label for="nginxUsername" class="form-label">Username</label>
<input type="text" class="form-control" id="nginxUsername" name="nginxUsername"
placeholder="Enter your NGINX Proxy Manager username" required
value="{{ nginx_settings.username if nginx_settings and nginx_settings.username else '' }}">
</div>
<div class="mb-3">
<label for="nginxPassword" class="form-label">Password</label>
<input type="password" class="form-control" id="nginxPassword" name="nginxPassword"
placeholder="Enter your NGINX Proxy Manager password" required
value="{{ nginx_settings.password if nginx_settings and nginx_settings.password else '' }}">
</div>
<div class="d-flex justify-content-end">
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-1"></i>Save NGINX Settings
</button>
</div>
</form>
</div>
</div>
</div>
<!-- GitLab Connection Card -->
<div class="col-md-6 mb-4">
<div class="card h-100">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">
<i class="fab fa-gitlab me-2"></i>GitLab Connection
</h5>
<button class="btn btn-sm btn-outline-primary" onclick="testGitConnection('gitlab')">
<i class="fas fa-plug me-1"></i>Test Connection
</button>
</div>
<div class="card-body">
<form id="gitlabForm" onsubmit="saveGitConnection(event, 'gitlab')">
<div class="mb-3">
<label for="gitlabUrl" class="form-label">GitLab Server URL</label>
<input type="url" class="form-control" id="gitlabUrl" name="gitlabUrl"
placeholder="https://gitlab.com" required
value="{{ git_settings.url if git_settings and git_settings.provider == 'gitlab' and git_settings.url else '' }}">
<div class="form-text">The URL of your GitLab server (use https://gitlab.com for GitLab.com)</div>
</div>
<div class="mb-3">
<label for="gitlabUsername" class="form-label">Username</label>
<input type="text" class="form-control" id="gitlabUsername" name="gitlabUsername"
placeholder="Enter your GitLab username" required
value="{{ git_settings.username if git_settings and git_settings.provider == 'gitlab' and git_settings.username else '' }}">
</div>
<div class="mb-3">
<label for="gitlabToken" class="form-label">Personal Access Token</label>
<input type="password" class="form-control" id="gitlabToken" name="gitlabToken"
placeholder="Enter your GitLab personal access token" required
value="{{ git_settings.token if git_settings and git_settings.provider == 'gitlab' and git_settings.token else '' }}">
<div class="form-text">You can generate this in your GitLab user settings > Access Tokens</div>
</div>
<div class="mb-3">
<label for="gitlabRepo" class="form-label">Repository</label>
<div class="input-group">
<select class="form-select" id="gitlabRepo" name="gitlabRepo" required>
<option value="">Select a repository</option>
{% if git_settings and git_settings.provider == 'gitlab' and git_settings.repo %}
<option value="{{ git_settings.repo }}" selected>{{ git_settings.repo }}</option>
{% endif %}
</select>
<button class="btn btn-outline-secondary" type="button" onclick="loadGitlabRepos()">
<i class="fas fa-sync-alt"></i>
</button>
</div>
<div class="form-text">Select the repository to connect to</div>
</div>
<div class="d-flex justify-content-end">
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-1"></i>Save GitLab Settings
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Gitea Connection Card -->
<div class="col-md-6 mb-4">
<div class="card h-100">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">
<i class="fas fa-code-branch me-2"></i>Gitea Connection
</h5>
<button class="btn btn-sm btn-outline-primary" onclick="testGitConnection('gitea')">
<i class="fas fa-plug me-1"></i>Test Connection
</button>
</div>
<div class="card-body">
<form id="giteaForm" onsubmit="saveGitConnection(event, 'gitea')">
<div class="mb-3">
<label for="giteaUrl" class="form-label">Gitea Server URL</label>
<input type="url" class="form-control" id="giteaUrl" name="giteaUrl"
placeholder="https://gitea.example.com" required
value="{{ git_settings.url if git_settings and git_settings.provider == 'gitea' and git_settings.url else '' }}">
<div class="form-text">The URL of your Gitea server</div>
</div>
<div class="mb-3">
<label for="giteaUsername" class="form-label">Username</label>
<input type="text" class="form-control" id="giteaUsername" name="giteaUsername"
placeholder="Enter your Gitea username" required
value="{{ git_settings.username if git_settings and git_settings.provider == 'gitea' and git_settings.username else '' }}">
</div>
<div class="mb-3">
<label for="giteaPassword" class="form-label">Password</label>
<input type="password" class="form-control" id="giteaPassword" name="giteaPassword"
placeholder="Enter your Gitea password"
value="{{ git_settings.password if git_settings and git_settings.provider == 'gitea' and git_settings.password else '' }}">
<div class="form-text">Required for token generation if you don't have an existing token</div>
</div>
<div class="mb-3">
<label for="giteaOtp" class="form-label">Two-Factor Code (if enabled)</label>
<input type="text" class="form-control" id="giteaOtp" name="giteaOtp"
placeholder="Enter 2FA code if enabled">
<div class="form-text">Required if you have two-factor authentication enabled</div>
</div>
<div class="mb-3">
<label for="giteaToken" class="form-label">Access Token</label>
<div class="input-group">
<input type="password" class="form-control" id="giteaToken" name="giteaToken"
placeholder="Enter your Gitea access token" required
value="{{ git_settings.token if git_settings and git_settings.provider == 'gitea' and git_settings.token else '' }}">
<button class="btn btn-outline-secondary" type="button" onclick="generateGiteaToken()">
<i class="fas fa-key me-1"></i>Generate Token
</button>
</div>
<div class="form-text">You can generate a new token or use an existing one</div>
</div>
<div class="mb-3">
<label for="giteaRepo" class="form-label">Repository</label>
<div class="input-group">
<select class="form-select" id="giteaRepo" name="giteaRepo" required>
<option value="">Select a repository</option>
{% if git_settings and git_settings.provider == 'gitea' and git_settings.repo %}
<option value="{{ git_settings.repo }}" selected>{{ git_settings.repo }}</option>
{% endif %}
</select>
<button class="btn btn-outline-secondary" type="button" onclick="loadGiteaRepos()">
<i class="fas fa-sync-alt"></i>
</button>
</div>
<div class="form-text">Select the repository to connect to</div>
</div>
<div class="d-flex justify-content-end">
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-1"></i>Save Gitea Settings
</button>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- Save Connection Modal -->
<div class="modal fade" id="saveConnectionModal" tabindex="-1" aria-labelledby="saveConnectionModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="saveConnectionModalLabel">Save Connection</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p id="saveConnectionMessage"></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div>
{{ connection_modals() }}
<script>
// Form validation
(function () {
'use strict'
var forms = document.querySelectorAll('.needs-validation')
Array.prototype.slice.call(forms).forEach(function (form) {
form.addEventListener('submit', function (event) {
if (!form.checkValidity()) {
event.preventDefault()
event.stopPropagation()
}
form.classList.add('was-validated')
}, false)
})
})()
// Show success modal
function showSuccess(message) {
const successModal = document.getElementById('successModal');
if (successModal) {
document.getElementById('successMessage').textContent = message;
new bootstrap.Modal(successModal).show();
} else {
alert(message); // Fallback if modal doesn't exist
}
}
// Show error modal
function showError(message) {
const errorModal = document.getElementById('errorModal');
if (errorModal) {
document.getElementById('errorMessage').textContent = message;
new bootstrap.Modal(errorModal).show();
} else {
alert(message); // Fallback if modal doesn't exist
}
}
// Get CSRF token from meta tag
function getCsrfToken() {
const metaTag = document.querySelector('meta[name="csrf-token"]');
return metaTag ? metaTag.getAttribute('content') : '';
}
// Get JWT token using management API key
async function getJwtToken() {
try {
const response = await fetch('/api/admin/management-token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': '{{ site_settings.management_api_key }}'
}
});
if (!response.ok) {
throw new Error('Failed to get JWT token');
}
const data = await response.json();
return data.token;
} catch (error) {
console.error('Error getting JWT token:', error);
throw error;
}
}
// Load Gitea Repositories
async function loadGiteaRepos() {
const url = document.getElementById('giteaUrl').value;
const token = document.getElementById('giteaToken').value;
const repoSelect = document.getElementById('giteaRepo');
const currentRepo = repoSelect.value; // Store current selection
if (!url || !token) {
showError('Please fill in the server URL and access token');
return;
}
try {
const response = await fetch('/api/admin/list-gitea-repos', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': getCsrfToken()
},
body: JSON.stringify({ url, token })
});
const data = await response.json();
if (response.ok) {
repoSelect.innerHTML = '<option value="">Select a repository</option>';
data.repositories.forEach(repo => {
const option = document.createElement('option');
option.value = repo.full_name;
option.textContent = repo.full_name;
if (repo.full_name === currentRepo) { // Restore selection
option.selected = true;
}
repoSelect.appendChild(option);
});
} else {
throw new Error(data.message || 'Failed to load repositories');
}
} catch (error) {
showError(error.message);
}
}
// Load GitLab Repositories
async function loadGitlabRepos() {
const url = document.getElementById('gitlabUrl').value;
const token = document.getElementById('gitlabToken').value;
const repoSelect = document.getElementById('gitlabRepo');
const currentRepo = repoSelect.value; // Store current selection
if (!url || !token) {
showError('Please fill in the server URL and access token');
return;
}
try {
const response = await fetch('/api/admin/list-gitlab-repos', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': getCsrfToken()
},
body: JSON.stringify({ url, token })
});
const data = await response.json();
if (response.ok) {
repoSelect.innerHTML = '<option value="">Select a repository</option>';
data.repositories.forEach(repo => {
const option = document.createElement('option');
option.value = repo.path_with_namespace;
option.textContent = repo.path_with_namespace;
if (repo.path_with_namespace === currentRepo) { // Restore selection
option.selected = true;
}
repoSelect.appendChild(option);
});
} else {
throw new Error(data.message || 'Failed to load repositories');
}
} catch (error) {
showError(error.message);
}
}
// Load repositories on page load if settings exist
document.addEventListener('DOMContentLoaded', function() {
const gitSettings = JSON.parse('{{ git_settings|tojson|safe }}');
if (gitSettings) {
if (gitSettings.provider === 'gitea' && gitSettings.url && gitSettings.token) {
loadGiteaRepos();
} else if (gitSettings.provider === 'gitlab' && gitSettings.url && gitSettings.token) {
loadGitlabRepos();
}
}
});
// Test Git Connection
async function testGitConnection(provider) {
const saveModal = new bootstrap.Modal(document.getElementById('saveConnectionModal'));
const messageElement = document.getElementById('saveConnectionMessage');
messageElement.textContent = 'Testing connection...';
messageElement.className = '';
saveModal.show();
try {
let data = {};
if (provider === 'gitea') {
const url = document.getElementById('giteaUrl').value;
const username = document.getElementById('giteaUsername').value;
const token = document.getElementById('giteaToken').value;
if (!url || !username || !token) {
throw new Error('Please fill in all required fields');
}
data = {
provider: 'gitea',
url: url,
username: username,
token: token
};
} else if (provider === 'gitlab') {
const url = document.getElementById('gitlabUrl').value;
const username = document.getElementById('gitlabUsername').value;
const token = document.getElementById('gitlabToken').value;
if (!url || !username || !token) {
throw new Error('Please fill in all required fields');
}
data = {
provider: 'gitlab',
url: url,
username: username,
token: token
};
}
const response = await fetch('/settings/test-git-connection', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': getCsrfToken()
},
body: JSON.stringify(data)
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Connection test failed');
}
messageElement.textContent = 'Connection test successful!';
messageElement.className = 'text-success';
} catch (error) {
messageElement.textContent = error.message || 'Connection test failed';
messageElement.className = 'text-danger';
}
}
// Test Portainer Connection
async function testPortainerConnection() {
const saveModal = new bootstrap.Modal(document.getElementById('saveConnectionModal'));
const messageElement = document.getElementById('saveConnectionMessage');
messageElement.textContent = 'Testing connection...';
messageElement.className = '';
saveModal.show();
try {
const url = document.getElementById('portainerUrl').value;
const apiKey = document.getElementById('portainerApiKey').value;
if (!url || !apiKey) {
throw new Error('Please fill in all required fields');
}
const response = await fetch('/api/admin/test-portainer-connection', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': getCsrfToken()
},
body: JSON.stringify({
url: url,
api_key: apiKey
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Connection test failed');
}
messageElement.textContent = 'Connection test successful!';
messageElement.className = 'text-success';
} catch (error) {
messageElement.textContent = error.message || 'Connection test failed';
messageElement.className = 'text-danger';
}
}
// Test NGINX Connection
async function testNginxConnection() {
const saveModal = new bootstrap.Modal(document.getElementById('saveConnectionModal'));
const messageElement = document.getElementById('saveConnectionMessage');
messageElement.textContent = 'Testing connection...';
messageElement.className = '';
saveModal.show();
try {
const url = document.getElementById('nginxUrl').value;
const username = document.getElementById('nginxUsername').value;
const password = document.getElementById('nginxPassword').value;
if (!url || !username || !password) {
throw new Error('Please fill in all required fields');
}
// First, get the token
const tokenResponse = await fetch(`${url}/api/tokens`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
identity: username,
secret: password
})
});
if (!tokenResponse.ok) {
throw new Error('Failed to authenticate with NGINX Proxy Manager');
}
const tokenData = await tokenResponse.json();
const token = tokenData.token;
// Now test the connection using the token
const response = await fetch(`${url}/api/nginx/proxy-hosts`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'Accept': 'application/json'
}
});
if (!response.ok) {
throw new Error('Failed to connect to NGINX Proxy Manager');
}
messageElement.textContent = 'Connection test successful!';
messageElement.className = 'text-success';
} catch (error) {
messageElement.textContent = error.message || 'Connection test failed';
messageElement.className = 'text-danger';
}
}
// Save Portainer Connection
async function savePortainerConnection(event) {
event.preventDefault();
const saveModal = new bootstrap.Modal(document.getElementById('saveConnectionModal'));
const messageElement = document.getElementById('saveConnectionMessage');
messageElement.textContent = '';
messageElement.className = '';
try {
const url = document.getElementById('portainerUrl').value;
const apiKey = document.getElementById('portainerApiKey').value;
if (!url || !apiKey) {
throw new Error('Please fill in all required fields');
}
const response = await fetch('/settings/save-portainer-connection', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': getCsrfToken()
},
body: JSON.stringify({
url: url,
api_key: apiKey
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Failed to save settings');
}
messageElement.textContent = 'Settings saved successfully!';
messageElement.className = 'text-success';
} catch (error) {
messageElement.textContent = error.message || 'Failed to save settings';
messageElement.className = 'text-danger';
}
saveModal.show();
}
// Save NGINX Connection
async function saveNginxConnection(event) {
event.preventDefault();
const saveModal = new bootstrap.Modal(document.getElementById('saveConnectionModal'));
const messageElement = document.getElementById('saveConnectionMessage');
messageElement.textContent = '';
messageElement.className = '';
try {
const url = document.getElementById('nginxUrl').value;
const username = document.getElementById('nginxUsername').value;
const password = document.getElementById('nginxPassword').value;
if (!url || !username || !password) {
throw new Error('Please fill in all required fields');
}
const response = await fetch('/settings/save-nginx-connection', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': getCsrfToken()
},
body: JSON.stringify({
url: url,
username: username,
password: password
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Failed to save settings');
}
messageElement.textContent = 'Settings saved successfully!';
messageElement.className = 'text-success';
} catch (error) {
messageElement.textContent = error.message || 'Failed to save settings';
messageElement.className = 'text-danger';
}
saveModal.show();
}
// Save Git Connection
async function saveGitConnection(event, provider) {
event.preventDefault();
const saveModal = new bootstrap.Modal(document.getElementById('saveConnectionModal'));
const messageElement = document.getElementById('saveConnectionMessage');
messageElement.textContent = '';
messageElement.className = '';
try {
let data = {};
if (provider === 'gitea') {
const url = document.getElementById('giteaUrl').value;
const username = document.getElementById('giteaUsername').value;
const token = document.getElementById('giteaToken').value;
const repo = document.getElementById('giteaRepo').value;
const password = document.getElementById('giteaPassword').value;
const otp = document.getElementById('giteaOtp').value;
if (!url || !username || !token || !repo) {
throw new Error('Please fill in all required fields');
}
data = {
provider: 'gitea',
url: url,
username: username,
token: token,
repo: repo,
password: password,
otp: otp
};
} else if (provider === 'gitlab') {
const url = document.getElementById('gitlabUrl').value;
const username = document.getElementById('gitlabUsername').value;
const token = document.getElementById('gitlabToken').value;
const repo = document.getElementById('gitlabRepo').value;
if (!url || !username || !token || !repo) {
throw new Error('Please fill in all required fields');
}
data = {
provider: 'gitlab',
url: url,
username: username,
token: token,
repo: repo
};
}
const response = await fetch('/settings/save-git-connection', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': getCsrfToken()
},
body: JSON.stringify(data)
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Failed to save settings');
}
messageElement.textContent = 'Settings saved successfully!';
messageElement.className = 'text-success';
} catch (error) {
messageElement.textContent = error.message || 'Failed to save settings';
messageElement.className = 'text-danger';
}
saveModal.show();
}
</script>
{% endmacro %}