Nền tảng blog hiện đại với Vue 3 và Supabase: CRUD bài viết, xác thực người dùng, hệ thống bình luận, quản lý profile và phân quyền admin.
- 🔐 Xác thực người dùng - Đăng ký và đăng nhập bằng email/password
- 📄 Quản lý bài viết - Tạo, sửa, xóa bài viết (chỉ Admin)
- 💬 Hệ thống bình luận - Thêm bình luận vào bài viết
- 👤 Quản lý profile - Chỉnh sửa thông tin cá nhân và đổi mật khẩu
- 🎨 Thiết kế responsive - Bootstrap 5 với mobile-first design
- 🚀 Hiệu suất cao - Vite và Vue 3 Composition API
- ☁️ Database đám mây - Supabase PostgreSQL backend
- 🔔 Thông báo (toast) - vue-sonner cho UX mượt mà
- Frontend: Vue 3.5.24 (Composition API với
<script setup>) - Build Tool: Vite 6.0.3
- UI Framework: Bootstrap 5.3.8
- Database: Supabase (PostgreSQL)
- Routing: Vue Router 4.6.3
- Notifications: vue-sonner 2.0.9
- Icons: Font Awesome 6
- HTTP Client: @supabase/supabase-js
- Node.js (phiên bản 16 trở lên)
- npm hoặc yarn
- Tài khoản Supabase (có gói miễn phí)
git clone https://github.com/ndyudev/techtalk-blog.git
cd techtalk-blognpm install- Tạo project mới tại supabase.com
- Vào SQL Editor và chạy schema ở phần Thiết lập Database bên dưới
- Vào Settings → API để lấy credentials
Tạo file .env.local ở thư mục gốc:
VITE_SUPABASE_URL=your_supabase_project_url
VITE_SUPABASE_ANON_KEY=your_supabase_anon_keyThay thế bằng URL và anon key thực tế từ Supabase dashboard.
npm run devMở http://localhost:5173 trên trình duyệt.
Chạy SQL sau trong Supabase SQL Editor để tạo schema:
-- Tạo bảng Users
CREATE TABLE users (
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
email TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
name TEXT NOT NULL,
avatar TEXT,
bio TEXT,
role TEXT DEFAULT 'user',
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Tạo bảng Posts
CREATE TABLE posts (
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
title TEXT NOT NULL,
author_name TEXT NOT NULL,
content TEXT NOT NULL,
image TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Tạo bảng Comments
CREATE TABLE comments (
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
post_id TEXT REFERENCES posts(id) ON DELETE CASCADE,
author TEXT NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Bật Row Level Security
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
ALTER TABLE comments ENABLE ROW LEVEL SECURITY;
-- Tạo policies (public access cho demo)
CREATE POLICY "Public Access Users" ON users FOR ALL USING (true);
CREATE POLICY "Public Access Posts" ON posts FOR ALL USING (true);
CREATE POLICY "Public Access Comments" ON comments FOR ALL USING (true);Sau khi chạy schema, bạn có thể đăng nhập với:
- Email:
admin@gmail.com - Password:
123456
techtalk-blog/
├── src/
│ ├── assets/ # Tài nguyên tĩnh
│ ├── components/ # Component tái sử dụng
│ │ ├── Header.vue
│ │ └── Footer.vue
│ ├── config/ # File cấu hình
│ │ ├── supabase.js # Supabase client
│ │ └── api-service.js # Lớp trừu tượng API
│ ├── router/ # Cấu hình Vue Router
│ │ └── index.js
│ ├── views/ # Các trang
│ │ ├── Home.vue
│ │ ├── PostDetail.vue
│ │ ├── CreatePost.vue
│ │ ├── EditPost.vue
│ │ ├── Signin.vue
│ │ ├── Signup.vue
│ │ └── Profile.vue
│ ├── App.vue # Component gốc
│ └── main.js # Entry point
├── .env.local # Biến môi trường
├── index.html
├── package.json
└── vite.config.js
- Admin: Tạo, sửa, xóa bài viết
- User: Xem bài viết, thêm bình luận, chỉnh sửa profile
# Chạy development server
npm run dev
# Build production
npm run build
# Preview production build
npm run preview
# Lint code
npm run lintnpm run build
npm run previewDeploy tĩnh (Vercel/Netlify khuyến nghị):
# Deploy lên Vercel
npm install -g vercel
vercelNhớ cấu hình biến môi trường VITE_SUPABASE_URL và VITE_SUPABASE_ANON_KEY trên nền tảng deploy.
File: src/config/api-service.js
| Method | Mô tả |
|---|---|
getAll() |
Lấy tất cả users |
getByEmail(email) |
Tìm user theo email |
create(userData) |
Tạo user mới |
update(id, userData) |
Cập nhật user |
delete(id) |
Xóa user |
| Method | Mô tả |
|---|---|
getAll() |
Lấy tất cả posts (sắp xếp theo created_at desc) |
getById(id) |
Lấy post theo ID |
create(postData) |
Tạo post mới |
update(id, postData) |
Cập nhật post (tự động set updated_at) |
delete(id) |
Xóa post |
| Method | Mô tả |
|---|---|
getByPostId(postId) |
Lấy comments theo post_id |
create(commentData) |
Tạo comment mới |
delete(id) |
Xóa comment |
- Composition API: Sử dụng
<script setup>để code ngắn gọn hơn - Reactive State: Dùng
ref()cho reactive data,onMounted()cho lifecycle - LocalStorage: Lưu session user với key
userLogin - Toast Notifications: vue-sonner để thông báo thành công/lỗi
- Router Navigation: Programmatic routing với
router.push() - Supabase Client: Tất cả API calls đi qua abstraction layer
api-service.js
Duy Chau Nhat - ndyudev
- GitHub: https://github.com/ndyudev
MIT © 2025 ndyudev