Đặc tả Kỹ thuật API

API Quản lý Tệp tin
FILE
Phiên bản1.0.0
Base URLhttp://localhost:3001/api/files
Hệ thốngHệ thống Cổng Thông tin Tuyển dụng
ModuleFILE - Quản lý Tệp tin
Ngày2026-08-17
Tác giảNam Nguyen
SRS liên quanSRS Quản lý Tệp tin

Mục lục

1. Tổng quan

1.1 Yêu cầu được Giải quyết

Yêu cầu SRSEndpoint API
FR-001 Tải lên Tệp tinPOST /files/upload (multipart)
FR-002 Tải xuống Tệp tinGET /files/:id/download
FR-003 Liệt kê Tệp tinGET /files
FR-004 Xóa Tệp tinDELETE /files/:id
FR-005 Kiểm soát Truy cập Tệp tinTất cả endpoint (middleware)
FR-008 Tạo Thư mụcPOST /files/folders
FR-009 Tìm kiếm Tệp tinGET /files/search
FR-010 Đổi tên Tệp tinPATCH /files/:id
FR-011 Tải lên Hàng loạtPOST /files/batch
FR-012 Sao chép Tệp tinPOST /files/:id/copy
FR-013 Di chuyển Tệp tinPOST /files/:id/move
FR-014 Quản lý QuyềnGET /files/:id/permissions, PUT /files/:id/permissions
FR-016 Theo dõi Sử dụng Lưu trữGET /files/storage-usage
FR-017 Quản lý Hạn ngạchGET /files/quotas, PUT /files/quotas
FR-018 Chính sách Lưu trữGET /files/policies, PUT /files/policies

1.2 Xác thực

Tất cả endpoint yêu cầu xác thực qua:

Ngoài ra, quyền truy cập tệp tin được kiểm tra theo quyền sở hữu hoặc thành viên tổ chức (RBAC).

1.3 Tóm tắt Endpoint

Phương thứcĐường dẫnMô tảCần Xác thực
POST/files/uploadTải lên tệp tin (multipart/form-data)
POST/files/batchTải lên hàng loạt (nhiều tệp tin)
GET/files/:id/downloadTải xuống tệp tin (file stream)
GET/filesLiệt kê tệp tin với lọc và phân trang
GET/files/searchTìm kiếm toàn văn tệp tin/thư mục
POST/files/foldersTạo thư mục mới
PATCH/files/:idĐổi tên tệp tin hoặc thư mục
POST/files/:id/copySao chép tệp tin đến thư mục đích
POST/files/:id/moveDi chuyển tệp tin đến thư mục đích
DELETE/files/:idXóa mềm tệp tin hoặc thư mục
GET/files/:id/permissionsLấy quyền tệp tin/thư mục
PUT/files/:id/permissionsQuản lý quyền tệp tin/thư mụcCó (Admin)
GET/files/storage-usageLấy thống kê sử dụng lưu trữ
GET/files/quotasLấy cấu hình hạn ngạch lưu trữCó (Admin)
PUT/files/quotasCập nhật giới hạn hạn ngạch lưu trữCó (Admin)
GET/files/policiesLấy chính sách lưu trữCó (Admin)
PUT/files/policiesCập nhật chính sách lưu trữCó (Admin)

2. Định dạng Phản hồi

2.1 Phản hồi Thành công

{
  "success": true,
  "data": { ... },
  "message": "Thao tác hoàn tất thành công"
}

2.2 Phản hồi Phân trang

{
  "success": true,
  "data": {
    "files": [ ... ],
    "total": 42,
    "page": 1,
    "limit": 20
  }
}

2.3 Phản hồi Lỗi

{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Loại tệp tin không hợp lệ cho danh mục RESUME"
  }
}

3. Schema

3.1 Tệp tin

TrườngLoạiBắt buộcMô tả
idstring (UUID)Định danh tệp tin duy nhất
employerIdstring (UUID)KhôngMã nhà tuyển dụng liên kết (null cho tệp tin cá nhân)
userIdstring (UUID)Mã người dùng chủ sở hữu
originalNamestringTên tệp tin gốc khi tải lên
filePathstringĐường dẫn tệp tin trên local filesystem
mimeTypestringLoại MIME (ví dụ: application/pdf)
fileSizenumber (bigint)Kích thước tệp tin bằng byte
categorystring (enum)RESUME, AVATAR, COMPANY_DOC, JOB_ATTACHMENT, OTHER
isPublicbooleanTệp tin có được truy cập công khai không
statusstring (enum)TEMP, ACTIVE, DELETED, REPLACED
createdAtdatetimeThời gian tạo
updatedAtdatetimeThời gian cập nhật lần cuối

3.2 Phản hồi URL Presigned

TrườngLoạiMô tả
idstring (UUID)Mã bản ghi tệp tin
originalNamestringTên tệp tin gốc
filePathstringĐường dẫn tệp tin trên filesystem

4. Endpoint Tệp tin

4.1 Tải lên Tệp tin (Local Filesystem)

POST /files/upload FR-001

Tải lên tệp tin qua multipart/form-data. Server lưu tệp tin vào local filesystem và tạo bản ghi metadata.

Yêu cầu: Content-Type: multipart/form-data

TrườngLoạiBắt buộcMô tả
filefileTệp tin cần tải lên
categorystring (enum)RESUME, AVATAR, COMPANY_DOC, JOB_ATTACHMENT, OTHER
employer_idstring (UUID)KhôngMã nhà tuyển dụng cho tệp tin theo phạm vi tổ chức

Quy tắc Xác thực:

Danh mụcCác loại MIME được phépKích thước Tối đa
RESUMEapplication/pdf, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document10 MB
AVATARimage/jpeg, image/png, image/webp5 MB
COMPANY_DOCimage/*, application/pdf10 MB
JOB_ATTACHMENTapplication/pdf, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document, image/*10 MB
OTHER*10 MB

Phản hồi 200:

{
  "success": true,
  "data": {
    "id": "file-uuid-123",
    "original_name": "my-resume.pdf",
    "file_path": "user-123/RESUME/a1b2c3d4-resume.pdf",
    "mime_type": "application/pdf",
    "file_size": 524288,
    "category": "RESUME",
    "status": "ACTIVE"
  }
}

4.2 Tải lên Hàng loạt

POST /files/batch FR-011

Tải lên nhiều tệp tin trong một yêu cầu duy nhất (multipart/form-data).

Yêu cầu: Content-Type: multipart/form-data

TrườngLoạiBắt buộcMô tả
filesfile[]Mảng các tệp tin cần tải lên
categorystring (enum)RESUME, AVATAR, COMPANY_DOC, JOB_ATTACHMENT, OTHER
employer_idstring (UUID)KhôngMã nhà tuyển dụng

Phản hồi 200:

{
  "success": true,
  "data": {
    "uploads": [
      { "id": "uuid-1", "original_name": "resume.pdf", "file_path": "..." },
      { "id": "uuid-2", "original_name": "avatar.jpg", "file_path": "..." }
    ],
    "errors": []
  }
}

4.3 Tải xuống Tệp tin

GET /files/:id/download FR-002

Tải xuống tệp tin từ local filesystem. Kiểm tra quyền truy cập trước khi trả về nội dung.

Tham số Đường dẫn:

Tham sốLoạiMô tả
idstring (UUID)Mã tệp tin

Phản hồi 200: File stream (Content-Type: application/octet-stream)

HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Disposition: attachment; filename="my-resume.pdf"
Content-Length: 524288

<binary file data>

Lỗi 403 — Từ chối truy cập:

{
  "success": false,
  "error": {
    "code": "FORBIDDEN",
    "message": "Bạn không có quyền truy cập tệp tin này"
  }
}

Lỗi 404 — Không tìm thấy tệp tin:

{
  "success": false,
  "error": {
    "code": "NOT_FOUND",
    "message": "Không tìm thấy tệp tin"
  }
}

4.5 Liệt kê Tệp tin

GET /files FR-003

Liệt kê tệp tin với lọc tùy chọn theo người dùng, nhà tuyển dụng và danh mục. Hỗ trợ phân trang.

Tham số Truy vấn:

Tham sốLoạiBắt buộcMô tả
user_idstring (UUID)KhôngLọc theo mã người dùng (mặc định là người dùng hiện tại)
employer_idstring (UUID)KhôngLọc theo mã nhà tuyển dụng
categorystring (enum)KhôngLọc theo danh mục (RESUME, AVATAR, COMPANY_DOC, JOB_ATTACHMENT, OTHER)
pageintegerKhôngSố trang (mặc định: 1)
limitintegerKhôngSố mục mỗi trang (mặc định: 20, tối đa: 100)

Ví dụ Yêu cầu:

GET /files?category=RESUME&page=1&limit=10

Phản hồi 200:

{
  "success": true,
  "data": {
    "files": [
      {
        "id": "file-uuid-123",
        "employer_id": "acme-corp-uuid",
        "user_id": "user-uuid-456",
        "original_name": "my-resume.pdf",
        "file_path": "user-123/RESUME/a1b2c3d4-resume.pdf",
        "file_path": "user-123/RESUME/a1b2c3d4-resume.pdf",
        "mime_type": "application/pdf",
        "file_size": 524288,
        "category": "RESUME",
        "is_public": false,
        "status": "ACTIVE",
        "created_at": "2026-08-17T10:45:00Z",
        "updated_at": "2026-08-17T10:45:00Z"
      }
    ],
    "total": 42,
    "page": 1,
    "limit": 10
  }
}

4.6 Tìm kiếm Tệp tin

GET /files/search FR-009

Tìm kiếm toàn văn trên tên và metadata tệp tin/thư mục.

Tham số Truy vấn:

Tham sốLoạiBắt buộcMô tả
qstringTruy vấn tìm kiếm
categorystringKhôngLọc theo danh mục
mime_typestringKhôngLọc theo loại MIME
pageintegerKhôngSố trang (mặc định: 1)
limitintegerKhôngSố mục mỗi trang (mặc định: 20)

Phản hồi 200:

{
  "success": true,
  "data": {
    "files": [ ... ],
    "total": 15,
    "page": 1,
    "limit": 20
  }
}

4.7 Tạo Thư mục

POST /files/folders FR-008

Tạo thư mục mới trong cấu trúc thư mục được ủy quyền.

Yêu cầu:

{
  "name": "My Documents",
  "parent_folder_id": "folder-uuid-123"
}

Phản hồi 200:

{
  "success": true,
  "data": {
    "id": "folder-uuid-456",
    "name": "My Documents",
    "parent_folder_id": "folder-uuid-123",
    "type": "FOLDER",
    "created_at": "2026-08-17T10:45:00Z"
  }
}

4.8 Đổi tên Tệp tin/Thư mục

PATCH /files/:id FR-010

Đổi tên tệp tin hoặc thư mục. Xử lý tên trùng lặp bằng cách thêm hậu tố số.

Yêu cầu:

{
  "name": "New Document Name"
}

Phản hồi 200:

{
  "success": true,
  "data": {
    "id": "file-uuid-123",
    "original_name": "New Document Name",
    "updated_at": "2026-08-17T10:50:00Z"
  }
}

4.9 Sao chép Tệp tin

POST /files/:id/copy FR-012

Sao chép tệp tin đến thư mục đích. Tạo bản ghi mới với cùng file vật lý.

Yêu cầu:

{
  "destination_folder_id": "folder-uuid-789"
}

Phản hồi 200:

{
  "success": true,
  "data": {
    "id": "file-uuid-new",
    "original_name": "document.pdf",
    "parent_folder_id": "folder-uuid-789",
    "file_path": "same-as-original",
    "created_at": "2026-08-17T10:55:00Z"
  }
}

4.10 Di chuyển Tệp tin

POST /files/:id/move FR-013

Di chuyển tệp tin đến thư mục đích. Cập nhật parent_folder_id.

Yêu cầu:

{
  "destination_folder_id": "folder-uuid-789"
}

Phản hồi 200:

{
  "success": true,
  "data": {
    "id": "file-uuid-123",
    "original_name": "document.pdf",
    "parent_folder_id": "folder-uuid-789",
    "updated_at": "2026-08-17T11:00:00Z"
  }
}

4.11 Xóa Tệp tin/Thư mục

DELETE /files/:id FR-004

Xóa mềm tệp tin hoặc thư mục. Đối với thư mục, đánh dấu đệ quy tất cả các mục con là DELETED.

Phản hồi 200:

{
  "success": true,
  "message": "Tệp tin/thư mục đã được xóa thành công"
}

4.12 Quản lý Quyền

GET /files/:id/permissions FR-014

Lấy tất cả quyền cho tệp tin hoặc thư mục.

Phản hồi 200:

{
  "success": true,
  "data": {
    "permissions": [
      { "id": "perm-uuid-1", "user_id": "user-uuid-1", "permission": "VIEW" },
      { "id": "perm-uuid-2", "role": "HR", "permission": "DOWNLOAD" },
      { "id": "perm-uuid-3", "org_id": "org-uuid-1", "permission": "EDIT" }
    ]
  }
}

PUT /files/:id/permissions FR-014

Cập nhật quyền cho tệp tin hoặc thư mục (chỉ admin).

Yêu cầu:

{
  "permissions": [
    { "user_id": "user-uuid-1", "permission": "VIEW" },
    { "role": "HR", "permission": "DOWNLOAD" },
    { "org_id": "org-uuid-1", "permission": "EDIT" }
  ]
}

Phản hồi 200:

{
  "success": true,
  "message": "Quyền đã được cập nhật thành công"
}

4.13 Quản lý Lưu trữ

GET /files/storage-usage FR-016

Lấy thống kê sử dụng lưu trữ cho người dùng hoặc tổ chức hiện tại.

Phản hồi 200:

{
  "success": true,
  "data": {
    "user": { "total_size": 52428800, "file_count": 25 },
    "organization": { "total_size": 524288000, "file_count": 250 }
  }
}

GET /files/quotas FR-017

Lấy cấu hình hạn ngạch lưu trữ (chỉ admin).

Phản hồi 200:

{
  "success": true,
  "data": {
    "quotas": [
      { "user_id": "user-uuid-1", "max_total_size": 104857600, "max_file_count": 100 },
      { "org_id": "org-uuid-1", "max_total_size": 1073741824, "max_file_count": 1000 }
    ]
  }
}

PUT /files/quotas FR-017

Cập nhật giới hạn hạn ngạch lưu trữ (chỉ admin).

Yêu cầu:

{
  "user_id": "user-uuid-1",
  "max_total_size": 209715200,
  "max_file_count": 200
}

GET /files/policies FR-018

Lấy chính sách lưu trữ theo danh mục (chỉ admin).

Phản hồi 200:

{
  "success": true,
  "data": {
    "policies": [
      {
        "category": "RESUME",
        "allowed_mime_types": ["application/pdf", "application/msword"],
        "max_file_size": 10485760,
        "retention_days": null,
        "encryption_enabled": false
      }
    ]
  }
}

PUT /files/policies FR-018

Cập nhật chính sách lưu trữ (chỉ admin).

Yêu cầu:

{
  "category": "RESUME",
  "allowed_mime_types": ["application/pdf", "application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"],
  "max_file_size": 10485760,
  "retention_days": 365,
  "encryption_enabled": true
}

5. Mã Lỗi

HTTP StatusMã LỗiMô tả
400VALIDATION_ERRORXác thực yêu cầu thất bại (thiếu/trường không hợp lệ, loại tệp tin không hợp lệ)
400INVALID_NAMETên tệp tin/thư mục chứa ký tự đặc biệt
400INVALID_FOLDERKhông thể di chuyển thư mục vào chính nó
401UNAUTHORIZEDThiếu hoặc xác thực không hợp lệ
403FORBIDDENKhông đủ quyền (không phải chủ sở hữu tệp tin, không phải admin tổ chức)
404NOT_FOUNDKhông tìm thấy tệp tin/thư mục hoặc đã bị xóa
409NAME_CONFLICTTệp tin/thư mục với tên tương tự đã tồn tại tại vị trí đích
413PAYLOAD_TOO_LARGETệp tin vượt quá kích thước tối đa cho danh mục
413QUOTA_EXCEEDEDVượt quá hạn ngạch lưu trữ cho người dùng hoặc tổ chức
415UNSUPPORTED_MEDIA_TYPELoại MIME tệp tin không được phép cho danh mục
429RATE_LIMITEDQuá nhiều yêu cầu
500INTERNAL_ERRORLỗi server (filesystem không khả dụng)