No description
- Go 100%
Bumps go.mod from 1.23 to 1.26 to match the installed toolchain. Refactors store to use os.Root (Go 1.24+) for file operations: - Split filePath into rootDir + fileName - load() opens the directory as a root and reads within it - save() creates the directory if needed, then writes via root - Prevents path traversal attacks (e.g. DATA_FILE=../../../etc/passwd) Updates README and docs to reference Go 1.26 and document os.Root and range-over-func iterators (slices.All, maps.All). |
||
|---|---|---|
| docs | ||
| .gitignore | ||
| go.mod | ||
| LICENSE | ||
| main.go | ||
| main_test.go | ||
| README.md | ||
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓ ▓▓
▓▓ ░▒▓ D̷O̷C̷H̷A̷Z̷K̷O̷T̷R̷O̷N̷-̷5̷0̷0̷0̷ ▓▒░ ▓▓
▓▓ ▓▓
▓▓ [ K I N D E R G A R T E N A T T E N D A N C E S Y S T E M ] ▓▓
▓▓ ▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
Simple web application for tracking kindergarten attendance with a parent excuse feature (must be submitted before 08:00).
Built as a modern Go 1.26 reference — structured logging, method-aware routing, graceful shutdown, slices generics, middleware, and table-driven tests.
Features
- Two classes (16 children each)
- Absence excuses accepted until 08:00 on the given day
- Attendance list per class
- Parent authorization — only the assigned parent can excuse their child
- Health endpoint for load-balancer / k8s probes
- Graceful shutdown on
SIGINT/SIGTERM - Persistent JSON file storage
Requirements
Installation & Running
git clone <your-repo-url>
cd dochazkotron-5000
go mod tidy
go run main.go
Open http://localhost:8080 (or set PORT env variable).
Environment Variables
| Variable | Default | Description |
|---|---|---|
PORT |
8080 |
HTTP server port |
DATA_FILE |
attendance.json |
Path to the JSON data file |
API Endpoints
Health Check (GET)
curl http://localhost:8080/health
Response:
{"status":"ok"}
Excuse a Child (POST)
curl -X POST http://localhost:8080/excuse \
-H "Content-Type: application/json" \
-d '{"child_id":"child_0_0","parent_id":"parent1"}'
Parameters:
child_id— Child ID (e.g.child_0_0)parent_id— Parent ID (must match the child's assigned parent)
Constraints:
- Excuses are accepted only until 08:00 on the given day.
- A parent can only excuse their own children.
Success Response:
{"status":"excused"}
Error Responses:
400 Bad Request— Invalid JSON, missing fields, or excuse time expired403 Forbidden— Parent does not match child or unknown parent404 Not Found— Child not found
Get Class Attendance (GET)
curl "http://localhost:8080/attendance/class1"
Path Variable:
class_id— Class ID (class1orclass2)
Response:
{
"Dítě 1-1": true,
"Dítě 1-2": false
}
true— Child is absent and not excused (needs attention)false— Child is excused
Modern Go Patterns Used
This project is intentionally kept small to serve as a learning reference. Key modern patterns:
| Feature | Where | Why |
|---|---|---|
slog |
main() |
Structured, leveled logging instead of log |
http.ServeMux method + path matching |
main() |
Go 1.22+: POST /excuse, GET /attendance/{class_id} |
r.PathValue("class_id") |
handleAttendance |
Extract route variables without third-party routers |
signal.NotifyContext |
main() |
Graceful shutdown with context cancellation |
http.Server timeouts |
main() |
Production-hardened server config |
http.MaxBytesHandler |
main() |
Request body size limits |
os.Root |
store |
Go 1.24+: sandboxed file I/O prevents path traversal |
slices.All / maps.All |
store |
Go 1.23+: range-over-func iterators |
slices.IndexFunc |
store |
Generic slice search |
range over integers |
newStore |
for j := range 16 |
cmp.Or |
main() |
Default-value fallback |
time.DateOnly |
store |
"2006-01-02" constant |
errors.Is sentinel errors |
handlers + tests | Clean error classification |
sync.RWMutex |
store |
Many concurrent readers, one writer |
| Middleware chaining | app.chain |
Composable recoverPanic + logRequest |
t.Parallel() + table-driven tests |
main_test.go |
Fast, scalable test suite |
httptest through ServeMux |
main_test.go |
Tests routing + path values together |
Injected clock function |
app struct |
Deterministic time in tests |
Data Storage
All data is stored in attendance.json (override with DATA_FILE).
Example structure:
{
"classes": [
{
"id": "class1",
"name": "Třída 1",
"children": [
{
"id": "child_0_0",
"name": "Dítě 1-1",
"class_id": "class1",
"parent_id": "parent1"
}
]
}
],
"attendances": [],
"parents": ["parent1", "parent2"]
}
Data Model
Children
- Each class contains 16 children.
- Every child has:
id— Unique identifier (e.g.child_0_0)name— Display nameclass_id— Class identifierparent_id— Parent ID used for excuse authorization
Testing
go test -v -race ./...
Tests cover:
- Store logic (excuse, attendance, validation, persistence)
- HTTP handlers (routing, status codes, JSON responses)
- Middleware (panic recovery, request logging)
- Time constraint logic
Production Notes
- The server runs with read/write timeouts and graceful shutdown.
- For HTTPS, place a reverse proxy (Nginx, Caddy, Traefik) or a cloud load balancer in front.
- The JSON file uses
0600permissions. For production workloads, migrate to a real database. - Authentication is parent-ID-based in this demo. Add OAuth2 / JWT for real deployments.
License
MIT