Clock in and out
Employees record one clock-in and one clock-out per day. The status, on time up to 08:00 WIB and late after, is set automatically.
PROJECT 01 · FULL-STACK APP
Attenda is a full-stack employee attendance app. A React frontend talks to a Go REST API backed by MySQL. Employees clock in and out from a dashboard, and administrators manage accounts and review attendance records.
Overview
01 / WHY
Recording attendance is only part of the problem. Employees need a simple way to log their time, and administrators need one place to manage access and review the records. I built Attenda to practice authentication, role-based access, and a clean separation between frontend and backend.
02 / WHAT IT DOES
Employees record one clock-in and one clock-out per day. The status, on time up to 08:00 WIB and late after, is set automatically.
On-time rate, recent attendance, and a searchable attendance history.
Admins create employee accounts and activate or deactivate them. Deactivated accounts lose access at once.
Admins see a summary of today and browse all records, searching by employee name and filtering by status.
bcrypt passwords, HS256 JWT, role middleware, input validation, and rate limiting on login and register.
Go tests for the JWT, the late rule, validation, and the admin middleware. GitHub Actions runs format, vet, test, lint, and build.
SCREENSHOTS
API ENDPOINTS
| METHOD | PATH | ACCESS | NOTE |
|---|---|---|---|
| GET | /health | Public | API and database health check. |
| POST | /register | Public | Create an employee account. The role cannot be chosen by the client. |
| POST | /login | Public | Returns a JWT. Rate limited per IP. |
| GET | /profile | Login required | The logged-in user. |
| POST | /clock-in | Login required | Record today's clock-in. Status is on_time or late, set by the server. |
| POST | /clock-out | Login required | Record today's clock-out. |
| GET | /attendance/history | Login required | Your own attendance history, newest first. |
| GET | /admin/employees | Admin only | List employees. |
| POST | /admin/employees | Admin only | Create an employee account. |
| PATCH | /admin/employees/:id/status | Admin only | Activate or deactivate an employee. |
| GET | /admin/attendances | Admin only | All attendance records. |
DESIGN DECISIONS
The auth middleware loads the user from the database each time. A deactivated account, or one whose role changed, loses access immediately instead of keeping it until the JWT expires.
A unique index on user and date rejects a second clock-in for the same day, even when two requests arrive at the same moment. The handler turns MySQL duplicate-entry error 1062 into a clear response.
Public registration always creates an employee. Admin accounts come only from a seed command, and admin routes sit behind both the auth and the admin middleware.
A clock-in after 08:00 WIB is late. The cutoff is computed in the Asia/Jakarta time zone on the server, so it does not depend on the device clock, and a unit test covers the rule.
Passwords use bcrypt. Tokens must be HS256 and are rejected otherwise, the server refuses to run auth without a JWT secret, login and register are rate limited, and the server has read, write, and idle timeouts.
03 / NEXT IMPROVEMENTS
Store the JWT in an httpOnly cookie with refresh tokens instead of localStorage, paginate the admin lists, use versioned migrations instead of AutoMigrate, and add integration tests that run against a real database.