Skip to content

Commit 9e8050c

Browse files
login
1 parent b88ec48 commit 9e8050c

File tree

4 files changed

+60
-30
lines changed

4 files changed

+60
-30
lines changed

Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,5 @@ VOLUME ["/app/data"]
3838
EXPOSE 5000
3939

4040
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
41-
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "2", "--threads", "4", "--access-logfile", "-", "--error-logfile", "-", "app.main:create_app()"]
41+
# Use --preload so that db.create_all runs once before forking workers (avoids SQLite lock/race)
42+
CMD ["gunicorn", "--preload", "--bind", "0.0.0.0:5000", "--workers", "2", "--threads", "4", "--access-logfile", "-", "--error-logfile", "-", "app.main:create_app()"]

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,10 @@ Access the application at `http://localhost:5000`
6363
| `SECRET_KEY` | Flask secret key for session encryption || \- | `your-secret-key-here` |
6464
| `USER_UID` | User ID for container process || `1000` | `1001` |
6565
| `USER_GID` | Group ID for container process || `1000` | `1001` |
66-
| `DATABASE_URL` | SQLAlchemy database URL || `sqlite:///users.db` | `postgresql://...` |
66+
| `DATABASE_URL` | SQLAlchemy database URL || `sqlite:////app/data/users.db` | `postgresql://...` |
67+
| `DATA_DIR` | Override data directory (SQLite, uploads) || `/app/data` | `/data` |
68+
| `SESSION_COOKIE_SECURE` | Force secure cookies (set true in HTTPS) || `false` | `true` |
69+
| `SESSION_LIFETIME_DAYS` | Session lifetime in days || `1` | `7` |
6770

6871
## 📖 Usage
6972

app/config.py

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,45 @@
11
import os
22
from datetime import timedelta
33

4+
# Resolve project paths
5+
BASE_APP_DIR = os.path.abspath(os.path.dirname(__file__)) # /app/app
6+
PROJECT_ROOT = os.path.abspath(os.path.join(BASE_APP_DIR, '..')) # /app
7+
8+
# Data directory (where the sqlite db will live by default)
9+
DEFAULT_DATA_DIR = os.path.join(PROJECT_ROOT, 'data')
10+
DATA_DIR = os.environ.get('DATA_DIR', DEFAULT_DATA_DIR)
11+
os.makedirs(DATA_DIR, exist_ok=True)
12+
13+
def _bool(env_name: str, default: bool) -> bool:
14+
val = os.environ.get(env_name)
15+
if val is None:
16+
return default
17+
return val.strip().lower() in ('1', 'true', 'yes', 'on')
18+
419
class Config:
520
# Core settings
621
SECRET_KEY = os.environ.get('SECRET_KEY') or 'dev-secret-key-change-this'
7-
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or 'sqlite:///users.db'
22+
23+
# Default SQLite DB under /app/data (volume mount) unless DATABASE_URL provided
24+
_default_sqlite_path = os.path.join(DATA_DIR, 'users.db')
25+
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or f'sqlite:///{_default_sqlite_path}'
826
SQLALCHEMY_TRACK_MODIFICATIONS = False
927

1028
# Session configuration
1129
SESSION_COOKIE_HTTPONLY = True
12-
SESSION_COOKIE_SAMESITE = 'Lax'
13-
SESSION_COOKIE_SECURE = True # Set to True if using HTTPS
14-
PERMANENT_SESSION_LIFETIME = timedelta(days=1) # 1 day session time
30+
SESSION_COOKIE_SAMESITE = os.environ.get('SESSION_COOKIE_SAMESITE', 'Lax')
31+
# Allow overriding secure flag for local HTTP testing (set SESSION_COOKIE_SECURE=false)
32+
SESSION_COOKIE_SECURE = _bool('SESSION_COOKIE_SECURE', default=False)
33+
PERMANENT_SESSION_LIFETIME = timedelta(days=int(os.environ.get('SESSION_LIFETIME_DAYS', '1')))
1534

1635
# JSON configuration
1736
JSON_AS_ASCII = False
1837
JSONIFY_PRETTYPRINT_REGULAR = True
1938

20-
# Optional settings
39+
# Optional settings / environment
2140
LOG_LEVEL = os.environ.get('LOG_LEVEL', 'INFO')
2241
FLASK_ENV = os.environ.get('FLASK_ENV', 'production')
2342

43+
# Expose data dir path for other modules if needed
44+
DATA_DIR = DATA_DIR
45+

app/main.py

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -271,30 +271,34 @@ def handle_exception(error):
271271

272272
# ===== Database Initialization =====
273273

274-
with app.app_context():
275-
# Create all tables
276-
db.create_all()
274+
# Ensure DB initialization only once (important with multi-worker if --preload not used)
275+
if not app.config.get('_DB_INITIALIZED', False):
276+
with app.app_context():
277+
print(f"Initializing database at URI: {app.config['SQLALCHEMY_DATABASE_URI']}")
278+
db.create_all()
279+
280+
# Create default admin user only if no administrators exist
281+
admin_count = User.query.filter_by(is_admin=True).count()
282+
if admin_count == 0:
283+
# No administrators exist, create default admin user
284+
admin_user = User(username='admin', is_admin=True)
285+
admin_user.set_password('changeme') # Default password
286+
db.session.add(admin_user)
287+
try:
288+
db.session.commit()
289+
print("=" * 50)
290+
print("Default admin user created!")
291+
print("Username: admin")
292+
print("Password: changeme")
293+
print("PLEASE CHANGE THIS PASSWORD IMMEDIATELY!")
294+
print("=" * 50)
295+
except Exception as e:
296+
print(f"Error creating admin user: {e}")
297+
db.session.rollback()
298+
else:
299+
print(f"Found {admin_count} administrator(s) - skipping default admin creation")
277300

278-
# Create default admin user only if no administrators exist
279-
admin_count = User.query.filter_by(is_admin=True).count()
280-
if admin_count == 0:
281-
# No administrators exist, create default admin user
282-
admin_user = User(username='admin', is_admin=True)
283-
admin_user.set_password('changeme') # Default password
284-
db.session.add(admin_user)
285-
try:
286-
db.session.commit()
287-
print("=" * 50)
288-
print("Default admin user created!")
289-
print("Username: admin")
290-
print("Password: changeme")
291-
print("PLEASE CHANGE THIS PASSWORD IMMEDIATELY!")
292-
print("=" * 50)
293-
except Exception as e:
294-
print(f"Error creating admin user: {e}")
295-
db.session.rollback()
296-
else:
297-
print(f"Found {admin_count} administrator(s) - skipping default admin creation")
301+
app.config['_DB_INITIALIZED'] = True
298302

299303
# ===== Additional App Configuration =====
300304

0 commit comments

Comments
 (0)