Daniel Nguyen
← Tất cả dự án

FotoVerse: ứng dụng làm album ảnh cùng nhau

Dự án cá nhân để thiết kế album ảnh theo từng trang, mời người cùng làm và xuất bản. Tôi xây dựng Django REST API và trình chỉnh sửa React.

Side project
Vài người bạn đang dùngDự án riêng tư, web app + REST API
Mảng
Ứng dụng chia sẻ ảnh
Thời gian
2026
Vai trò
Full-stack developer (làm một mình)
  • Python
  • Django
  • Django REST Framework
  • PostgreSQL
  • Auth0
  • ImageKit
  • React
  • TypeScript
  • Render

Các phần ghép với nhau thế nào

FotoVerse: ứng dụng làm album ảnh cùng nhauThe album editor calls a layered REST API that checks the sign-in token, applies access rules in services, and stores soft-deleted rows and image links.Friend / editorngười dùngReact album editordịch vụIdentity providerbên ngoàiToken checkkiểm tra, cảnh báoREST APIdịch vụAccess ruleskiểm tra, cảnh báoPostgreSQLkho dữ liệuImage CDNbên ngoàidesign pagessign inbearer tokenverify signaturefetch signing keysservice layerUUID rows, soft deleteupload images
The album editor calls a layered REST API that checks the sign-in token, applies access rules in services, and stores soft-deleted rows and image links.
  • Người dùng
  • Dịch vụ
  • Bên ngoài
  • Kiểm tra, cảnh báo
  • Kho dữ liệu

Vấn đề

Tôi muốn có cách đơn giản để bạn bè cùng làm album ảnh. Mỗi album gồm nhiều trang, mỗi trang chứa ảnh và chữ đặt tự do trên canvas. Một người sở hữu album, người khác tham gia với vai trò editor hoặc viewer, và album làm xong có thể xuất bản để ai cũng xem được.

Nghe như một app CRUD nhỏ, nhưng quy tắc phân quyền lan ra rất nhanh. Bản nháp chỉ chủ sở hữu và collaborator thấy. Editor được sửa album, viewer thì không. Chỉ chủ sở hữu được xóa hoặc xuất bản. Ảnh thuộc về người tải lên. Nếu mỗi endpoint tự kiểm tra, chỉ cần quên một chỗ là lộ album riêng tư. Dự án để private và đang được vài người bạn sử dụng.

Tôi đã làm gì

Chia mỗi domain theo cùng một kiểu lớp

Backend có một module cho mỗi domain: album, trang, media, collaborator và user. Module nào cũng cùng cấu trúc. Repository ghi dữ liệu, selector đọc dữ liệu, service giữ quy tắc nghiệp vụ, view thì mỏng. Một base service generic cho mọi module cùng các thao tác cơ bản. Đánh đổi là nhiều file và một ít boilerplate cho app cỡ này. Bù lại, quy tắc nào cũng có chỗ rõ ràng, và thêm module mới rất nhanh.

Đặt quy tắc phân quyền ở tầng service

Mọi lần đọc album đều đi qua một phương thức service trả lời câu hỏi “user này có được xem album này không?”. Thứ tự kiểm tra: album đã xuất bản thì ai cũng xem được, nếu không thì user phải đăng nhập, sau đó chủ sở hữu được qua, rồi đến collaborator. Sửa, xóa và xuất bản có kiểm tra owner hoặc editor riêng, và ném ra lỗi domain để API chuyển thành response lỗi. Đánh đổi: database không tự áp các quy tắc này, nên đường code nào bỏ qua service là bỏ qua luôn bước kiểm tra.

Giao việc đăng nhập cho identity provider

Tôi không muốn lưu mật khẩu. Frontend đăng nhập qua Auth0, còn API xác thực token đã ký ở mỗi request: chọn đúng public key, kiểm tra chữ ký, audience, issuer và thời hạn, rồi tạo user nội bộ ở lần đăng nhập đầu, gắn với id người dùng của provider. Đánh đổi là phụ thuộc cứng vào provider. Bản đầu của tôi còn tải signing key ở mọi request, phần này tôi nói ở dưới.

Soft delete và khóa UUID cho mọi model

Mọi model dùng chung một lớp cơ sở với khóa chính UUID, timestamp và soft delete theo cascade. Xóa album sẽ ẩn các trang và liên kết của nó chứ không xóa hẳn, nên lỡ tay vẫn khôi phục được. UUID giúp id khó đoán trong link chia sẻ. Cái giá là dòng đã xóa vẫn nằm trong database và mọi query phải tôn trọng cờ đã xóa, việc này thư viện lo.

Deploy từ repository

Mỗi lần push lên main sẽ chạy một workflow sao chép cấu hình của app từ environment được bảo vệ của repository sang dịch vụ hosting, rồi kích hoạt deploy. Ảnh nằm trên image CDN; database chỉ lưu link và metadata. Đánh đổi là cấu hình giờ nằm ở hai nơi, với repository là nguồn chuẩn.

Kết quả

FotoVerse chạy dưới dạng web app React và TypeScript trên nền Django REST API, có trình chỉnh trang kéo thả và đổi kích thước, chế độ xem lật trang, vai trò collaborator và xuất bản. Vài người bạn đang dùng. Cấu trúc module theo lớp hoạt động đủ tốt để tôi dùng lại trong một dự án cá nhân sau đó.

Nếu làm lại

Hiện chưa có test tự động, trong khi quy tắc phân quyền chính là thứ đáng test đầu tiên, mỗi trường hợp owner, editor, viewer và người ẩn danh một test. Bước kiểm tra token tải signing key của provider ở mọi request, không cache và không timeout, nên provider chậm thì mọi request đều chậm; tôi sẽ cache key và chỉ làm mới khi gặp key id lạ. Một README đầy đủ cũng sẽ giúp người khác chạy được dự án.