Internal
Public Access
Compare commits
1
Commits
2026.9.5
...
2026.9.5.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f41939983a |
@@ -1182,10 +1182,9 @@ Current version: 1.0 (no version prefix in URLs)
|
||||
|
||||
For API support or questions:
|
||||
- GitHub Issues: Create an issue in the repository
|
||||
- Email: keith@firebugit.com
|
||||
- Email: keithsmith@darksingularity.org
|
||||
|
||||
---
|
||||
|
||||
**Built by Firebug IT**
|
||||
**Documentation Version: 1.0**
|
||||
**Last Updated: 2025-01-10**
|
||||
**Last Updated: 2026-09-05**
|
||||
|
||||
@@ -39,7 +39,7 @@ A powerful Django-based task management system with time tracking, tag organizat
|
||||
|
||||
### API & Sync
|
||||
- **RESTful API**: Full REST API for programmatic access
|
||||
- **Mobile App Support**: Sync protocol for Android app
|
||||
- **Offline-First Sync Protocol**: Bidirectional sync with conflict resolution, powering the offline PWA
|
||||
- **Real-time Updates**: Task changes sync across devices
|
||||
- **Conflict Resolution**: Handles offline changes and syncing
|
||||
|
||||
@@ -72,7 +72,7 @@ A powerful Django-based task management system with time tracking, tag organizat
|
||||
|
||||
1. **Clone the repository**
|
||||
```bash
|
||||
git clone https://git.firebugit.com/Firebug_IT/KeepItGoingServer.git
|
||||
git clone https://git.darksingularity.org/DarkSingularity/KeepItGoingServer.git
|
||||
cd KeepItGoingServer
|
||||
```
|
||||
|
||||
@@ -423,9 +423,9 @@ Key environment variables (see `.env.example`):
|
||||
#### Core Settings
|
||||
- `SECRET_KEY` - Django secret key (required in production, generate with `python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"`)
|
||||
- `DEBUG` - Enable debug mode (True/False, must be False in production)
|
||||
- `ALLOWED_HOSTS` - Comma-separated list of allowed hosts (e.g., `tasks.firebugit.com,localhost`)
|
||||
- `CSRF_TRUSTED_ORIGINS` - HTTPS origins for CSRF (e.g., `https://tasks.firebugit.com`)
|
||||
- `SITE_DOMAIN` - Domain name for email links (e.g., `tasks.firebugit.com`)
|
||||
- `ALLOWED_HOSTS` - Comma-separated list of allowed hosts (e.g., `tasks.darksingularity.org,localhost`)
|
||||
- `CSRF_TRUSTED_ORIGINS` - HTTPS origins for CSRF (e.g., `https://tasks.darksingularity.org`)
|
||||
- `SITE_DOMAIN` - Domain name for email links (e.g., `tasks.darksingularity.org`)
|
||||
|
||||
#### Database
|
||||
- `DATABASE_URL` - Database connection string (optional, overrides settings)
|
||||
@@ -441,7 +441,7 @@ Key environment variables (see `.env.example`):
|
||||
- `EMAIL_USE_SSL` - Use SSL (True/False, use True for port 465)
|
||||
- `EMAIL_HOST_USER` - SMTP username/email
|
||||
- `EMAIL_HOST_PASSWORD` - SMTP password or API key
|
||||
- `DEFAULT_FROM_EMAIL` - From email address (e.g., `KeepItGoing <noreply@firebugit.com>`)
|
||||
- `DEFAULT_FROM_EMAIL` - From email address (e.g., `KeepItGoing <noreply@darksingularity.org>`)
|
||||
- `SERVER_EMAIL` - Server error email address
|
||||
|
||||
#### Email Verification Settings
|
||||
@@ -642,7 +642,7 @@ sudo useradd -m -s /bin/bash keepitgoing
|
||||
sudo su - keepitgoing
|
||||
|
||||
# Clone repository
|
||||
git clone https://git.firebugit.com/Firebug_IT/KeepItGoingServer.git
|
||||
git clone https://git.darksingularity.org/DarkSingularity/KeepItGoingServer.git
|
||||
cd KeepItGoingServer
|
||||
|
||||
# Create virtual environment
|
||||
@@ -665,9 +665,9 @@ cat > .env << 'EOF'
|
||||
# Django Settings
|
||||
SECRET_KEY=GENERATE_WITH_get_random_secret_key
|
||||
DEBUG=False
|
||||
ALLOWED_HOSTS=tasks.firebugit.com,localhost,127.0.0.1
|
||||
CSRF_TRUSTED_ORIGINS=https://tasks.firebugit.com
|
||||
SITE_DOMAIN=tasks.firebugit.com
|
||||
ALLOWED_HOSTS=tasks.darksingularity.org,localhost,127.0.0.1
|
||||
CSRF_TRUSTED_ORIGINS=https://tasks.darksingularity.org
|
||||
SITE_DOMAIN=tasks.darksingularity.org
|
||||
|
||||
# Database Configuration
|
||||
# Choose ONE of the following based on your database from Step 2:
|
||||
@@ -688,8 +688,8 @@ EMAIL_PORT=587
|
||||
EMAIL_USE_TLS=True
|
||||
EMAIL_HOST_USER=your-email@gmail.com
|
||||
EMAIL_HOST_PASSWORD=your-app-password
|
||||
DEFAULT_FROM_EMAIL=KeepItGoing <noreply@firebugit.com>
|
||||
SERVER_EMAIL=server@firebugit.com
|
||||
DEFAULT_FROM_EMAIL=KeepItGoing <noreply@darksingularity.org>
|
||||
SERVER_EMAIL=server@darksingularity.org
|
||||
|
||||
# Email Verification
|
||||
EMAIL_VERIFICATION_TOKEN_EXPIRY_HOURS=24
|
||||
@@ -919,7 +919,7 @@ upstream keepitgoing {
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name tasks.firebugit.com;
|
||||
server_name tasks.darksingularity.org;
|
||||
|
||||
# Redirect HTTP to HTTPS
|
||||
return 301 https://$server_name$request_uri;
|
||||
@@ -927,11 +927,11 @@ server {
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name tasks.firebugit.com;
|
||||
server_name tasks.darksingularity.org;
|
||||
|
||||
# SSL Configuration
|
||||
ssl_certificate /etc/letsencrypt/live/tasks.firebugit.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/tasks.firebugit.com/privkey.pem;
|
||||
ssl_certificate /etc/letsencrypt/live/tasks.darksingularity.org/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/tasks.darksingularity.org/privkey.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
ssl_prefer_server_ciphers on;
|
||||
@@ -985,7 +985,7 @@ sudo systemctl restart nginx
|
||||
|
||||
```bash
|
||||
# Obtain SSL certificate
|
||||
sudo certbot --nginx -d tasks.firebugit.com
|
||||
sudo certbot --nginx -d tasks.darksingularity.org
|
||||
|
||||
# Certbot will automatically configure nginx
|
||||
# Certificate auto-renews via cron
|
||||
@@ -1004,7 +1004,7 @@ sudo ufw status
|
||||
#### Step 10: Initial Admin User and Approval
|
||||
|
||||
```bash
|
||||
# Log in to the web interface at https://tasks.firebugit.com/admin
|
||||
# Log in to the web interface at https://tasks.darksingularity.org/admin
|
||||
# Use the superuser credentials created in Step 3
|
||||
|
||||
# If ALLOW_SELF_REGISTRATION=True and new users register:
|
||||
@@ -1115,7 +1115,7 @@ from django.core.mail import send_mail
|
||||
send_mail(
|
||||
'Test Subject',
|
||||
'Test message.',
|
||||
'noreply@firebugit.com',
|
||||
'noreply@darksingularity.org',
|
||||
['your-email@example.com'],
|
||||
)
|
||||
# Check for any errors
|
||||
@@ -1198,9 +1198,9 @@ sudo tail -f /var/log/nginx/access.log
|
||||
sudo journalctl -u keepitgoing -f
|
||||
```
|
||||
|
||||
## Mobile App Integration
|
||||
## Offline-First Sync
|
||||
|
||||
This server works with the KeepItGoing Android app via the sync endpoint.
|
||||
The sync endpoint (`/api/sync/`) powers the offline PWA's background sync. It was originally built for a native Android app, which has since been retired in favor of the PWA, but the protocol itself is unchanged.
|
||||
|
||||
**Sync Protocol:**
|
||||
- Client sends current state and last sync timestamp
|
||||
@@ -1219,16 +1219,63 @@ This server works with the KeepItGoing Android app via the sync endpoint.
|
||||
## License
|
||||
|
||||
Proprietary - All Rights Reserved
|
||||
Copyright (c) 2025 Firebug IT
|
||||
Copyright (c) 2026 Keith Smith
|
||||
|
||||
## Support
|
||||
|
||||
For issues and questions:
|
||||
- Create an issue in the repository
|
||||
- Email: keith@firebugit.com
|
||||
- Email: keithsmith@darksingularity.org
|
||||
|
||||
## Changelog
|
||||
|
||||
### 2026.9.5
|
||||
|
||||
**New Features:**
|
||||
- Installable Progressive Web App (PWA): app manifest, service worker, and offline fallback pages
|
||||
- Web Push notifications
|
||||
- Full offline task management: create, edit, complete tasks, manage subtasks and tags, and track time while offline, with automatic sync once back online
|
||||
- Per-task reminder notifications - "before due", "due now", and "overdue" - delivered by email and/or push, timed per task and configurable via the profile's Default Reminder setting
|
||||
|
||||
**Bug Fixes:**
|
||||
- Overdue status now accounts for due time, not just due date
|
||||
- Background sync now runs on every page load instead of being throttled, so the offline cache can no longer serve stale data (e.g. a tag deleted online reappearing offline)
|
||||
- Offline mode now matches the online dashboard: correct subtask nesting, tag sidebar, overdue styling, and full task editing/tags/time tracking
|
||||
- Fixed a crash when editing a task's due date/time
|
||||
- Service worker precache now reliably picks up updated files after a deploy instead of a stale cached copy
|
||||
- "Overdue" and "due now" notifications no longer arrive at the same instant - overdue now waits an hour
|
||||
- Changing the reminder-timing setting now retroactively updates reminders already scheduled on existing tasks
|
||||
|
||||
### 2026.8.31.1
|
||||
|
||||
- Custom recurrence patterns: recurring tasks can repeat on specific days of the week, an interval of weeks (e.g. every other Wednesday), a specific day of the month, or the Nth weekday of the month (e.g. every second Tuesday)
|
||||
- New "Custom" recurrence builder in the task create/edit UI generates the underlying RRULE pattern automatically
|
||||
|
||||
### 2026.8.31
|
||||
|
||||
**New Features:**
|
||||
- Notification system simplified to a single daily task-due email, sent within each user's local morning window
|
||||
- Self-registration can now be enabled/disabled via configuration (disabled by default)
|
||||
|
||||
**Bug Fixes:**
|
||||
- Task sort now defaults to due date instead of raw/unsorted ordering
|
||||
- Priority sort now orders correctly (high to low / low to high)
|
||||
- Recurring tasks no longer create duplicate instances on completion
|
||||
- Daily email timing now correctly accounts for all user timezones
|
||||
- Overdue calculation now uses the user's local timezone
|
||||
- Fixed Celery worker/beat health check issues
|
||||
|
||||
**Infrastructure:**
|
||||
- Docker Compose now builds from the git repository
|
||||
- Replaced pytz with zoneinfo for timezone handling
|
||||
|
||||
### v1.2.0 - Recurring Tasks
|
||||
|
||||
- Fully functional recurring tasks (daily, weekly, biweekly, monthly, yearly)
|
||||
- Automatic creation of next task instance on completion
|
||||
- Recurrence end date support
|
||||
- Hourly background job to ensure no missed recurrences
|
||||
|
||||
### Version 1.1.0 (2025-01-22)
|
||||
- **Email Verification System**: Users must verify email before logging in
|
||||
- **Admin Approval Workflow**: Admins approve new users via Django admin
|
||||
@@ -1249,7 +1296,3 @@ For issues and questions:
|
||||
- Web interface with dark mode
|
||||
- Responsive design
|
||||
- RESTful API
|
||||
|
||||
---
|
||||
|
||||
Built with ❤️ by Firebug IT
|
||||
|
||||
@@ -44,7 +44,7 @@ MIDDLEWARE = [
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'tasks.middleware.AllowMobileAppFramingMiddleware', # Allow mobile app iframe embedding
|
||||
'tasks.middleware.SecurityHeadersMiddleware',
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'config.urls'
|
||||
|
||||
@@ -20,10 +20,10 @@ if not SECRET_KEY:
|
||||
DEBUG = True
|
||||
|
||||
# Development-only hosts
|
||||
ALLOWED_HOSTS = ['localhost', '127.0.0.1', '[::1]', '10.0.2.2', '192.168.1.241', 'tasks.firebugit.com']
|
||||
ALLOWED_HOSTS = ['localhost', '127.0.0.1', '[::1]', '10.0.2.2', '192.168.1.241']
|
||||
|
||||
# CSRF - for development testing only
|
||||
CSRF_TRUSTED_ORIGINS = os.environ.get('CSRF_TRUSTED_ORIGINS', '').split(',') if os.environ.get('CSRF_TRUSTED_ORIGINS') else ['https://tasks.firebugit.com']
|
||||
CSRF_TRUSTED_ORIGINS = os.environ.get('CSRF_TRUSTED_ORIGINS', '').split(',') if os.environ.get('CSRF_TRUSTED_ORIGINS') else []
|
||||
|
||||
# Database - SQLite for development
|
||||
DATABASES = {
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
from .mobile_app import AllowMobileAppFramingMiddleware
|
||||
from .security_headers import SecurityHeadersMiddleware
|
||||
|
||||
__all__ = ['AllowMobileAppFramingMiddleware']
|
||||
__all__ = ['SecurityHeadersMiddleware']
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
"""
|
||||
Middleware to allow iframe embedding for the KeepItGoing mobile app.
|
||||
|
||||
The mobile app uses Capacitor WebView which embeds the website in an iframe.
|
||||
This middleware detects requests from the mobile app and removes both
|
||||
X-Frame-Options and Content-Security-Policy frame-ancestors headers to allow
|
||||
iframe embedding, while keeping clickjacking protection for regular web browsers.
|
||||
"""
|
||||
|
||||
|
||||
class AllowMobileAppFramingMiddleware:
|
||||
"""
|
||||
Remove frame-blocking headers for requests from KeepItGoing mobile app.
|
||||
|
||||
The mobile app uses a Capacitor WebView. We detect these requests via
|
||||
User-Agent and remove X-Frame-Options and CSP frame-ancestors headers.
|
||||
"""
|
||||
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
response = self.get_response(request)
|
||||
|
||||
# Check if request is from KeepItGoing mobile app
|
||||
user_agent = request.META.get('HTTP_USER_AGENT', '')
|
||||
|
||||
# Detect Capacitor/Android WebView patterns
|
||||
is_mobile_app = (
|
||||
'wv' in user_agent.lower() or # Android WebView
|
||||
'CapacitorHttp' in user_agent or
|
||||
'com.firebugit.keepitgoing' in user_agent or
|
||||
('KeepItGoing' in user_agent and 'Mobile' in user_agent)
|
||||
)
|
||||
|
||||
if is_mobile_app:
|
||||
# Mobile app: Allow iframe embedding - don't add frame-blocking headers
|
||||
# Remove any existing frame headers
|
||||
if 'X-Frame-Options' in response:
|
||||
del response['X-Frame-Options']
|
||||
if 'Content-Security-Policy' in response:
|
||||
del response['Content-Security-Policy']
|
||||
else:
|
||||
# Regular browsers: Add security headers for clickjacking protection
|
||||
if 'X-Frame-Options' not in response:
|
||||
response['X-Frame-Options'] = 'DENY'
|
||||
|
||||
if 'Content-Security-Policy' not in response:
|
||||
response['Content-Security-Policy'] = (
|
||||
"default-src 'self'; "
|
||||
"script-src 'self' 'unsafe-inline'; "
|
||||
"style-src 'self' 'unsafe-inline'; "
|
||||
"img-src 'self' data: https:; "
|
||||
"font-src 'self' data:; "
|
||||
"connect-src 'self'; "
|
||||
"frame-ancestors 'none'; "
|
||||
"base-uri 'self'; "
|
||||
"form-action 'self';"
|
||||
)
|
||||
|
||||
return response
|
||||
@@ -0,0 +1,35 @@
|
||||
"""
|
||||
Middleware that applies clickjacking protection headers to every response.
|
||||
|
||||
Previously also allowed iframe embedding for a native mobile app's WebView
|
||||
(detected via User-Agent); that app has been retired in favor of the PWA,
|
||||
so the headers are now applied unconditionally.
|
||||
"""
|
||||
|
||||
|
||||
class SecurityHeadersMiddleware:
|
||||
"""Add X-Frame-Options and a CSP frame-ancestors policy to every response."""
|
||||
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
response = self.get_response(request)
|
||||
|
||||
if 'X-Frame-Options' not in response:
|
||||
response['X-Frame-Options'] = 'DENY'
|
||||
|
||||
if 'Content-Security-Policy' not in response:
|
||||
response['Content-Security-Policy'] = (
|
||||
"default-src 'self'; "
|
||||
"script-src 'self' 'unsafe-inline'; "
|
||||
"style-src 'self' 'unsafe-inline'; "
|
||||
"img-src 'self' data: https:; "
|
||||
"font-src 'self' data:; "
|
||||
"connect-src 'self'; "
|
||||
"frame-ancestors 'none'; "
|
||||
"base-uri 'self'; "
|
||||
"form-action 'self';"
|
||||
)
|
||||
|
||||
return response
|
||||
+1
-1
@@ -142,7 +142,7 @@
|
||||
{% block auth_content %}{% endblock %}
|
||||
</div>
|
||||
<footer class="footer">
|
||||
<p>© 2025 Firebug IT. All rights reserved.</p>
|
||||
<p>© 2026 Keith Smith. All rights reserved.</p>
|
||||
</footer>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user