chore: add docker infrastructure, gists feature, attachment system, and admin CLI tools

- Add .dockerignore, Dockerfile, and docker compose targets to Makefile for containerized deployment
- Implement gists router with CRUD operations, database schema, and polymorphic comment/vote reuse
- Create attachment upload system with thumbnail generation, MIME detection, and storage path management
- Add attachments_prune CLI command to clean orphaned attachment records and files
- Introduce rate limiting middleware with 60 requests per minute window
- Add custom 404 and 500 error handlers with SEO-optimized template responses
- Extend database initialization with gists and attachments indexes plus upload site settings defaults
- Update load_comments to include attachment mapping for comment resources
- Register gists and uploads routers in main application and update AGENTS.md documentation
This commit is contained in:
2026-05-12 13:07:34 +00:00
parent ff2585b098
commit aa88f18c03
69 changed files with 3069 additions and 450 deletions
+20 -15
View File
@@ -30,6 +30,8 @@
<link rel="stylesheet" href="/static/css/base.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/styles/atom-one-dark.min.css">
<link rel="stylesheet" href="/static/css/markdown.css">
<link rel="stylesheet" href="/static/css/mention.css">
<link rel="stylesheet" href="/static/css/attachments.css">
{% block extra_head %}{% endblock %}
{% if page_schema %}
@@ -43,14 +45,15 @@
<div class="topnav-inner">
<a href="/feed" class="topnav-logo">Dev<span>Place</span></a>
<div class="topnav-links">
<a href="/" class="topnav-link {% if request.url.path == '/' %}active{% endif %}">Home</a>
<a href="/feed" class="topnav-link {% if request.url.path == '/feed' %}active{% endif %}">Posts</a>
<a href="/news" class="topnav-link {% if 'news' in request.url.path %}active{% endif %}">News</a>
<a href="/projects" class="topnav-link {% if 'projects' in request.url.path %}active{% endif %}">Projects</a>
<a href="/" class="topnav-link {% if request.url.path == '/' %}active{% endif %}"><span class="icon">🏠</span> Home</a>
<a href="/feed" class="topnav-link {% if request.url.path == '/feed' %}active{% endif %}"><span class="icon">📝</span> Posts</a>
<a href="/news" class="topnav-link {% if 'news' in request.url.path %}active{% endif %}"><span class="icon">📰</span> News</a>
<a href="/gists" class="topnav-link {% if 'gists' in request.url.path %}active{% endif %}"><span class="icon">📄</span> Gists</a>
<a href="/projects" class="topnav-link {% if 'projects' in request.url.path %}active{% endif %}"><span class="icon">🚀</span> Projects</a>
{% if user %}
<a href="/messages" class="topnav-link {% if 'messages' in request.url.path %}active{% endif %}">Messages</a>
<a href="/messages" class="topnav-link {% if 'messages' in request.url.path %}active{% endif %}"><span class="icon">✉️</span> Messages</a>
{% if user.get('role') == 'Admin' %}
<a href="/admin" class="topnav-link {% if 'admin' in request.url.path %}active{% endif %}">Admin</a>
<a href="/admin" class="topnav-link {% if 'admin' in request.url.path %}active{% endif %}"><span class="icon">⚙️</span> Admin</a>
{% endif %}
{% endif %}
</div>
@@ -72,12 +75,12 @@
</div>
</a>
<div class="dropdown-menu">
<a href="/auth/logout" class="dropdown-item">Logout</a>
<a href="/auth/logout" class="dropdown-item"><span class="icon">🚪</span>Logout</a>
</div>
</div>
{% else %}
<a href="/auth/login" class="topnav-link">Login</a>
<a href="/auth/signup" class="btn btn-primary btn-sm">Sign Up</a>
<a href="/auth/login" class="topnav-link"><span class="icon">🔑</span>Login</a>
<a href="/auth/signup" class="btn btn-primary btn-sm"><span class="icon"></span>Sign Up</a>
{% endif %}
</div>
</div>
@@ -108,17 +111,19 @@
<div class="footer-inner">
<span class="footer-copy">&copy; DevPlace</span>
<nav class="footer-links">
<a href="/bugs">Bug Report</a>
<a href="/bugs"><span class="icon">🐛</span>Bug Report</a>
</nav>
</div>
</footer>
{% endblock %}
<script src="https://cdn.jsdelivr.net/npm/marked/lib/marked.umd.js"></script>
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/highlight.min.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/emoji-picker-element@^1/index.js"></script>
<script src="/static/js/ContentRenderer.js"></script>
<script src="/static/js/EmojiPicker.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/marked/lib/marked.umd.js"></script>
<script defer src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/highlight.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/emoji-picker-element@^1/index.js"></script>
<script type="module" src="/static/js/ContentRenderer.js"></script>
<script type="module" src="/static/js/AttachmentUploader.js"></script>
<script type="module" src="/static/js/EmojiPicker.js"></script>
<script type="module" src="/static/js/MentionInput.js"></script>
<script type="module" src="/static/js/Application.js"></script>
{% block extra_js %}{% endblock %}
</body>