init project

This commit is contained in:
Jonas Heinrich 2025-08-19 08:30:34 +02:00
parent 3b4176fa0a
commit b66a9d0d2c
12 changed files with 721 additions and 492 deletions

138
templates/base.html Normal file
View file

@ -0,0 +1,138 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Fragify{% endblock %}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/select2-bootstrap-5-theme@1.3.0/dist/select2-bootstrap-5-theme.min.css" rel="stylesheet">
<style>
body {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.main-container {
background: white;
border-radius: 20px;
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
padding: 3rem;
margin: 2rem auto;
max-width: 800px;
}
.title {
color: #2c3e50;
font-weight: 700;
margin-bottom: 1rem;
}
.description {
color: #7f8c8d;
font-size: 1.1rem;
margin-bottom: 2.5rem;
line-height: 1.6;
}
.form-control, .form-select {
border-radius: 10px;
border: 2px solid #e9ecef;
padding: 0.75rem 1rem;
transition: all 0.3s ease;
}
.form-control:focus, .form-select:focus {
border-color: #667eea;
box-shadow: 0 0 0 0.2rem rgba(102, 126, 234, 0.25);
}
.btn-primary {
background: linear-gradient(45deg, #667eea, #764ba2);
border: none;
border-radius: 15px;
padding: 1rem 2rem;
font-size: 1.1rem;
font-weight: 600;
transition: all 0.3s ease;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);
}
.result-link {
background: #f8f9fa;
border: 2px solid #e9ecef;
border-radius: 10px;
padding: 1rem;
margin-top: 1rem;
word-break: break-all;
}
.loading {
display: none;
}
.footer {
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(10px);
}
.footer-links a {
font-size: 0.9rem;
transition: color 0.3s ease;
}
.footer-links a:hover {
color: #667eea !important;
}
.footer-text {
font-size: 0.8rem;
}
.footer-text a:hover {
color: #667eea !important;
}
.legal-content {
text-align: left;
line-height: 1.6;
}
.legal-content h2 {
color: #2c3e50;
margin-top: 2rem;
margin-bottom: 1rem;
}
.legal-content p {
margin-bottom: 1rem;
}
.legal-content ul {
margin-bottom: 1rem;
}
</style>
{% block extra_css %}{% endblock %}
</head>
<body>
<div class="container">
<div class="main-container">
{% block content %}{% endblock %}
</div>
<!-- Footer -->
<footer class="footer mt-5 pt-4 border-top">
<div class="container">
<div class="row">
<div class="col-12 text-center">
<div class="footer-links mb-2">
<a href="/impressum" class="text-muted text-decoration-none me-3">Impressum</a>
<a href="/datenschutz" class="text-muted text-decoration-none me-3">Datenschutz</a>
<a href="https://git.project-insanity.org/onny/fragify" class="text-muted text-decoration-none" target="_blank">Source</a>
</div>
<div class="footer-text">
<small class="text-muted">
Projekt von <a href="https://project-insanity.org" class="text-muted text-decoration-none" target="_blank">Project-Insanity.org</a>,
follow us on <a href="https://social.project-insanity.org/@pi_crew" class="text-muted text-decoration-none" target="_blank">Mastodon</a> :)
</small>
</div>
</div>
</div>
</div>
</footer>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
{% block extra_js %}{% endblock %}
</body>
</html>

100
templates/datenschutz.html Normal file
View file

@ -0,0 +1,100 @@
{% extends "base.html" %}
{% block title %}Datenschutz - Fragify{% endblock %}
{% block content %}
<div class="text-center">
<h1 class="title display-4">Datenschutzerklärung</h1>
</div>
<div class="legal-content">
<h2>1. Datenschutz auf einen Blick</h2>
<h3>Allgemeine Hinweise</h3>
<p>
Die folgenden Hinweise geben einen einfachen Überblick darüber, was mit Ihren personenbezogenen Daten passiert, wenn Sie diese Website besuchen. Personenbezogene Daten sind alle Daten, mit denen Sie persönlich identifiziert werden können.
</p>
<h2>2. Datenerfassung auf dieser Website</h2>
<h3>Wer ist verantwortlich für die Datenerfassung auf dieser Website?</h3>
<p>
Die Datenverarbeitung auf dieser Website erfolgt durch den Websitebetreiber. Dessen Kontaktdaten können Sie dem Abschnitt „Hinweis zur Verantwortlichen Stelle" in dieser Datenschutzerklärung entnehmen.
</p>
<h3>Wie erfassen wir Ihre Daten?</h3>
<p>
Ihre Daten werden zum einen dadurch erhoben, dass Sie uns diese mitteilen. Hierbei kann es sich z. B. um Daten handeln, die Sie in ein Kontaktformular eingeben.
</p>
<p>
Andere Daten werden automatisch oder nach Ihrer Einwilligung beim Besuch der Website durch unsere IT-Systeme erfasst. Das sind vor allem technische Daten (z. B. Internetbrowser, Betriebssystem oder Uhrzeit des Seitenaufrufs).
</p>
<h3>Wofür nutzen wir Ihre Daten?</h3>
<p>
Ein Teil der Daten wird erhoben, um eine fehlerfreie Bereitstellung der Website zu gewährleisten. Andere Daten können zur Analyse Ihres Nutzerverhaltens verwendet werden.
</p>
<h3>Welche Rechte haben Sie bezüglich Ihrer Daten?</h3>
<p>
Sie haben jederzeit das Recht, unentgeltlich Auskunft über Herkunft, Empfänger und Zweck Ihrer gespeicherten personenbezogenen Daten zu erhalten. Sie haben außerdem ein Recht, die Berichtigung oder Löschung dieser Daten zu verlangen. Wenn Sie eine Einwilligung zur Datenverarbeitung erteilt haben, können Sie diese Einwilligung jederzeit für die Zukunft widerrufen. Außerdem haben Sie das Recht, unter bestimmten Umständen die Einschränkung der Verarbeitung Ihrer personenbezogenen Daten zu verlangen.
</p>
<h2>3. Hosting</h2>
<p>
Wir hosten unsere Website bei uns selbst. Es werden keine Daten an externe Hosting-Dienste weitergegeben.
</p>
<h2>4. Allgemeine Hinweise und Pflichtinformationen</h2>
<h3>Datenschutz</h3>
<p>
Die Betreiber dieser Seiten nehmen den Schutz Ihrer persönlichen Daten sehr ernst. Wir behandeln Ihre personenbezogenen Daten vertraulich und entsprechend den gesetzlichen Datenschutzvorschriften sowie dieser Datenschutzerklärung.
</p>
<h2>5. Datenerfassung auf dieser Website</h2>
<h3>Cookies</h3>
<p>
<strong>Diese Website verwendet keine Cookies.</strong> Es werden keine Tracking-Cookies oder Analyse-Cookies gesetzt. Die Website funktioniert vollständig ohne Cookie-Speicherung.
</p>
<h3>Server-Log-Dateien</h3>
<p>
Der Provider der Seiten erhebt und speichert automatisch Informationen in so genannten Server-Log-Dateien, die Ihr Browser automatisch an uns übermittelt. Dies sind:
</p>
<ul>
<li>Browsertyp und Browserversion</li>
<li>verwendetes Betriebssystem</li>
<li>Referrer URL</li>
<li>Hostname des zugreifenden Rechners</li>
<li>Uhrzeit der Serveranfrage</li>
<li>IP-Adresse</li>
</ul>
<p>
Eine Zusammenführung dieser Daten mit anderen Datenquellen wird nicht vorgenommen.
</p>
<h2>6. API-Nutzung</h2>
<p>
Diese Website nutzt die öffentliche API von FragDenStaat.de, um Behördeninformationen abzurufen. Bei der Nutzung der Suchfunktion werden Ihre Suchanfragen an die FragDenStaat.de API weitergeleitet. Es werden keine persönlichen Daten an FragDenStaat.de übertragen, außer den reinen Suchbegriffen.
</p>
<p>
<strong>Wichtig:</strong> Wir speichern keine Ihrer Suchanfragen oder generierten Links auf unseren Servern. Alle Daten werden nur temporär im Browser verarbeitet und nicht dauerhaft gespeichert.
</p>
<h2>7. Kontakt</h2>
<p>
Bei Fragen zur Erhebung, Verarbeitung oder Nutzung Ihrer personenbezogenen Daten wenden Sie sich bitte an:
</p>
<p>
Jonas Heinrich<br>
E-Mail: <a href="mailto:onny@project-insanity.org">onny@project-insanity.org</a>
</p>
<h2>8. Änderungen</h2>
<p>
Wir behalten uns vor, diese Datenschutzerklärung anzupassen, damit sie stets den aktuellen rechtlichen Anforderungen entspricht oder um Änderungen unserer Leistungen in der Datenschutzerklärung umzusetzen, z. B. bei der Einführung neuer Services.
</p>
</div>
{% endblock %}

47
templates/impressum.html Normal file
View file

@ -0,0 +1,47 @@
{% extends "base.html" %}
{% block title %}Impressum - Fragify{% endblock %}
{% block content %}
<div class="text-center">
<h1 class="title display-4">Impressum</h1>
</div>
<div class="legal-content">
<h2>Angaben gemäß § 5 TMG</h2>
<p>
<strong>Jonas Heinrich</strong><br>
Erzbergerstraße 9<br>
76133 Karlsruhe<br>
Deutschland
</p>
<h2>Kontakt</h2>
<p>
E-Mail: <a href="mailto:onny@project-insanity.org">onny@project-insanity.org</a>
</p>
<h2>Verantwortlich für den Inhalt nach § 55 Abs. 2 RStV</h2>
<p>
Jonas Heinrich<br>
Erzbergerstraße 9<br>
76133 Karlsruhe
</p>
<h2>Haftungsausschluss</h2>
<h3>Haftung für Inhalte</h3>
<p>
Die Inhalte unserer Seiten wurden mit größter Sorgfalt erstellt. Für die Richtigkeit, Vollständigkeit und Aktualität der Inhalte können wir jedoch keine Gewähr übernehmen.
</p>
<h3>Haftung für Links</h3>
<p>
Unser Angebot enthält Links zu externen Webseiten Dritter, auf deren Inhalte wir keinen Einfluss haben. Deshalb können wir für diese fremden Inhalte auch keine Gewähr übernehmen. Für die Inhalte der verlinkten Seiten ist stets der jeweilige Anbieter oder Betreiber der Seiten verantwortlich.
</p>
<h3>Urheberrecht</h3>
<p>
Die durch die Seitenbetreiber erstellten Inhalte und Werke auf diesen Seiten unterliegen dem deutschen Urheberrecht. Die Vervielfältigung, Bearbeitung, Verbreitung und jede Art der Verwertung außerhalb der Grenzen des Urheberrechtes bedürfen der schriftlichen Zustimmung des jeweiligen Autors bzw. Erstellers.
</p>
</div>
{% endblock %}

138
templates/index.html Normal file
View file

@ -0,0 +1,138 @@
{% extends "base.html" %}
{% block title %}Fragify{% endblock %}
{% block content %}
<div class="text-center">
<h1 class="title display-4">Fragify</h1>
<p class="description">
Erstelle einfach Links für Anfragen bei dem Portal
<a href="https://fragdenstaat.de" target="_blank" class="text-decoration-none">FragDenStaat.de</a>,
welche du vorausfüllen und an Freund:innen schicken kannst!
</p>
<form id="fragifyForm" class="text-start">
<div class="mb-4">
<label for="publicbody" class="form-label fw-bold">Behörde</label>
<select class="form-select" id="publicbody" name="publicbody_id" required>
<option value="">Behörde auswählen...</option>
</select>
</div>
<div class="mb-4">
<label for="subject" class="form-label fw-bold">Betreff</label>
<input type="text" class="form-control" id="subject" name="subject"
placeholder="Betreff der Anfrage" required>
</div>
<div class="mb-4">
<label for="body" class="form-label fw-bold">Dokumente anfragen:</label>
<textarea class="form-control" id="body" name="body" rows="5"
placeholder="Beschreibe hier, welche Dokumente oder Informationen du anfragen möchtest..." required></textarea>
</div>
<div class="text-center">
<button type="submit" class="btn btn-primary btn-lg">
<span class="btn-text">Anfrage Link generieren</span>
<span class="loading spinner-border spinner-border-sm ms-2" role="status"></span>
</button>
</div>
</form>
<div id="result" class="mt-4" style="display: none;">
<h5 class="text-success mb-3">Link erfolgreich generiert!</h5>
<div class="result-link">
<a href="" id="generatedLink" target="_blank"></a>
</div>
<button class="btn btn-outline-primary mt-3" onclick="copyToClipboard()">
Link kopieren
</button>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
$(document).ready(function() {
// Initialize Select2 for public bodies
$('#publicbody').select2({
theme: 'bootstrap-5',
placeholder: 'Behörde auswählen...',
allowClear: true,
ajax: {
url: '/api/publicbodies',
dataType: 'json',
delay: 250,
data: function(params) {
return {
search: params.term,
page: params.page || 1
};
},
processResults: function(data, params) {
params.page = params.page || 1;
return {
results: data.results.map(function(item) {
return {
id: item.id,
text: item.name + ' (' + item.jurisdiction + ')'
};
}),
pagination: {
more: data.next !== null
}
};
},
cache: true
}
});
// Handle form submission
$('#fragifyForm').on('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
const submitBtn = $('button[type="submit"]');
const btnText = submitBtn.find('.btn-text');
const loading = submitBtn.find('.loading');
// Show loading state
btnText.hide();
loading.show();
submitBtn.prop('disabled', true);
fetch('/', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
$('#generatedLink').attr('href', data.link).text(data.link);
$('#result').show();
$('#fragifyForm')[0].reset();
$('#publicbody').val(null).trigger('change');
} else {
alert('Fehler: ' + data.error);
}
})
.catch(error => {
alert('Fehler beim Generieren des Links: ' + error);
})
.finally(() => {
// Hide loading state
btnText.show();
loading.hide();
submitBtn.prop('disabled', false);
});
});
});
function copyToClipboard() {
const link = document.getElementById('generatedLink').href;
navigator.clipboard.writeText(link).then(function() {
alert('Link wurde in die Zwischenablage kopiert!');
});
}
</script>
{% endblock %}