Skip to content

Repository files navigation

ZKAPI

REST API and persistence layer for the ZK attendance platform — a biometric time-and-attendance system built around ZKTeco fingerprint terminals.

ZKAPI owns the database. It exposes CRUD endpoints over employees, departments and attendance records, and it is the write target for the device collector that polls the physical terminals.


Where this fits

The platform is three repositories. This one is the middle layer.

┌──────────────────────┐
│  8 × ZKTeco terminal │   192.168.8.35 → .42, TCP 4370
│  (fingerprint/face)  │
└──────────┬───────────┘
           │  zkemkeeper COM SDK (pull)
           ▼
┌──────────────────────┐
│      ZUtility        │   Windows service. Polls terminals, pairs raw
│  (device collector)  │   punches into check-in/check-out sessions.
└──────────┬───────────┘
           │  HTTP POST /api/Attendance
           ▼
┌──────────────────────┐
│    ZKAPI  ← this repo│   ASP.NET Core + EF Core. Owns the MySQL schema.
│   (REST + database)  │
└──────────┬───────────┘
           │  HTTP GET (SWR)
           ▼
┌──────────────────────┐
│       FrontZK        │   Next.js dashboard. Daily register, per-employee
│     (dashboard)      │   history, monthly summaries.
└──────────────────────┘
Repository Role Link
ZKAPI REST API, MySQL schema this repository
ZUtility Device collector (Windows) Heritina-sys/ZUtility
FrontZK Web dashboard Heritina-sys/FrontZK

Full data flow, including the enroll-number translation that happens on write: docs/ARCHITECTURE.md.


Stack

Runtime .NET 10 (net10.0), ASP.NET Core
ORM Entity Framework Core 8.0.11
Database MySQL 8.0 via Pomelo.EntityFrameworkCore.MySql 8.0.2
API docs Swashbuckle / Swagger UI
Nullable enabled

Quick start

Prerequisites

1. Create the database

CREATE DATABASE att CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

2. Supply the connection string

appsettings.json ships with an empty connection string on purpose — real credentials must never be committed. Provide yours by environment variable:

export ConnectionStrings__DefaultConnection="server=localhost;port=3306;database=att;user=zkapi;password=<your-password>"

or, for local development, with the .NET secret store:

cd ZKAPI
dotnet user-secrets init
dotnet user-secrets set "ConnectionStrings:DefaultConnection" "server=localhost;port=3306;database=att;user=zkapi;password=<your-password>"

Both are read automatically — no code change needed. See docs/CONFIGURATION.md for precedence rules and the full setting list.

3. Create the schema

This project has no EF migrations committed (see Known limitations). Until migrations are added, generate the schema from the model:

cd ZKAPI
dotnet ef migrations add InitialCreate
dotnet ef database update

4. Run

dotnet run --project ZKAPI
HTTP http://localhost:5159
HTTPS https://localhost:7171
Swagger UI http://localhost:5159/swagger

API surface

Three resources, conventional REST, no authentication (see Known limitations). Full request/response schemas and examples: docs/API.md.

Method Route Purpose
GET /api/Users List employees, department name joined
GET /api/Users/{id} One employee
POST /api/Users Create employee
PUT /api/Users/{id} Update employee
DELETE /api/Users/{id} Delete employee
GET /api/Departments List departments
GET /api/Departments/{id} One department
POST /api/Departments Create department
PUT /api/Departments/{id} Update department
DELETE /api/Departments/{id} Delete department
GET /api/Attendance List attendance, employee name joined
GET /api/Attendance/{id} One attendance record
POST /api/Attendance Record a punch — resolves by EnrollNumber
PUT /api/Attendance/{id} Update a record
DELETE /api/Attendance/{id} Delete a record

POST /api/Attendance is asymmetric with the rest of the API. The incoming userId field is interpreted as the terminal's enroll number, not as User.Id. The controller looks up the matching employee and stores the real foreign key. This exists because ZUtility only knows enroll numbers — the terminals have no concept of database identity. GET responses return the real User.Id. Details in docs/API.md.


Data model

Department 1 ──── ∞ User 1 ──── ∞ Attendance
Entity Key fields
Department Id, Name (required, ≤100)
User Id, EnrollNumber (terminal ID), Name (≤100), DepartmentId → FK
Attendance Id, UserId → FK, Date, CheckIn, CheckOut, CheckInMode, CheckOutMode

CheckInMode / CheckOutMode carry the terminal's verification method. The numeric mapping is defined by the ZKTeco SDK and mirrored in FrontZK (lib/types.ts):

Value Method
0 Fingerprint
1 Card
2 Password
3 Face
4 Manual

Every field on Attendance except Id is nullable, which is how an open session is represented: a record with CheckIn set and CheckOut still null.


Known limitations

These are real, present in the code today, and tracked here rather than left for the next reader to discover.

# Issue Impact
1 No authentication or authorisation. Program.cs calls UseAuthorization() but registers no authentication scheme, and no controller carries [Authorize]. Anyone who can reach the port has full read/write/delete access to employee attendance records. Do not expose this outside a trusted network. See SECURITY.md.
2 No EF migrations committed. AppDbContext defines the model but no Migrations/ folder exists. The schema cannot be reproduced or versioned from the repository. Onboarding requires generating a migration by hand.
3 CORS origin hardcoded to http://localhost:3000 in Program.cs. Any non-local FrontZK deployment needs a recompile.
4 Swagger is served unconditionally, outside an IsDevelopment() guard. The full API shape is published in every environment, including production.
5 Query parameters in docs/API.md are not implemented. FrontZK calls /api/Attendance?date=…, ?userId=…, ?startDate=…&endDate=…; AttendanceController.GetAll() ignores all of them and returns the entire table. The dashboard filters client-side. Works at 41 employees, will not work at scale.
6 Duplicated [HttpPost] attribute on AttendanceController.Create. Harmless today, but it is a copy-paste artefact that should go.
7 WeatherForecast scaffolding (WeatherForecast.cs, WeatherForecastController.cs) is still present from the project template. Dead code and a live unauthenticated endpoint.
8 user=root;password=root was committed in appsettings.json from May 2026 until September 2026. Removed from HEAD, still present in git history. See SECURITY.md.

Contributions that close any of these are welcome — see CONTRIBUTING.md.


Repository layout

ZKAPI.slnx                      Solution (SLNX format)
ZKAPI/
├── Program.cs                  Host, DI, EF, CORS, Swagger
├── Controllers/
│   ├── UsersController.cs
│   ├── DepartmentsController.cs
│   ├── AttendanceController.cs
│   └── WeatherForecastController.cs   ← template leftover
├── Models/
│   ├── AppDbContext.cs         DbSets and relationship config
│   ├── User.cs / UserDto.cs
│   ├── Department.cs / DepartmentDto.cs
│   └── Attendance.cs / AttendanceDto.cs
├── appsettings.json            No secrets — empty connection string
└── ZKAPI.csproj
docs/
├── ARCHITECTURE.md             Three-repo data flow, design decisions
├── API.md                      Endpoint reference
└── CONFIGURATION.md            Settings and precedence

Documentation

Document Contents
docs/ARCHITECTURE.md System data flow, why the enroll-number indirection exists, state ownership
docs/API.md Every endpoint, payloads, status codes, curl examples
docs/CONFIGURATION.md Every setting, environment variables, precedence
CONTRIBUTING.md Branching, commit format, review expectations
SECURITY.md Reporting process and the credential exposure history
CHANGELOG.md Release history

License

MIT.

About

REST API for ZKTeco biometric attendance — ASP.NET Core, EF Core, MySQL

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages