Checkpoint, configuration can now be done through DB and the site. Server settings are still on the file system

This commit is contained in:
2024-11-05 19:03:04 -05:00
parent b46ec98115
commit 9abdd18f33
8 changed files with 770 additions and 289 deletions

View File

@ -115,6 +115,42 @@
.delete-btn:hover {
background-color: #ff1a1a;
}
.config-section {
margin: 2rem 0;
padding: 1rem;
background: #fff;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.config-editor fieldset {
margin-bottom: 1.5rem;
border: 1px solid #ddd;
padding: 1rem;
border-radius: 4px;
}
.config-editor legend {
font-weight: bold;
padding: 0 0.5rem;
}
.form-group {
margin-bottom: 1rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
}
.form-group input,
.form-group textarea {
width: 100%;
padding: 0.5rem;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1rem;
}
.form-group textarea {
font-family: monospace;
}
</style>
</head>
<body>
@ -173,8 +209,55 @@
{% endfor %}
</tbody>
</table>
<div class="config-section">
<h2>Configuration</h2>
<div class="config-editor">
<form id="configForm">
<!-- Appearance -->
<fieldset>
<legend>Appearance</legend>
<div class="form-group">
<label for="appearance.accent_color">Accent Color:</label>
<input style="min-height: 2rem;" type="color" id="appearance.accent_color" name="appearance.accent_color" value="{{ config.appearance.accent_color }}">
</div>
<div class="form-group">
<label for="appearance.site_title">Site Title:</label>
<input type="text" id="appearance.site_title" name="appearance.site_title" value="{{ config.appearance.site_title }}">
</div>
</fieldset>
<!-- About Section -->
<fieldset>
<legend>About</legend>
<div class="form-group">
<label for="about.name">Name:</label>
<input type="text" id="about.name" name="about.name" value="{{ config.about.name }}">
</div>
<div class="form-group">
<label for="about.location">Location:</label>
<input type="text" id="about.location" name="about.location" value="{{ config.about.location }}">
</div>
<div class="form-group">
<label for="about.profile_image">Profile Image Path:</label>
<input type="text" id="about.profile_image" name="about.profile_image" value="{{ config.about.profile_image }}">
</div>
<div class="form-group">
<label for="about.bio">Bio (Markdown):</label>
<textarea id="about.bio" name="about.bio" rows="4">{{ config.about.bio }}</textarea>
</div>
</fieldset>
<button type="submit" class="btn btn-primary">Save Configuration</button>
</form>
</div>
<div class="config-history-link" style="margin-top: 1rem;">
<a href="{{ url_for('config_history') }}">View Configuration History</a>
</div>
</div>
</div>
<script>
<script nonce="{{ g.csp_nonce }}">
function makeEditable(element) {
const value = element.textContent;
const input = document.createElement('input');
@ -250,6 +333,38 @@
});
}
}
document.getElementById('configForm').addEventListener('submit', async (e) => {
e.preventDefault();
const formData = {};
const inputs = e.target.querySelectorAll('input, textarea');
inputs.forEach(input => {
formData[input.name] = input.value;
});
try {
const response = await fetch('/admin/update_config', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(formData)
});
const result = await response.json();
if (result.success) {
alert('Configuration saved successfully');
location.reload();
} else {
alert('Error saving configuration: ' + result.error);
}
} catch (error) {
alert('Error saving configuration: ' + error);
}
});
</script>
</body>
</html>

View File

@ -0,0 +1,56 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Site Configuration History</title>
<style>
.config-entry {
background: white;
padding: 1rem;
margin-bottom: 1rem;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.config-date {
color: #666;
font-size: 0.9rem;
}
pre {
background: #f5f5f5;
padding: 1rem;
border-radius: 4px;
overflow-x: auto;
}
</style>
</head>
<body>
<div class="container">
<div class="logout">
<a href="{{ url_for('admin') }}">Back to Admin</a> |
<a href="{{ url_for('admin_logout') }}">Logout</a>
</div>
<h1>Site Configuration History</h1>
{% for config in site_configs %}
<div class="config-entry">
<div class="config-date">
Updated: {{ config.updated_at.strftime('%Y-%m-%d %H:%M:%S UTC') }}
</div>
<pre>{
"appearance": {
"site_title": "{{ config.site_title }}",
"accent_color": "{{ config.accent_color }}"
},
"about": {
"name": "{{ config.author_name }}",
"location": "{{ config.author_location }}",
"profile_image": "{{ config.profile_image }}",
"bio": {{ config.bio | tojson }}
}
}</pre>
</div>
{% endfor %}
</div>
</body>
</html>

View File

@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tanishq Dubey Photography</title>
<title>{{ site_title }}</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+Mono:wght@100..900&display=swap" rel="stylesheet">
@ -172,31 +172,119 @@
display: block;
}
}
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.5);
backdrop-filter: blur(5px);
}
.modal-content {
background-color: #f0f0f0;
margin: 10% auto;
padding: 20px;
width: 80%;
max-width: 600px;
border-radius: 8px;
position: relative;
display: flex;
flex-direction: column;
gap: 1rem;
}
.modal-header {
display: flex;
align-items: center;
gap: 1rem;
}
.profile-image {
width: 100px;
height: 100px;
border-radius: 50%;
object-fit: cover;
}
.profile-info {
flex-grow: 1;
}
.profile-name {
font-size: 1.5rem;
margin: 0;
}
.profile-location {
color: #666;
margin: 0;
}
.modal-bio {
line-height: 1.6;
}
.close {
position: absolute;
right: 20px;
top: 20px;
font-size: 1.5rem;
cursor: pointer;
color: #666;
}
.close:hover {
color: #000;
}
</style>
</head>
<body>
<div class="sidebar">
<div class="sidebar-nav noto-sans-mono-font">
<div class="nav-toggle"></div>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><hr></li>
<li>Powered by <a href="https://dws.rip">DWS</a></li>
</ul>
</nav>
<header>
<div class="sidebar">
<div class="sidebar-nav noto-sans-mono-font">
<div class="nav-toggle"></div>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="#" id="aboutLink">About</a></li>
<li><hr></li>
<li>Powered by <a href="https://dws.rip">DWS</a></li>
</ul>
</nav>
</div>
<div class="sidebar-title noto-sans-mono-font">
<h1>{{ site_title }}</h1>
</div>
</div>
<div class="sidebar-title noto-sans-mono-font">
<h1>Tanishq Dubey Photography</h1>
</div>
</div>
</header>
<div class="main-content">
<div class="grid-container" id="polaroid-grid"></div>
<div id="loading">Loading more images...</div>
</div>
<script>
<div id="aboutModal" class="modal">
<div class="modal-content">
<span class="close">&times;</span>
<div class="modal-header">
<img src="{{ about.profile_image }}" alt="{{ about.name }}" class="profile-image">
<div class="profile-info">
<h2 class="profile-name">{{ about.name }}</h2>
<p class="profile-location">{{ about.location }}</p>
</div>
</div>
<div class="modal-bio" id="bio-content"></div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js" nonce="{{ nonce }}"></script>
<script nonce="{{ nonce }}">
const gridContainer = document.getElementById('polaroid-grid');
const loadingIndicator = document.getElementById('loading');
const baseSize = 110; // Size of one grid cell in pixels
@ -345,6 +433,32 @@
}
setupNavToggle();
// Modal functionality
const modal = document.getElementById('aboutModal');
const aboutLink = document.getElementById('aboutLink');
const closeBtn = document.querySelector('.close');
const bioContent = document.getElementById('bio-content');
// Render markdown content
bioContent.innerHTML = marked.parse(`{{ about.bio | safe }}`);
aboutLink.onclick = function() {
modal.style.display = 'block';
document.body.style.overflow = 'hidden';
}
closeBtn.onclick = function() {
modal.style.display = 'none';
document.body.style.overflow = 'auto';
}
window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = 'none';
document.body.style.overflow = 'auto';
}
}
</script>
</body>
</html>