Skip to content

Latest commit

 

History

History
251 lines (178 loc) · 6.97 KB

File metadata and controls

251 lines (178 loc) · 6.97 KB

API reference

Base URL in local development: http://localhost:5159 Interactive reference: http://localhost:5159/swagger

No authentication. No endpoint in this API requires credentials. Every route below is fully open to any client that can reach the port. See SECURITY.md.

All request and response bodies are application/json. Route names are case-insensitive (/api/users and /api/Users both work).


Conventions

Timestamps ISO 8601, serialised from .NET DateTime. No timezone offset is stored — values are whatever the terminal's clock reported.
Nullability Almost every field is nullable. A missing value means "not recorded", not "zero".
Errors 404 Not Found for unknown ids, 400 Bad Request with a plain-text message on the one validated path (POST /api/Attendance). No structured error envelope.
Success Write endpoints return 200 OK with the persisted entity (not the DTO). DELETE returns 200 OK with an empty body.

Users

Employees. The EnrollNumber field links an employee to their identity on the physical terminals.

GET /api/Users

Lists every employee with their department name joined in.

curl http://localhost:5159/api/Users
[
  {
    "id": 1,
    "enrollNumber": 35,
    "name": "Rakoto Aina",
    "departmentId": 2,
    "departmentName": "Production"
  }
]

Takes no query parameters. FrontZK calls GET /api/Users?departmentId=… in getUsersByDepartment(); the parameter is ignored and the full list is returned. The frontend then filters in useUsersByDepartment.

GET /api/Users/{id}

{id} is the database User.Id, not the enroll number.

200 OK with a single UserDto, or 404 Not Found.

POST /api/Users

curl -X POST http://localhost:5159/api/Users \
  -H 'Content-Type: application/json' \
  -d '{"name":"Rakoto Aina","enrollNumber":35,"departmentId":2}'

Reads name, enrollNumber and departmentId. Any id or departmentName in the body is ignored.

Returns 200 OK with the created User entity, including its assigned id.

EnrollNumber is not validated for uniqueness, and no unique index exists on the column. Two employees sharing an enroll number will silently break attendance attribution — see ARCHITECTURE.md § 4.

PUT /api/Users/{id}

Full replacement of name, enrollNumber, departmentId. 404 if {id} is unknown.

DELETE /api/Users/{id}

200 OK on success, 404 if unknown.

No cascade configuration is declared. Deleting an employee who has attendance rows will fail at the database level on the foreign-key constraint.


Departments

GET /api/Departments

[
  { "id": 1, "name": "Administration" },
  { "id": 2, "name": "Production" }
]

GET /api/Departments/{id}

200 OK with one DepartmentDto, or 404.

POST /api/Departments

curl -X POST http://localhost:5159/api/Departments \
  -H 'Content-Type: application/json' \
  -d '{"name":"Logistique"}'

Name is required and capped at 100 characters by the model.

PUT /api/Departments/{id}

Replaces name. 404 if unknown.

DELETE /api/Departments/{id}

200 OK, or 404. Same foreign-key caveat as employees: departments with employees attached cannot be deleted.


Attendance

One row per work session. CheckIn set with CheckOut still null means the session is open.

GET /api/Attendance

Returns every attendance record in the database, with the employee name joined.

[
  {
    "id": 412,
    "userId": 1,
    "userName": "Rakoto Aina",
    "date": "2026-09-07T00:00:00",
    "checkIn": "2026-09-07T07:52:14",
    "checkOut": "2026-09-07T16:31:02",
    "checkInMode": 0,
    "checkOutMode": 0
  }
]

No filtering is implemented. FrontZK calls this endpoint with ?date=, ?userId=, ?startDate=&endDate=GetAll() accepts no parameters and ignores all of them. Every dashboard view downloads the full table and filters in the browser. Acceptable at 41 employees; not at 500.

GET /api/Attendance/{id}

200 OK with one AttendanceDto, or 404.

POST /api/Attendance

This endpoint does not behave like the others. Read this before calling it.

The userId field in the request body is interpreted as the terminal's enroll number, not as User.Id. The controller resolves it to a real employee and stores the correct foreign key:

var user = await _context.Users.FirstOrDefaultAsync(u => u.EnrollNumber == dto.UserId);
if (user == null)
    return BadRequest($"Aucun user avec EnrollNumber {dto.UserId}");

This exists because ZUtility — the only production caller — reads enroll numbers off the terminals and has no way to know database identities. Rationale in ARCHITECTURE.md § 3④.

# 35 here is an ENROLL NUMBER, not a User.Id
curl -X POST http://localhost:5159/api/Attendance \
  -H 'Content-Type: application/json' \
  -d '{
        "userId": 35,
        "date": "2026-09-07T00:00:00",
        "checkIn": "2026-09-07T07:52:14",
        "checkOut": null,
        "checkInMode": 0,
        "checkOutMode": null
      }'
Response When
200 OK + Attendance entity Employee found. Note the returned userId is the real User.Id — it will differ from what you sent.
400 Bad Request + "Aucun user avec EnrollNumber {n}" No employee carries that enroll number.

Not idempotent. There is no natural-key constraint. Posting the same punch twice creates two rows. This is why a ZUtility restart — which resets its in-memory cursor and re-reads each device buffer from the beginning — duplicates history.

PUT /api/Attendance/{id}

Full replacement of date, checkIn, checkOut, checkInMode, checkOutMode, userId.

Unlike POST, PUT writes dto.UserId straight into the foreign key without any enroll-number lookup. The same field name means an enroll number on POST and a User.Id on PUT. Passing an enroll number here will corrupt the row's ownership.

DELETE /api/Attendance/{id}

200 OK, or 404.


Verification modes

checkInMode and checkOutMode carry the terminal's verification method, straight from the ZKTeco SDK's verifyMode. Mirrored in FrontZK as checkModeLabels (lib/types.ts).

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

Values outside this range are stored as-is; nothing validates the range.


Template leftovers

GET /api/WeatherForecast is still live — it comes from the ASP.NET Core project template and returns five random forecasts. It carries no data of interest and should be deleted.


Client implementations

Caller Location Uses
ZUtility Services/AttendanceService.PushAsync POST /api/Attendance
FrontZK lib/api.ts GET on all three resources