Skip to content

Commit 65df30c

Browse files
authored
Merge pull request #1 from NicoMartinic/2_update_readme_and_envs
2 Update readme, adjust .env files examples
2 parents 445dc62 + b31a4d8 commit 65df30c

3 files changed

Lines changed: 58 additions & 5 deletions

File tree

.env.development.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ PORT=3000
2323
# ─── Error tracking (optional) ─────────────────────────────────────────────────
2424
# SENTRY_DSN=https://xxx@oXXX.ingest.sentry.io/YYY
2525
# NEXT_PUBLIC_SENTRY_DSN=https://xxx@oXXX.ingest.sentry.io/YYY
26+
# HTTPS_ENABLED=0

.env.production.example

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ PORT=3000
2323
# ─── Error tracking ────────────────────────────────────────────────────────────
2424
# SENTRY_DSN=https://xxx@oXXX.ingest.sentry.io/YYY
2525
# NEXT_PUBLIC_SENTRY_DSN=https://xxx@oXXX.ingest.sentry.io/YYY
26-
26+
# HTTPS_ENABLED=1
2727

2828
# ─── For Testing locally for prod(Access via IP) ────────────────────────────────────────────────────────────────────
2929

@@ -55,3 +55,4 @@ PORT=3000
5555
# SENTRY_DSN=https://xxx@oXXX.ingest.sentry.io/YYY
5656
# NEXT_PUBLIC_SENTRY_DSN=https://xxx@oXXX.ingest.sentry.io/YYY
5757
# Browser hits http://localhost (port 80 via nginx), not :3000
58+
# HTTPS_ENABLED=0

README.md

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,12 @@ Copy `.env.development.example` → `.env` for local dev.
148148
| `CORS_ALLOWED_ORIGINS` | Allowed CORS origins | `http://localhost:3000` |
149149
| `COOKIE_SECURE` | Secure flag on refresh cookie | `False` |
150150
| `BACKEND_URL` | Internal URL used by Next.js proxy | `http://backend:8000` |
151+
| `HTTPS_ENABLED` | Set to `1` once TLS is terminated by nginx/LB | `0` |
151152
| `SENTRY_DSN` | Backend Sentry DSN (optional) | _(empty)_ |
152153
| `NEXT_PUBLIC_SENTRY_DSN` | Frontend Sentry DSN (optional) | _(empty)_ |
153154

154-
For production: set `DEBUG=0`, generate a new `SECRET_KEY`, set `COOKIE_SECURE=True` and `COOKIE_SAMESITE=Strict`.
155+
For production: set `DEBUG=0`, generate a new `SECRET_KEY`, set `COOKIE_SECURE=True`,
156+
`COOKIE_SAMESITE=Strict`, and `HTTPS_ENABLED=1` only after SSL is configured.
155157

156158
---
157159

@@ -297,7 +299,8 @@ The production stack adds an **nginx** reverse proxy on port 80/443 with:
297299
- Security headers (X-Frame-Options, HSTS, CSP)
298300
- Gzip compression
299301
- Rate limiting on `/api/` routes
300-
- Ready for SSL — uncomment the HTTPS server block and mount your certificates
302+
- Ready for SSL — uncomment the HTTPS server block and mount your certificates,
303+
then set `HTTPS_ENABLED=1` in `.env.production`
301304

302305
---
303306

@@ -313,6 +316,54 @@ ESLint + tsc (TypeScript).
313316

314317
---
315318

316-
## License
319+
## Development Process
317320

318-
MIT
321+
This project was built iteratively in a series of focused sessions, each with a clear goal:
322+
323+
**Session 1 — Core architecture.** Established the full-stack foundation: Django REST API with JWT authentication, Next.js frontend with Redux + Redux-Saga, and the API proxy pattern that makes HttpOnly cookies work across services without CORS issues.
324+
325+
**Session 2 — Feature completion.** Built out all CRUD surfaces (notes, categories, users), implemented pagination, search, archive, dark mode, and the complete UI. Integrated Formik + Zod for form validation with field-level error propagation from the backend.
326+
327+
**Session 3 — Test infrastructure.** Wrote the full pytest backend suite, Jest unit tests for Redux reducers and Zod schemas, and 15 Playwright end-to-end tests covering every critical user journey. This session involved significant debugging — cold Next.js compilation races, httpOnly cookie path mismatches, React hydration timing, and Django throttle overrides that bypassed global settings.
328+
329+
**Session 4 — Polish and hardening.** Added the 13 improvements: `is_pinned` field and pin endpoint, note ordering API, Markdown editor with preview, category note-count badges, health check endpoint, Sentry integration, nginx reverse proxy, `.env` example files, Makefile, pre-commit hooks, and security headers.
330+
331+
**Session 5 — Production fixes.** Resolved nginx startup failure (upstream directives must live inside `http {}` — solved by using `conf.d/` mounting instead of replacing the root config), and the `SECURE_SSL_REDIRECT` 301 loop on Docker health checks (decoupled SSL redirect from `DEBUG`, gated behind an explicit `HTTPS_ENABLED=1` env var).
332+
333+
---
334+
335+
## Key Design and Technical Decisions
336+
337+
**API proxy over Next.js rewrites.** Next.js `rewrites` in `next.config.js` silently drop `Set-Cookie` response headers, making httpOnly cookie auth impossible. The Route Handler proxy solves this by copying headers explicitly. It also rewrites cookie `Path=` from `/api/auth/` to `/` so the refresh token is sent on all proxy routes, not just auth ones.
338+
339+
**Redux-Saga over React Query or SWR.** This app has complex async flows — token refresh queuing, replaying failed requests after a 401, coordinated side effects between auth and data loading. Sagas express these as readable sequential generator functions. React Query would require custom retry logic and shared interceptors to achieve the same result.
340+
341+
**Timestamp-based e2e test isolation.** Each Playwright test calls `uniqueUser()` which generates credentials like `testuser_1741234567890`. Tests never share state, never need database cleanup between runs, and can run in any order. The trade-off is that test databases accumulate users — acceptable for a dev stack.
342+
343+
**`BACKEND_URL` as runtime env, not build arg.** Next.js `output: 'standalone'` bakes environment into the build. The API proxy reads `process.env.BACKEND_URL` at request time (server-side), so it must be set as a Docker `environment:` variable at runtime — not just a `build.args` entry. Both are needed: the ARG for the build stage, the ENV for the running container.
344+
345+
**`HTTPS_ENABLED` decoupled from `DEBUG`.** `SECURE_SSL_REDIRECT = not DEBUG` seems obvious but creates a redirect loop in production Docker: the health check probe, the internal proxy, and inter-service traffic all use plain HTTP even when the public site is HTTPS. The fix is an explicit opt-in: set `HTTPS_ENABLED=1` only after TLS termination is confirmed at the nginx layer.
346+
347+
**nginx conf.d over full nginx.conf replacement.** The official nginx Docker image's root `nginx.conf` already contains the `http {}` wrapper and includes `conf.d/*.conf` inside it. Mounting a replacement `nginx.conf` that contains `upstream {}` at the top level causes "directive not allowed here" errors. Mounting into `conf.d/` is the correct pattern and requires no changes to the base image behaviour.
348+
349+
**Soft black/isort enforcement via pre-commit, strict in CI.** Local commits are formatted automatically. CI runs `--check` mode and fails the build if formatting diverges. This means contributors are never blocked by style issues locally, but unformatted code cannot merge.
350+
351+
---
352+
353+
## AI Tools Used
354+
355+
This project was developed through an extended series of sessions using **Claude (Anthropic)** as a pair-programming assistant.
356+
357+
**How it was used:**
358+
359+
- **Architecture design.** I designed the overall architecture and discussed key decisions with Claude before implementing them. This included the API proxy pattern, the Redux-Saga approach for token refresh queuing, and the JWT + httpOnly cookie split. Claude helped evaluate trade-offs and refine the implementation details.
360+
361+
- **Code implementation.** I used Claude to help generate and iterate on code based on the requirements I defined. This included Django views and serializers, Redux slices and sagas, React components, Tailwind styling, Docker configuration, nginx configuration, and GitHub Actions workflows.
362+
363+
- **Debugging.** Claude was particularly useful for analyzing complex failures. By sharing Docker logs, pytest output, and Playwright traces, I was able to work through root-cause analysis with Claude. For example, issues like cookies not being sent due to mismatched `Path` values between Django and the proxy routes were identified during these debugging sessions.
364+
365+
- **Iterative development.** Development progressed incrementally across multiple sessions. I provided the evolving codebase and requirements, and Claude assisted with targeted updates and fixes to specific files rather than regenerating large sections of the project.
366+
367+
- **Limitations.** Claude could not execute the code directly, so all fixes had to be tested locally using Docker. This required running the code, capturing logs or errors, and feeding that information back into the conversation. Some issues required several iterations to isolate the root cause, and maintaining awareness of the latest file versions during long debugging sessions was necessary.
368+
369+
---

0 commit comments

Comments
 (0)