6 Commits

Author SHA1 Message Date
jamie 24ab43cceb Merge pull request 'feat: add support for SQLite alongside MySQL' (#5) from v1.2.0 into master
Reviewed-on: #5
2026-01-08 17:32:39 +00:00
jamie 898ad38303 feat: add support for SQLite alongside MySQL
Release / release (pull_request) Successful in 18s
2026-01-08 17:32:10 +00:00
jamie 16ef30de83 Merge pull request 'v1.1.3' (#4) from v1.1.3 into master
Reviewed-on: #4
2026-01-08 17:14:49 +00:00
jamie a45397d003 chore: remove Kubernetes deployment steps from release workflow
Release / release (pull_request) Successful in 24s
2026-01-08 17:14:28 +00:00
jamie eec6fd4b3d chore: remove release-please configuration and related files; update Dockerfile and README for new image registry 2026-01-08 17:13:40 +00:00
jamie 7c3c2cc41e build: 🚀 redeploy 2025-12-20 11:00:22 +00:00
15 changed files with 274 additions and 291 deletions
+46
View File
@@ -0,0 +1,46 @@
name: Release
on:
pull_request:
branches:
- master
types: [closed]
jobs:
release:
if: github.event.pull_request.merged == true && startsWith(github.head_ref, 'v')
runs-on: build-htz-01
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Extract Version
id: get_version
run: echo "VERSION=${{ github.head_ref }}" >> $GITHUB_OUTPUT
- name: Generate Changelog
id: changelog
uses: https://github.com/metcalfc/changelog-generator@v4.6.2
with:
myToken: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Docker image
run: |
VERSION=${{ steps.get_version.outputs.VERSION }}
docker build -t cr.jdbnet.co.uk/public/echolog:$VERSION \
-t cr.jdbnet.co.uk/public/echolog:latest \
--build-arg VERSION=$VERSION \
.
docker push cr.jdbnet.co.uk/public/echolog:$VERSION
docker push cr.jdbnet.co.uk/public/echolog:latest
- name: Create Gitea Release
uses: https://gitea.com/actions/gitea-release-action@v1
with:
tag_name: ${{ steps.get_version.outputs.VERSION }}
name: ${{ steps.get_version.outputs.VERSION }}
body: ${{ steps.changelog.outputs.changelog }}
draft: false
prerelease: false
-72
View File
@@ -1,72 +0,0 @@
name: Release Please
on:
push:
branches:
- master
permissions:
contents: write
pull-requests: write
packages: write
jobs:
release-please:
runs-on: ubuntu-latest
outputs:
release_created: ${{ steps.release.outputs.release_created }}
steps:
- uses: googleapis/release-please-action@v4
id: release
with:
manifest-file: .release-please-manifest.json
config-file: .release-please-config.json
- name: Checkout
if: ${{ steps.release.outputs.release_created }}
uses: actions/checkout@v4
- name: Set up Docker Buildx
if: ${{ steps.release.outputs.release_created }}
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
if: ${{ steps.release.outputs.release_created }}
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Read version
if: ${{ steps.release.outputs.release_created }}
id: version
run: |
VERSION=$(cat VERSION)
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "VERSION=$VERSION" >> $GITHUB_ENV
- name: Build and push Docker image
if: ${{ steps.release.outputs.release_created }}
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: |
ghcr.io/jdb-net/echolog:${{ env.VERSION }}
ghcr.io/jdb-net/echolog:latest
build-args: |
VERSION=${{ env.VERSION }}
deploy:
name: Deploy to Kubernetes
needs: release-please
if: ${{ needs.release-please.outputs.release_created }}
runs-on: [ k3s-lan-01 ]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Apply manifests
run: |
sudo kubectl replace -f deployment.yml --grace-period=60 --force
+2 -1
View File
@@ -1,4 +1,5 @@
tailwindcss
static/output.css
.env
__pycache__
__pycache__
echolog.db
-10
View File
@@ -1,10 +0,0 @@
{
"packages": {
".": {
"release-type": "simple",
"version-file": "VERSION"
}
},
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json"
}
-3
View File
@@ -1,3 +0,0 @@
{
".": "1.1.2"
}
-33
View File
@@ -1,33 +0,0 @@
# Changelog
## [1.1.2](https://github.com/JDB-NET/echolog/compare/v1.1.1...v1.1.2) (2025-12-14)
### Bug Fixes
* :bug: docker image in readme ([049271b](https://github.com/JDB-NET/echolog/commit/049271baeeaba9266aeac4603dd9f9588fec4bfb))
## [1.1.1](https://github.com/JDB-NET/echolog/compare/v1.1.0...v1.1.1) (2025-11-02)
### Bug Fixes
* :bug: image name ([7334fb0](https://github.com/JDB-NET/echolog/commit/7334fb0ec6d077ffa1166f1b1d93194635f9454e))
## [1.1.0](https://github.com/JDB-NET/echolog/compare/v1.0.0...v1.1.0) (2025-11-02)
### Features
* :sparkles: add release please ([d2c4e08](https://github.com/JDB-NET/echolog/commit/d2c4e08e397c9e7ab9c30b2c04b04804fb81ad16))
* Added favicon and PWA ([1df4920](https://github.com/JDB-NET/echolog/commit/1df49204065529b07f4f6ba6ae8dfd9f74ba74d8))
* Added public build ([3187cbb](https://github.com/JDB-NET/echolog/commit/3187cbbcc50e57b6f459f7d5c116a49ad5b15f65))
* Added timezone environment variable ([7fb273d](https://github.com/JDB-NET/echolog/commit/7fb273d0abefbeeeaad7b81c244f48501b692f6a))
* Initial Commit ([853e78b](https://github.com/JDB-NET/echolog/commit/853e78b45f7c5d38d67d85468e35122250b1080c))
* streak counter to count how many days a user has posted consecutively ([200bc97](https://github.com/JDB-NET/echolog/commit/200bc973b8f410e9bba3ef2ace668a02ce33ce74))
### Bug Fixes
* environment variable names ([22481e5](https://github.com/JDB-NET/echolog/commit/22481e5af28581784ace8b76f666c5d5ece07c3b))
* Fixed search ([4c6328c](https://github.com/JDB-NET/echolog/commit/4c6328c2e90a368db07de5a2babf8fd3dac1808b))
+3
View File
@@ -1,6 +1,9 @@
FROM python:3.13-slim
LABEL org.opencontainers.image.vendor="JDB-NET"
WORKDIR /app
COPY . /app
ARG VERSION=unknown
ENV VERSION=${VERSION}
RUN pip install -r requirements.txt \
&& apt-get update \
&& apt-get install curl -y \
+29 -113
View File
@@ -11,65 +11,45 @@ A Flask-based web application for personal homelab journaling. Track your daily
- **Daily Journal Entries**: Create and manage journal entries for any date
- **Streak Tracking**: Automatically calculates consecutive days with entries
- **Search Functionality**: Search by keyword or filter by specific date
- **PWA Support**: Progressive Web App with offline capabilities and installable on mobile devices
- **PWA Support**: Progressive Web App making this installable on mobile devices
- **Modern UI**: Beautiful gradient design with Tailwind CSS and responsive layout
- **Optional Authentication**: Enable login protection with environment variables
- **MySQL Backend**: Robust data persistence with MySQL/MariaDB
- **Multiple Database Support**: Use MySQL/MariaDB or SQLite for data persistence
- **Docker Ready**: Easy deployment with Docker and Docker Compose
- **Kubernetes Support**: Includes Kubernetes deployment manifest
## Quick Start with Docker
### Docker Run
```bash
docker run -d \
--name echolog \
-p 5000:5000 \
-e MYSQL_HOST=10.10.2.27 \
-e MYSQL_USER=echolog \
-e MYSQL_PASSWORD=your_password \
-e MYSQL_DATABASE=echolog \
-e SECRET_KEY=your_secret_key \
-e TZ=Europe/London \
-e LOGIN_ENABLED=true \
-e LOGIN_USERNAME=admin \
-e LOGIN_PASSWORD=your_password \
ghcr.io/jdb-net/echolog:latest
```
### Docker Compose
```yaml
version: '3.8'
services:
echolog:
image: ghcr.io/jdb-net/echolog:latest
container_name: echolog
restart: unless-stopped
ports:
- "5000:5000"
environment:
- MYSQL_HOST=10.10.2.27
- MYSQL_USER=echolog
- MYSQL_PASSWORD=your_password
- MYSQL_DATABASE=echolog
- SECRET_KEY=your_secret_key
- TZ=Europe/London
- LOGIN_ENABLED=true
- LOGIN_USERNAME=admin
- LOGIN_PASSWORD=your_password
```
For deployment examples, see the [examples folder](examples/).
## Configuration
### Database Selection
EchoLog supports both MySQL/MariaDB and SQLite:
- SQLite (default for quick start)
- MySQL/MariaDB
See the [examples folder](examples/) for docker-compose files for both database types.
### Environment Variables
#### Database Configuration
- `DB_TYPE`: Database type - `mysql` (default) or `sqlite`
**MySQL/MariaDB options:**
- `MYSQL_HOST`: MySQL/MariaDB host (default: localhost)
- `MYSQL_USER`: Database user (default: root)
- `MYSQL_PASSWORD`: Database password (default: empty)
- `MYSQL_DATABASE`: Database name (default: echolog)
**SQLite options:**
- `SQLITE_DB`: Path to SQLite database file (default: echolog.db)
#### Application Configuration
- `SECRET_KEY`: Flask secret key for sessions (**REQUIRED in production!**)
- `TZ`: Timezone for date handling (default: UTC)
- `LOGIN_ENABLED`: Enable login protection (default: false)
@@ -78,6 +58,8 @@ services:
### Database Setup
#### MySQL/MariaDB
The application automatically initializes the database schema on first run. Ensure the database and user exist with appropriate permissions:
```sql
@@ -87,6 +69,10 @@ GRANT ALL PRIVILEGES ON echolog.* TO 'echolog'@'%';
FLUSH PRIVILEGES;
```
#### SQLite
SQLite requires no setup - the database file is created automatically on first run. Ensure the directory where the database file is stored is writable by the application.
## Usage
### Creating Entries
@@ -112,76 +98,6 @@ FLUSH PRIVILEGES;
The streak counter automatically tracks consecutive days with journal entries. The streak includes today or yesterday to start, and continues as long as you have entries on consecutive days.
## Kubernetes Deployment
The project includes a Kubernetes deployment manifest. See `deployment.yml` for details.
**Example Kubernetes deployment:**
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: echolog
namespace: echolog
spec:
replicas: 1
template:
spec:
containers:
- name: echolog
image: ghcr.io/jdb-net/echolog:latest
ports:
- containerPort: 5000
env:
- name: SECRET_KEY
valueFrom:
secretKeyRef:
name: echolog-secrets
key: secret-key
- name: MYSQL_HOST
value: "mysql-service"
- name: MYSQL_USER
value: "echolog"
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: echolog-secrets
key: mysql-password
- name: MYSQL_DATABASE
value: "echolog"
- name: TZ
value: "Europe/London"
- name: LOGIN_ENABLED
value: "true"
- name: LOGIN_USERNAME
valueFrom:
secretKeyRef:
name: echolog-secrets
key: username
- name: LOGIN_PASSWORD
valueFrom:
secretKeyRef:
name: echolog-secrets
key: password
```
## Progressive Web App (PWA)
EchoLog can be installed as a Progressive Web App on supported devices:
- **Mobile**: Add to home screen from browser menu
- **Desktop**: Install from browser address bar
- **Offline**: Works offline with cached content
## Security Notes
- **ENABLE LOGIN** in production by setting `LOGIN_ENABLED=true`
- **CHANGE THE DEFAULT CREDENTIALS** if using authentication
- **CHANGE THE SECRET_KEY** in production - use a strong random string (e.g., `openssl rand -hex 32`)
- Use strong passwords for database access
- Ensure database connections are secured (consider SSL/TLS for MySQL connections)
## Troubleshooting
### Database Connection Issues
-1
View File
@@ -1 +0,0 @@
1.1.2
+118 -53
View File
@@ -1,50 +1,75 @@
import os
import pytz
import logging
import sqlite3
from flask import Flask, render_template, request, redirect, url_for, session, jsonify
from datetime import datetime, date as datedate, datetime as dt
import mysql.connector
from dotenv import load_dotenv
# Read version from VERSION file
try:
with open('VERSION', 'r') as f:
__version__ = f.read().strip()
except FileNotFoundError:
__version__ = 'unknown'
load_dotenv()
__version__ = os.getenv('VERSION', 'unknown')
app = Flask(__name__)
load_dotenv()
TIMEZONE = os.getenv('TZ', 'UTC')
tz = pytz.timezone(TIMEZONE)
app.config['SECRET_KEY'] = os.getenv('SECRET_KEY', 'defaultsecret')
app.config['LOGIN_ENABLED'] = os.getenv('LOGIN_ENABLED', 'false').lower() == 'true'
mysql_config = {
'user': os.getenv('MYSQL_USER', 'root'),
'password': os.getenv('MYSQL_PASSWORD', ''),
'host': os.getenv('MYSQL_HOST', 'localhost'),
'database': os.getenv('MYSQL_DATABASE', 'echolog')
}
# Database configuration
DB_TYPE = os.getenv('DB_TYPE', 'mysql').lower()
if DB_TYPE == 'mysql':
import mysql.connector
mysql_config = {
'user': os.getenv('MYSQL_USER', 'root'),
'password': os.getenv('MYSQL_PASSWORD', ''),
'host': os.getenv('MYSQL_HOST', 'localhost'),
'database': os.getenv('MYSQL_DATABASE', 'echolog')
}
elif DB_TYPE == 'sqlite':
SQLITE_DB = os.getenv('SQLITE_DB', 'echolog.db')
else:
raise ValueError(f"Unsupported DB_TYPE: {DB_TYPE}. Use 'sqlite' or 'mysql'")
logging.basicConfig(level=logging.INFO, format='[%(asctime)s] %(levelname)s in %(module)s: %(message)s')
def get_db_connection():
return mysql.connector.connect(**mysql_config)
if DB_TYPE == 'mysql':
return mysql.connector.connect(**mysql_config)
elif DB_TYPE == 'sqlite':
conn = sqlite3.connect(SQLITE_DB)
conn.row_factory = sqlite3.Row
return conn
def dict_from_row(row):
"""Convert database row to dictionary"""
if DB_TYPE == 'mysql':
return row
elif DB_TYPE == 'sqlite':
return dict(row) if row else None
def init_db():
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS journal_entry (
id INT AUTO_INCREMENT PRIMARY KEY,
date DATE NOT NULL,
content TEXT NOT NULL
);
""")
if DB_TYPE == 'mysql':
cursor.execute("""
CREATE TABLE IF NOT EXISTS journal_entry (
id INT AUTO_INCREMENT PRIMARY KEY,
date DATE NOT NULL,
content TEXT NOT NULL
);
""")
elif DB_TYPE == 'sqlite':
cursor.execute("""
CREATE TABLE IF NOT EXISTS journal_entry (
id INTEGER PRIMARY KEY AUTOINCREMENT,
date DATE NOT NULL,
content TEXT NOT NULL
);
""")
conn.commit()
cursor.close()
conn.close()
@@ -53,7 +78,10 @@ def calculate_streak():
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("SELECT date FROM journal_entry ORDER BY date DESC")
dates = [row[0] for row in cursor.fetchall()]
if DB_TYPE == 'mysql':
dates = [row[0] for row in cursor.fetchall()]
elif DB_TYPE == 'sqlite':
dates = [row[0] for row in cursor.fetchall()]
cursor.close()
conn.close()
if not dates:
@@ -89,14 +117,25 @@ def index():
per_page = 7
offset = (page - 1) * per_page
conn = get_db_connection()
cursor = conn.cursor(dictionary=True)
cursor.execute("SELECT SQL_CALC_FOUND_ROWS * FROM journal_entry ORDER BY date DESC LIMIT %s OFFSET %s", (per_page, offset))
entries = cursor.fetchall()
cursor.execute("SELECT FOUND_ROWS() as total")
total = cursor.fetchone()['total']
cursor = conn.cursor()
if DB_TYPE == 'mysql':
cursor.execute("SELECT SQL_CALC_FOUND_ROWS * FROM journal_entry ORDER BY date DESC LIMIT %s OFFSET %s", (per_page, offset))
entries = cursor.fetchall()
cursor.execute("SELECT FOUND_ROWS() as total")
total = cursor.fetchone()['total']
elif DB_TYPE == 'sqlite':
cursor.execute("SELECT COUNT(*) as total FROM journal_entry")
total = cursor.fetchone()[0]
cursor.execute("SELECT * FROM journal_entry ORDER BY date DESC LIMIT ? OFFSET ?", (per_page, offset))
entries = [dict_from_row(row) for row in cursor.fetchall()]
today = datetime.now(tz).date().isoformat()
cursor.execute("SELECT * FROM journal_entry WHERE date = %s", (today,))
cursor.execute("SELECT * FROM journal_entry WHERE date = ?" if DB_TYPE == 'sqlite' else "SELECT * FROM journal_entry WHERE date = %s", (today,))
todays_entry = cursor.fetchone()
if DB_TYPE == 'sqlite' and todays_entry:
todays_entry = dict_from_row(todays_entry)
cursor.close()
conn.close()
has_prev = page > 1
@@ -112,12 +151,13 @@ def add_entry():
if content.strip():
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("SELECT id FROM journal_entry WHERE date = %s", (date,))
param_marker = "?" if DB_TYPE == 'sqlite' else "%s"
cursor.execute(f"SELECT id FROM journal_entry WHERE date = {param_marker}", (date,))
existing = cursor.fetchone()
if existing:
cursor.execute("UPDATE journal_entry SET content = %s WHERE date = %s", (content, date))
cursor.execute(f"UPDATE journal_entry SET content = {param_marker} WHERE date = {param_marker}", (content, date))
else:
cursor.execute("INSERT INTO journal_entry (date, content) VALUES (%s, %s)", (date, content))
cursor.execute(f"INSERT INTO journal_entry (date, content) VALUES ({param_marker}, {param_marker})", (date, content))
conn.commit()
cursor.close()
conn.close()
@@ -129,30 +169,49 @@ def entry_for_date():
if not date:
return jsonify({'content': ''})
conn = get_db_connection()
cursor = conn.cursor(dictionary=True)
cursor.execute("SELECT content FROM journal_entry WHERE date = %s", (date,))
cursor = conn.cursor()
param_marker = "?" if DB_TYPE == 'sqlite' else "%s"
cursor.execute(f"SELECT content FROM journal_entry WHERE date = {param_marker}", (date,))
entry = cursor.fetchone()
cursor.close()
conn.close()
return jsonify({'content': entry['content'] if entry else ''})
if DB_TYPE == 'sqlite':
content = entry[0] if entry else ''
else:
content = entry['content'] if entry else ''
return jsonify({'content': content})
@app.route('/search', methods=['GET'])
def search():
query = request.args.get('query', '')
date = request.args.get('date', None)
conn = get_db_connection()
cursor = conn.cursor(dictionary=True)
sql = "SELECT * FROM journal_entry WHERE 1=1"
params = []
if query:
sql += " AND content LIKE %s"
params.append(f"%{query}%")
if date:
sql += " AND date = %s"
params.append(date)
sql += " ORDER BY date DESC"
cursor.execute(sql, params)
entries = cursor.fetchall()
cursor = conn.cursor()
if DB_TYPE == 'mysql':
sql = "SELECT * FROM journal_entry WHERE 1=1"
params = []
if query:
sql += " AND content LIKE %s"
params.append(f"%{query}%")
if date:
sql += " AND date = %s"
params.append(date)
sql += " ORDER BY date DESC"
cursor.execute(sql, params)
entries = cursor.fetchall()
elif DB_TYPE == 'sqlite':
sql = "SELECT * FROM journal_entry WHERE 1=1"
params = []
if query:
sql += " AND content LIKE ?"
params.append(f"%{query}%")
if date:
sql += " AND date = ?"
params.append(date)
sql += " ORDER BY date DESC"
cursor.execute(sql, params)
entries = [dict_from_row(row) for row in cursor.fetchall()]
cursor.close()
conn.close()
today = datetime.now(tz).date().isoformat()
@@ -191,22 +250,26 @@ def require_login():
@app.route('/edit/<int:entry_id>', methods=['GET', 'POST'])
def edit_entry(entry_id):
conn = get_db_connection()
cursor = conn.cursor(dictionary=True)
cursor = conn.cursor()
param_marker = "?" if DB_TYPE == 'sqlite' else "%s"
if request.method == 'POST':
date = request.form.get('date')
content = request.form.get('content')
cursor.execute("UPDATE journal_entry SET date=%s, content=%s WHERE id=%s", (date, content, entry_id))
cursor.execute(f"UPDATE journal_entry SET date={param_marker}, content={param_marker} WHERE id={param_marker}", (date, content, entry_id))
conn.commit()
cursor.close()
conn.close()
return redirect(url_for('index'))
else:
cursor.execute("SELECT * FROM journal_entry WHERE id=%s", (entry_id,))
cursor.execute(f"SELECT * FROM journal_entry WHERE id={param_marker}", (entry_id,))
entry = cursor.fetchone()
cursor.close()
conn.close()
if not entry:
return redirect(url_for('index'))
if DB_TYPE == 'sqlite':
entry = dict_from_row(entry)
return render_template('edit.html', entry=entry)
@app.route('/edit_modal', methods=['POST'])
@@ -217,7 +280,8 @@ def edit_entry_modal():
if entry_id and date and content:
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("UPDATE journal_entry SET date=%s, content=%s WHERE id=%s", (date, content, entry_id))
param_marker = "?" if DB_TYPE == 'sqlite' else "%s"
cursor.execute(f"UPDATE journal_entry SET date={param_marker}, content={param_marker} WHERE id={param_marker}", (date, content, entry_id))
conn.commit()
cursor.close()
conn.close()
@@ -227,7 +291,8 @@ def edit_entry_modal():
def delete_entry(entry_id):
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("DELETE FROM journal_entry WHERE id=%s", (entry_id,))
param_marker = "?" if DB_TYPE == 'sqlite' else "%s"
cursor.execute(f"DELETE FROM journal_entry WHERE id={param_marker}", (entry_id,))
conn.commit()
cursor.close()
conn.close()
+2 -2
View File
@@ -15,14 +15,14 @@ spec:
spec:
containers:
- name: echolog
image: ghcr.io/jdb-net/echolog:latest
image: cr.jdbnet.co.uk/public/echolog:latest
imagePullPolicy: Always
ports:
- containerPort: 5000
name: "echolog"
env:
- name: MYSQL_HOST
value: "10.10.2.27"
value: "10.10.25.4"
- name: MYSQL_USER
value: "echolog"
- name: MYSQL_PASSWORD
+44
View File
@@ -0,0 +1,44 @@
version: '3.8'
services:
mysql:
image: mariadb:latest
container_name: echolog-mysql
restart: unless-stopped
environment:
- MYSQL_ROOT_PASSWORD=root_password
- MYSQL_DATABASE=echolog
- MYSQL_USER=echolog
- MYSQL_PASSWORD=echolog_password
volumes:
- mysql-data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
timeout: 20s
retries: 10
echolog:
image: cr.jdbnet.co.uk/public/echolog:latest
container_name: echolog
restart: unless-stopped
ports:
- "5000:5000"
depends_on:
mysql:
condition: service_healthy
environment:
- DB_TYPE=mysql
- MYSQL_HOST=mysql
- MYSQL_USER=echolog
- MYSQL_PASSWORD=echolog_password
- MYSQL_DATABASE=echolog
- SECRET_KEY=change-me-in-production
- TZ=Europe/London
- LOGIN_ENABLED=false
# Uncomment below to enable login protection
# - LOGIN_ENABLED=true
# - LOGIN_USERNAME=admin
# - LOGIN_PASSWORD=change-me
volumes:
mysql-data:
+24
View File
@@ -0,0 +1,24 @@
version: '3.8'
services:
echolog:
image: cr.jdbnet.co.uk/public/echolog:latest
container_name: echolog
restart: unless-stopped
ports:
- "5000:5000"
volumes:
- echolog-data:/data
environment:
- DB_TYPE=sqlite
- SQLITE_DB=/data/echolog.db
- SECRET_KEY=change-me-in-production
- TZ=Europe/London
- LOGIN_ENABLED=false
# Uncomment below to enable login protection
# - LOGIN_ENABLED=true
# - LOGIN_USERNAME=admin
# - LOGIN_PASSWORD=change-me
volumes:
echolog-data:
+5 -2
View File
@@ -1,5 +1,8 @@
Flask
mysql-connector-python
python-dotenv
pytz
gunicorn
gunicorn
# Optional database drivers:
# For MySQL/MariaDB support, uncomment the line below:
# mysql-connector-python
# SQLite is built-in with Python
+1 -1
View File
@@ -213,7 +213,7 @@
<footer class="mt-8 py-6 border-t border-gray-800 text-center text-gray-500 text-sm">
<span class="mr-1"><a href="https://www.jdbnet.co.uk" target="_blank" rel="noopener" class="text-gray-500 hover:text-pink-400 hover:underline mx-1">JDB-NET</a> &copy; {{ now.year }}</span>
&middot;
<span class="mx-1"><a href="https://github.com/JDB-NET/echolog" target="_blank" rel="noopener" class="text-gray-500 hover:text-pink-400 hover:underline mx-1">v{{ version }}</a></span>
<span class="mx-1"><a href="https://git.jdbnet.co.uk/jamie/echolog" target="_blank" rel="noopener" class="text-gray-500 hover:text-pink-400 hover:underline mx-1">{{ version }}</a></span>
</footer>
<script>