Skip to content

Latest commit

 

History

History
602 lines (437 loc) · 21.3 KB

File metadata and controls

602 lines (437 loc) · 21.3 KB

kistack API Documentation

This document describes the HTTP endpoints currently exposed by the backend.

  • Base URL: http://localhost:8080
  • Data format: JSON unless otherwise specified
  • Authentication: Spring Security session cookie (JSESSIONID)
  • Default verification code length: 6
  • Default upload limit: 10 MB
  • Chinese version: API_zh.md

1. General Conventions

1.1 Session Authentication

After a successful login, the server maintains authentication through the JSESSIONID cookie. Requests to /u/** require the USER or ADMIN authority, while /back/** requires ADMIN. /a/** and /p/** allow anonymous access.

Only one active session is allowed per account.

1.2 CSRF

CSRF protection applies to all currently exposed state-changing endpoints, including anonymous POST endpoints under /a/** and /p/**. Server-rendered pages expose the token through:

<meta name="csrf-token" content="...">
<meta name="csrf-header" content="X-CSRF-TOKEN">

JSON and multipart requests should send the token using the supplied header name. HTML forms should submit the _csrf hidden field.

X-CSRF-TOKEN: <token>

CSRF failures are handled directly by Spring Security and do not pass through the application exception handler.

1.3 Responses and Errors

Business endpoints return 200 OK on success. JSON endpoints use a { "data": ... } envelope; sensitive profile changes return 302 Location: /login.

Errors also use the JSON envelope. Validation and account workflow failures return 400 Bad Request; content authorization failures return 403 Forbidden; missing posts, comments, or public users return 404 Not Found; duplicate post titles return 409 Conflict; unexpected failures return 500 Internal Server Error. Authentication and CSRF failures may be handled directly by Spring Security.

Detailed business messages are returned for profile changes, content validation, moderation constraints, and content 403/404/409 responses. Other account workflows deliberately return the generic {"data":"Request error"} message to avoid exposing sensitive account state. Oversized uploads return {"data":"Max upload size exceeded"}.

Spring Security handles form-login failures directly rather than passing them through the global exception handler: the server returns 302 Location: /login?error, and the login page displays a generic failure message based on the error query parameter. Unauthenticated access to other protected endpoints may also be redirected to /login instead of receiving a JSON 401 response.

2. Endpoint Summary

Method Path Authentication CSRF Description
POST /a/register/verify Public Required Send registration verification code
POST /a/register Public Required Register an account
POST /a/reset-password/verify Public Required Send password-reset verification code
POST /a/reset-password Public Required Reset password
POST /a/login Public Required Log in
POST /logout Authenticated Required Log out
GET /u/me USER or ADMIN No Get current user profile
POST /u/me/modify/name USER or ADMIN Required Update username
POST /u/me/modify/description USER or ADMIN Required Update description
POST /u/me/modify/password USER or ADMIN Required Update password
POST /u/me/modify/email/verify USER or ADMIN Required Send new-email verification code
POST /u/me/modify/email USER or ADMIN Required Update email
POST /u/me/modify/avatar USER or ADMIN Required Upload current user avatar
GET /back/users ADMIN No List users with pagination
POST /back/modify/user ADMIN Required Update all managed fields for a user
POST /back/create/user ADMIN Required Create a user
POST /back/delete/user ADMIN Required Delete a user
DELETE /back/posts/{postId} ADMIN Required Delete any post and its discussion data
DELETE /back/comments/{commentId} ADMIN Required Moderate any comment
GET /p/users/{id} Public No Get a public user profile
GET /p/users/{id}/avatar Public No Resolve a public user's avatar
GET /avatars/{filename} Public No Read an avatar file
GET /p/posts Public No List posts by hot score
GET /p/posts/hot Public No Get the hottest posts
GET /p/posts/search/author Public No Search posts by author name
GET /p/posts/author/{authorId} Public No List posts by author ID
GET /p/posts/search/title Public No Search posts by title
GET /p/posts/search/tag Public No Search posts by tag
GET /p/posts/{postId} Public No Read a post and record an effective view
POST /u/posts USER or ADMIN Required Create a post
PUT /u/posts/{postId} USER or ADMIN Required Replace the current user's post content
DELETE /u/posts/{postId} USER or ADMIN Required Delete the current user's post
GET /p/posts/{postId}/comments Public No List root comments
GET /p/comments/{commentId}/replies Public No List direct replies
POST /u/posts/{postId}/comments USER or ADMIN Required Publish a root comment
POST /u/comments/{commentId}/replies USER or ADMIN Required Reply to a comment
DELETE /u/comments/{commentId} USER or ADMIN Required Delete the current user's comment
PUT /u/posts/{postId}/like USER or ADMIN Required Like a post
DELETE /u/posts/{postId}/like USER or ADMIN Required Remove a post like
PUT /u/comments/{commentId}/like USER or ADMIN Required Like a comment
DELETE /u/comments/{commentId}/like USER or ADMIN Required Remove a comment like

3. Authentication Endpoints

3.1 Send Registration Verification Code

POST /a/register/verify

Sends an email verification code for registration. The email address must not already be registered.

Content-Type: application/json
X-CSRF-TOKEN: <token>
{
  "email": "user@example.com",
  "scene": "REGISTER"
}

Field requirements:

  • email: Required and must match a valid email format.
  • scene: Must be REGISTER.

Success: 200 OK, application/json

{
  "data": null
}

3.2 Register Account

POST /a/register

Creates an account using a registration verification code. Registration does not automatically create a login session.

Content-Type: application/json
X-CSRF-TOKEN: <token>
{
  "username": "alice",
  "password": "secret123",
  "email": "user@example.com",
  "verificationCode": "A1B2C3"
}

Field requirements:

  • username: Required, non-blank, no regular space characters, at most 32 characters, and unique.
  • password: Required, no regular space characters, and 6 to 32 characters long.
  • email: Required, validly formatted, and unique.
  • verificationCode: Required; its length must match the verification configuration, which defaults to 6.

Success: 200 OK, application/json

{
  "data": {
    "message": "Register successfully",
    "name": "alice"
  }
}

3.3 Send Password-reset Verification Code

POST /a/reset-password/verify

Sends a high-risk operation verification code to an existing account email.

Content-Type: application/json
X-CSRF-TOKEN: <token>
{
  "email": "user@example.com",
  "scene": "HIGH_RISK"
}

Field requirements:

  • email: Required, validly formatted, and must belong to an existing account.
  • scene: Must be HIGH_RISK.

Success: 200 OK, application/json

{
  "data": null
}

3.4 Reset Password

POST /a/reset-password

The verification code and email must belong to the same account. On success, the code is invalidated and matching existing sessions are expired through the session registry.

Content-Type: application/json
X-CSRF-TOKEN: <token>
{
    "email": "user@example.com",
  "newPassword": "newSecret123",
  "verificationCode": "D4E5F6"
}

Field requirements:

  • email: Required, validly formatted, and must belong to an existing account.
  • newPassword: Required, no regular space characters, and 6 to 32 characters long.
  • verificationCode: Required; its length must match the verification configuration.

Success: 200 OK, application/json

{
  "data": "Password reset successfully"
}

3.5 Login

POST /a/login

This endpoint is provided by Spring Security form login and does not accept JSON.

Content-Type: application/x-www-form-urlencoded
username=alice&password=secret123&_csrf=<token>
  • Success: sets the session cookie and redirects with 302 to /dashboard.
  • Failure: redirects back to the login page with 302 Location: /login?error. GET /login?error renders the login page with the generic "Incorrect username or password" message; the failure response is not JSON.

3.6 Logout

POST /logout

Requires an active session and a CSRF form field.

_csrf=<token>

On success, the current session is invalidated and the client is redirected with 302 to /login?logout by default.

4. User Profile Endpoints

4.1 Get Current User Profile

GET /u/me

Authentication: required (USER or ADMIN)

Success: 200 OK, application/json

{
  "data": {
    "userId": 1,
    "userName": "alice",
    "userEmail": "user@example.com",
    "description": "Hello",
    "createdAt": "2026-07-29T12:00:00",
    "enabled": true
  }
}

createdAt uses Jackson's default ISO-8601 JSON representation for LocalDateTime.

4.2 Update Username

POST /u/me/modify/name with {"newName":"alice2"}. The target user is always taken from the authenticated session. Success returns 302 Location: /login.

4.3 Update Description

POST /u/me/modify/description with {"newDescription":"About me"}.

4.4 Update Password

POST /u/me/modify/password with {"oldPassword":"old","newPassword":"newSecret"}. Success returns 302 Location: /login.

4.5 Update Email

Call /u/me/modify/email/verify with {"email":"new@example.com","scene":"RESET_EMAIL"}, then submit {"email":"new@example.com","password":"currentPassword","verificationCode":"ABC123"} to /u/me/modify/email. Success returns 302 Location: /login.

4.6 Update Current User Avatar

POST /u/me/modify/avatar

Authentication: required (USER or ADMIN)

Content-Type: multipart/form-data; boundary=...
X-CSRF-TOKEN: <token>
Name Type Required Description
file File Yes New avatar image

Requirements:

  • The file must be non-empty and its MIME type must start with image/.
  • The backend decodes the actual image content instead of trusting the filename extension.
  • PNG, JPEG, and GIF are supported.
  • Both the default per-file and total request limits are 10 MB.
  • The account must be enabled.

Success: 200 OK with {"data":null}.

5. Administrator Endpoints

5.1 List Users

GET /back/users

Authentication: ADMIN only. Supports Spring Data Pageable query parameters:

Parameter Example Description
page 0 Zero-based page index
size 20 Number of records per page, clamped to 1..100
sort id,asc Sort property and direction; may be repeated

Example: GET /back/users?page=0&size=20&sort=id,asc

The successful response stores a stable pagination object in data:

{
  "data": {
    "content": [
      {
        "id": 2,
        "username": "alice",
        "email": "alice@example.com",
        "avatarId": 2,
        "role": "USER",
        "enabled": true,
        "createdAt": "2026-07-29T12:00:00",
        "updatedAt": "2026-07-30T12:00:00"
      }
    ],
    "page": 0,
    "size": 20,
    "totalPages": 1,
    "totalElements": 1,
    "first": true,
    "last": true
  }
}

5.2 Update a User

POST /back/modify/user

Authentication: ADMIN only. Requires JSON and a CSRF header. Every save must include all five fields, including fields whose values did not change:

{
  "id": 2,
  "username": "alice",
  "email": "alice@example.com",
  "role": "USER",
  "enabled": true
}

Field requirements:

  • id: Required and must identify an existing user.
  • username: Required, non-empty, must not contain @ or a regular space, and at most 32 characters.
  • email: Required, non-empty, and must match the configured email regular expression.
  • role: Required; valid values are USER and ADMIN.
  • enabled: Required boolean. The DTO uses a primitive value, so omission binds as false; clients must not omit it.

The system always requires at least one enabled ADMIN; a change violating this rule is rolled back. After a successful update, all sessions for the target user that match in the session registry are marked expired. If an administrator updates their own account, the admin page redirects to login.

Success: 200 OK; data contains the updated user view with the same fields as a list item.

5.3 Create a User

POST /back/create/user

Authentication: ADMIN only. Requires JSON and a CSRF header. The request must include all five fields:

{
  "username": "alice",
  "email": "alice@example.com",
  "password": "secret1",
  "role": "USER",
  "enabled": true
}

Field requirements:

  • username: Required, non-empty, must not contain @ or a regular space, and at most 32 characters.
  • email: Required, non-empty, and must match the configured email regular expression.
  • password: Required, non-empty, must not contain regular spaces, and 6 to 64 characters long.
  • role: Required; valid values are USER and ADMIN.
  • enabled: Must explicitly be true; the current backend does not allow creating a disabled user directly.

The password is stored using BCrypt. Success returns 200 OK; data contains the new user view and never includes the password or password hash.

5.4 Delete a User

POST /back/delete/user

Authentication: ADMIN only. Requires JSON and a CSRF header. The request uses the complete last-known snapshot returned by the server:

{
  "id": 2,
  "username": "alice",
  "email": "alice@example.com",
  "role": "USER",
  "enabled": true
}

id, username, email, and role must match the stored user or the request fails. The admin page also sends enabled. On success, matching target sessions are marked expired and the response is {"data":true}. The service checks that an enabled administrator remains in the system.

5.5 Delete a Post

DELETE /back/posts/{postId}

Authentication: ADMIN only. A CSRF token is required and the request has no body. The administrator may delete a post regardless of its author. The operation runs in one transaction and removes the post's comments, comment likes, post likes, tag rows, and view-deduplication cache entries before the post becomes inaccessible.

Success: 200 OK. An unknown post returns 404 Not Found.

5.6 Delete a Comment

DELETE /back/comments/{commentId}

Authentication: ADMIN only. A CSRF token is required and the request has no body. The administrator may moderate a comment regardless of its publisher. A leaf comment is physically removed; a comment with replies is converted into the same deleted placeholder used by author deletion so that replies remain reachable. Likes and active comment counters are updated transactionally. Repeating the request for an existing soft-deleted placeholder is idempotent.

Success: 200 OK. An unknown comment returns 404 Not Found.

6. Public Profile and Avatar Endpoints

6.1 Get Public Profile

GET /p/users/{id}

Anonymous access is allowed and no CSRF token is needed. Disabled or unknown users return 404 Not Found. The response uses the standard data envelope:

{
  "data": {
    "userId": 2,
    "userName": "alice",
    "description": "Backend developer",
    "avatarUrl": "/p/users/2/avatar",
    "postCount": 3,
    "createdAt": "2026-07-29T12:00:00"
  }
}

6.2 Resolve Public Avatar

GET /p/users/{id}/avatar

Returns 302 Found with a Location header pointing to the user's current image or the default avatar. Disabled or unknown users return 404 Not Found.

6.3 Read Avatar File

GET /avatars/{filename}

Public. Reads a file from the configured kistack.data.path/avatars directory. The content type is determined by the static resource handler and file type.

GET /avatars/default.png

7. Page Routes

These routes return Thymeleaf HTML pages rather than JSON APIs.

Method Path Access Page
GET /login Public Login page
GET /register Public Registration page
GET /reset-password Public Password-reset page
GET / or /index Public Post index and search page
GET /posts/{postId} Public Post detail and comments page
GET /users/{userId} Public Public user profile page
GET /dashboard USER or ADMIN User profile dashboard
GET /editor USER or ADMIN Create-post editor
GET /editor/{postId} USER or ADMIN Edit-post editor
GET /back/dashboard ADMIN Administrator user-management page

GET /favicon.svg is a public static resource.

8. Post and Comment Endpoints

All list endpoints return a stable page object inside the normal data envelope:

{
  "data": {
    "content": [],
    "page": 0,
    "size": 20,
    "totalElements": 0,
    "totalPages": 0,
    "first": true,
    "last": true
  }
}

Post, search, comment, and reply lists are ordered by hotScore DESC, then createdAt DESC and id DESC. page defaults to 0, size defaults to 20, and size is capped at 100. The hot-post endpoint accepts limit, defaults to 20, and caps it at 50.

8.1 Query Posts

Method and path Query parameters
GET /p/posts page, size
GET /p/posts/hot limit
GET /p/posts/search/author keyword, page, size
GET /p/posts/author/{authorId} page, size
GET /p/posts/search/title keyword, page, size
GET /p/posts/search/tag keyword, page, size
GET /p/posts/{postId} None

Search is case-insensitive for ASCII text and treats %, _, and ! as ordinary characters. List responses omit full article content. Reading one post returns its content and records at most one view per visitor in the configured deduplication window. Lists and searches never increase view counts.

Old paths such as /p/post/pid={id} and /p/post/title={title} have been removed.

8.2 Create, Modify, and Delete Posts

POST /u/posts and PUT /u/posts/{postId} use this body:

{
  "title": "Spring Data JPA notes",
  "content": "Article content...",
  "tags": ["spring", "jpa", "sqlite"]
}

The author always comes from the authenticated session. title is limited to 200 characters, content to 100000 characters, and each post must have 1 to 10 tags of at most 64 characters. Tags are trimmed, lowercased, and deduplicated. Only the author can modify or delete a post. Deleting a post also removes its comments and all related likes and tags.

8.3 Comments and Replies

Root comments are read from GET /p/posts/{postId}/comments; direct replies are read from GET /p/comments/{commentId}/replies. Publishing and replying use:

{
  "content": "Comment text"
}

Comment content is limited to 500 characters. Reply postId and respondentId are derived from the parent comment; clients cannot supply them. A leaf comment is removed physically. A comment with replies is retained as a deleted placeholder so its reply chain remains readable. Users can only delete their own comments.

8.4 Likes and Hot Scores

Post and comment like endpoints are idempotent. A unique database record prevents the same user from increasing a counter twice, and repeated unlike requests cannot make counters negative. Successful responses are shaped as follows:

{
  "data": {
    "liked": true,
    "likeCount": 12
  }
}

Post hot scores combine logarithmic view, like, and comment signals with time decay. Comment hot scores use likes, direct replies, and time decay. Scores are recalculated after interactions and periodically in the background.

9. Configuration Notes

The following behavior can be changed through configuration or environment variables, so deployment values may differ from the defaults in this document:

  • Initial administrator password: KISTACK_ADMIN_PASSWORD (required; no default)
  • Administrator reset switch: KISTACK_ADMIN_RESET_ENABLED (defaults to false)
  • Verification code length: kistack.email.verification.verification-code-length
  • Email format regular expression: kistack.email.from.email-format-regex
  • Per-file size: spring.servlet.multipart.max-file-size
  • Total request size: spring.servlet.multipart.max-request-size
  • Data directory: kistack.data.path
  • Default avatar: kistack.data.default-avatar
  • Post-view deduplication window: kistack.caffeine.post-view-deduplicate-minutes
  • Post-view cache capacity: kistack.caffeine.post-view-maximum-size
  • Hot-score refresh interval: kistack.hot-score.refresh-interval-ms
  • Hot-score refresh batch size: kistack.hot-score.refresh-batch-size