Skip to content

Commit 3716a4c

Browse files
committed
pivot
1 parent a4cc2d3 commit 3716a4c

2 files changed

Lines changed: 56 additions & 64 deletions

File tree

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,4 @@ EXPOSE 5000
3434
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s CMD curl -f http://127.0.0.1:5000/health || exit 1
3535

3636
ENTRYPOINT ["/app/docker-entrypoint.sh"]
37-
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "2", "--timeout", "120", "--access-logfile", "-", "--log-config", "none", "--chdir", "/app/app", "main:app"]
37+
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "2", "--timeout", "120", "--access-logfile", "-", "--chdir", "/app/app", "main:app"]

app/main.py

Lines changed: 55 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -5,63 +5,59 @@
55
from pathlib import Path
66

77
# ---------------------------------------------------------------------------
8-
# Logging — configure first so every subsequent import sees the right level
8+
# Logging — configure first
99
# ---------------------------------------------------------------------------
1010

11-
12-
class _UserIdFilter(logging.Filter):
13-
"""Injects ``user_id`` into every log record."""
14-
15-
def filter(self, record: logging.LogRecord) -> bool:
16-
try:
17-
from flask import g as flask_g
18-
from flask import has_request_context
19-
20-
record.user_id = flask_g.get("uid", 0) if has_request_context() else 0
21-
except Exception:
22-
record.user_id = "?"
23-
return True
24-
25-
26-
_handler = logging.StreamHandler()
27-
_handler.addFilter(_UserIdFilter())
28-
_handler.setFormatter(logging.Formatter("[%(asctime)s] %(levelname)s uid=%(user_id)s %(name)s: %(message)s"))
29-
logging.root.addHandler(_handler)
30-
logging.root.setLevel(logging.INFO)
11+
logging.basicConfig(
12+
level=logging.INFO,
13+
format="[%(asctime)s] %(levelname)s %(name)s: %(message)s",
14+
)
3115

3216
_access_log = logging.getLogger("tracekit.access")
3317

3418
# ---------------------------------------------------------------------------
35-
# Sentry — initialise after logging so its handler inherits the INFO level
19+
# Sentry — modern SDK 2.x setup (Flask + Gunicorn safe)
3620
# ---------------------------------------------------------------------------
3721

3822
if _sentry_dsn := os.environ.get("SENTRY_DSN"):
3923
import sentry_sdk
24+
from sentry_sdk.integrations.flask import FlaskIntegration
4025

4126
def _traces_sampler(sampling_context: dict) -> float:
42-
request = sampling_context.get("request") or {}
43-
url = request.get("url", "")
44-
45-
if url.endswith("/health"):
27+
"""
28+
Drop healthcheck traces.
29+
Sample everything else at 100%.
30+
"""
31+
tx_ctx = sampling_context.get("transaction_context") or {}
32+
name = tx_ctx.get("name")
33+
34+
if name == "api.health":
4635
return 0.0
4736

4837
return 1.0
4938

5039
sentry_sdk.init(
5140
dsn=_sentry_dsn,
52-
send_default_pii=True,
41+
environment=os.getenv("SENTRY_ENV", "production"),
42+
# Performance
5343
traces_sampler=_traces_sampler,
54-
enable_logs=True,
55-
profile_session_sample_rate=1.0,
5644
profile_lifecycle="trace",
45+
profile_session_sample_rate=1.0,
46+
# Logs (new 2.x logs product)
47+
enable_logs=True,
48+
# Flask integration
49+
integrations=[FlaskIntegration()],
50+
# Attach user + request info automatically
51+
send_default_pii=True,
5752
)
5853

5954
# ---------------------------------------------------------------------------
6055
# Re-exports kept for backward-compatibility with the test suite
6156
# ---------------------------------------------------------------------------
57+
6258
from calendar_data import get_sync_calendar_data # noqa: F401
6359
from db_init import (
64-
_init_db, # noqa: F401
60+
_init_db,
6561
load_tracekit_config,
6662
)
6763
from flask import Flask, abort, g, redirect, request, session, url_for
@@ -85,7 +81,7 @@ def _traces_sampler(sampling_context: dict) -> float:
8581
app.secret_key = os.environ["SESSION_KEY"]
8682

8783
# ---------------------------------------------------------------------------
88-
# Template context — inject current_user into every template
84+
# User context + DB initialization
8985
# ---------------------------------------------------------------------------
9086

9187

@@ -95,21 +91,11 @@ def _set_user_context():
9591

9692
from tracekit.user_context import set_user_id
9793

98-
# Initialise g.uid so the log filter always has a value for this request,
99-
# even if we return early or abort below.
100-
g.uid = 0
101-
10294
if is_single_user_mode():
10395
set_user_id(0)
10496
return
10597

106-
# Ensure the DB is initialised and connected before any route that may query
107-
# it — including unauthenticated routes like /login and /signup. This
108-
# matters for fresh Gunicorn worker processes where _db_initialized is still
109-
# False. If the DB is genuinely unavailable, abort with 503.
11098
try:
111-
from db_init import _init_db
112-
11399
from tracekit.db import get_db
114100

115101
_init_db()
@@ -119,9 +105,6 @@ def _set_user_context():
119105

120106
uid = session.get("user_id")
121107
if not uid:
122-
# Unauthenticated request — explicitly reset to 0. ContextVars are not
123-
# reset between requests in a single-threaded WSGI process, so without
124-
# this a previous authenticated request's user_id would bleed through.
125108
set_user_id(0)
126109
return
127110

@@ -131,35 +114,33 @@ def _set_user_context():
131114

132115
user = User.get_by_id(uid)
133116
set_user_id(user.id)
134-
g.uid = user.id
135-
# Cache on g so the context processor never needs to re-query the DB.
136-
# Tracekit.cleanup() closes the connection before templates render, so a
137-
# second DB round-trip in inject_current_user() would intermittently fail.
138117
g.current_user = user
139118
except Exception as exc:
140119
import peewee
141120

142121
if isinstance(exc, peewee.DoesNotExist):
143-
# The user row was deleted — log out cleanly.
144122
session.pop("user_id", None)
145123
else:
146-
# Any other DB error: abort rather than writing data under user_id=0.
147124
abort(503)
148125

149126

150127
@app.context_processor
151128
def inject_current_user():
152129
from auth_mode import is_single_user_mode
153130

154-
single_user_mode = is_single_user_mode()
155-
if single_user_mode:
131+
if is_single_user_mode():
156132
return {"current_user": None, "single_user_mode": True}
157133

158-
# Use the user cached by before_request — no additional DB query needed.
159-
current_user = g.get("current_user")
160-
return {"current_user": current_user, "single_user_mode": False}
134+
return {
135+
"current_user": g.get("current_user"),
136+
"single_user_mode": False,
137+
}
161138

162139

140+
# ---------------------------------------------------------------------------
141+
# Auth enforcement
142+
# ---------------------------------------------------------------------------
143+
163144
_PUBLIC_ENDPOINTS = frozenset(
164145
{
165146
"auth.login",
@@ -175,7 +156,6 @@ def inject_current_user():
175156

176157
@app.before_request
177158
def _require_auth():
178-
"""Redirect unauthenticated users to /login in multi-user mode."""
179159
from auth_mode import is_single_user_mode
180160

181161
if is_single_user_mode():
@@ -184,13 +164,24 @@ def _require_auth():
184164
return
185165
if g.get("current_user"):
186166
return
167+
187168
return redirect(url_for("auth.login"))
188169

189170

171+
# ---------------------------------------------------------------------------
172+
# Request logging
173+
# ---------------------------------------------------------------------------
174+
175+
190176
@app.after_request
191177
def _log_request(response):
192178
if request.endpoint != "api.health":
193-
_access_log.info("%s %s %s", request.method, request.path, response.status_code)
179+
_access_log.info(
180+
"%s %s %s",
181+
request.method,
182+
request.path,
183+
response.status_code,
184+
)
194185
return response
195186

196187

@@ -221,7 +212,7 @@ def _log_request(response):
221212
app.register_blueprint(stripe_bp)
222213

223214
# ---------------------------------------------------------------------------
224-
# CLI entry point
215+
# CLI entry point (dev only)
225216
# ---------------------------------------------------------------------------
226217

227218
if __name__ == "__main__":
@@ -238,8 +229,9 @@ def _log_request(response):
238229
print(" Health: http://localhost:5000/health")
239230
print("\nPress Ctrl+C to stop")
240231

241-
try:
242-
app.run(debug=config.get("debug", False), host="0.0.0.0", port=5000, threaded=True)
243-
except Exception as e:
244-
print(f"Server failed to start: {e}")
245-
exit(1)
232+
app.run(
233+
debug=config.get("debug", False),
234+
host="0.0.0.0",
235+
port=5000,
236+
threaded=True,
237+
)

0 commit comments

Comments
 (0)