- TypeScript 92.9%
- JavaScript 5.9%
- CSS 0.7%
- Dockerfile 0.5%
- /vi/api/health route + Dockerfile HEALTHCHECK (auto-restart khi treo) - ItemList + Breadcrumb JSON-LD cho blog / sản phẩm / giải pháp / tuyển dụng - logo khách hàng <img> → next/image (khach-hang) - data-umami-event cta_book_consult cho nút Đặt lịch (section-cta + sản phẩm + giải pháp) - cảnh báo khi NEXT_PUBLIC_SERVER_URL ≠ https://tetra.vn ở prod |
||
|---|---|---|
| .gitea/workflows | ||
| .vscode | ||
| content | ||
| docs | ||
| public | ||
| scripts | ||
| src | ||
| tests/int | ||
| .env.example | ||
| .env.production.example | ||
| .gitignore | ||
| .npmrc | ||
| .prettierrc.json | ||
| .yarnrc | ||
| docker-compose.dev.yml | ||
| docker-compose.dokploy.yml | ||
| docker-compose.yml | ||
| Dockerfile | ||
| eslint.config.mjs | ||
| lighthouserc.json | ||
| next.config.ts | ||
| package.json | ||
| pnpm-lock.yaml | ||
| postcss.config.mjs | ||
| README.md | ||
| test.env | ||
| tsconfig.json | ||
| vitest.config.mts | ||
| vitest.setup.ts | ||
Tetra Software — Website
Website doanh nghiệp của Tetra Software: song ngữ (VI/EN), tối ưu SEO, quản trị nội dung bằng CMS, tự host bằng Docker. Xây dựng trên Next.js 16 (App Router) và Payload CMS 3.
Tổng quan
- Song ngữ VI/EN với định tuyến theo locale (
/vi,/en) vàhreflangchuẩn. - Quản trị nội dung qua Payload CMS tại
/admin— sản phẩm, giải pháp, blog, khách hàng, tuyển dụng, form liên hệ, cấu hình header/footer. - SEO bài bản: title/description tối ưu, JSON-LD (Organization, Website, SoftwareApplication, Breadcrumb, BlogPosting, JobPosting),
sitemap.xml,robots.txt,llms.txt, OpenGraph/Twitter card. - Tự chủ dữ liệu: chạy được hoàn toàn trên hạ tầng của bạn (Docker + PostgreSQL), dữ liệu nằm trong volume riêng.
Công nghệ
| Lớp | Thành phần |
|---|---|
| Frontend | Next.js 16 (App Router), React 19, TypeScript |
| CMS / Backend | Payload CMS 3, PostgreSQL 17 |
| Giao diện | Tailwind CSS 4 (theme tối), Motion, Lucide |
| i18n | next-intl (VI/EN) |
| Plugin Payload | SEO, Form Builder, Redirects, Email (Nodemailer) |
| Kiểm thử | Vitest |
Tài liệu (docs/)
| File | Nội dung |
|---|---|
| runbook-cloudflare-dokploy.md | Runbook tổng dựng site marketing: Dokploy/Traefik + Cloudflare (CDN, SSL, mở khoá AI crawler/AEO, cache, real-IP, gotchas, checklist 9 bước) |
| deploy-dokploy.md | Chi tiết deploy Dokploy: env vars, content/media snapshot, headless API, schema qua migrate.cjs |
| lighthouse-nextjs-payload.md | Cách tối ưu Lighthouse cho stack Next.js + Payload (TTFB/cache, LCP, TBT, CLS, images, checklist) |
| perf-log.md | Nhật ký số đo Lighthouse trên prod (qua mạng/CDN) — kiểm soát chất lượng |
| lighthouse-baseline.md | Baseline Lighthouse đo local (app cô lập) — 2026-06-10 |
| seo-and-content-guide.md | Kỹ thuật SEO + seed nội dung lexical (Payload) |
| analytics-umami.md | Cài & dùng Umami (analytics cookieless self-host) |
Yêu cầu
- Node.js ≥ 22 (bắt buộc — bản thấp hơn sẽ báo lỗi engine)
- pnpm 9 hoặc 10
- Docker (cho PostgreSQL ở local và khi triển khai)
Phát triển (local)
# 1. Khởi động PostgreSQL
docker compose -f docker-compose.dev.yml up -d
# 2. Tạo .env từ mẫu (đặt PAYLOAD_SECRET)
cp .env.example .env
# 3. Cài đặt & chạy
pnpm install
pnpm dev # http://localhost:3000
# 4. (Tuỳ chọn) seed dữ liệu mẫu + tài khoản admin
pnpm seed # tạo admin từ ADMIN_EMAIL/ADMIN_PASSWORD trong .env
- Website: http://localhost:3000 (mặc định chuyển sang
/vi) - Admin CMS: http://localhost:3000/admin
- Đổi ngôn ngữ: nút VI/EN trên header
Lệnh
| Lệnh | Mô tả |
|---|---|
pnpm dev |
Chạy dev server |
pnpm build |
Build production |
pnpm start |
Chạy bản build |
pnpm test |
Chạy test (Vitest) |
pnpm seed |
Seed dữ liệu mẫu (idempotent — chạy lại để cập nhật) |
pnpm lint |
Kiểm tra ESLint |
pnpm generate:types |
Sinh lại src/payload-types.ts từ schema CMS |
pnpm generate:importmap |
Sinh lại import map của admin |
Nội dung & SEO
Nội dung chia hai loại:
- Tĩnh (trang Giới thiệu, Văn hóa, trang sản phẩm chủ lực…) → trong i18n messages:
src/messages/{vi,en}.json. Sửa nội dung không cần đụng database. - Động (sản phẩm, giải pháp, blog, khách hàng, tuyển dụng) → trong Payload CMS, sửa tại
/admin.
Khi seed blog, nội dung được viết bằng Markdown rồi chuyển sang lexical bằng converter chính thức của Payload (convertMarkdownToLexical) — không build JSON lexical bằng tay.
Hướng dẫn kỹ thuật SEO và cách viết nội dung: xem
docs/seo-and-content-guide.md.
Cấu trúc
src/
app/(frontend)/[locale]/… Trang công khai (song ngữ)
app/(payload)/… Admin + REST/GraphQL API của Payload
collections/ Products, Solutions, Posts, Customers, Jobs, Categories, Media, Users
globals/ Header, Footer, SiteSettings
components/, lib/ UI và tiện ích (SEO, structured data, images…)
messages/ Bản dịch VI/EN
app/sitemap.ts, robots.ts SEO/AEO
docs/ Tài liệu (SEO guide, specs)
scripts/seed.ts Seed dữ liệu mẫu
db/init/01-schema.sql Schema khởi tạo cho PostgreSQL
Triển khai production (self-host bằng Docker)
# Trên server:
# 1. Tạo .env từ mẫu production và điền giá trị thật
cp .env.production.example .env
# 2. Build & chạy (app + postgres + volume media)
docker compose up -d --build
# Lần đầu DB rỗng: Postgres tự áp dụng db/init/01-schema.sql để tạo toàn bộ bảng.
# 3. Tạo tài khoản admin đầu tiên: mở https://<domain>/admin
Schema:
db/init/01-schema.sqlchạy đúng một lần khi khởi tạo volume PostgreSQL rỗng. Khi đổi model dữ liệu, cập nhật file này (hoặc dùng migration của Payload) rồi áp dụng cho DB hiện có.
- App lắng nghe cổng
3000— đặt Nginx/Caddy phía trước để xử lý HTTPS và proxy vềapp:3000. - Dữ liệu nằm trên server: PostgreSQL ở volume
pgdata, file media ở volumemedia(có thể đặt server tại Việt Nam để tự chủ dữ liệu). - Biến môi trường: xem
.env.production.example.
Hạ tầng production thực tế (tetra.vn)
Phần trên là cách self-host generic. Đây là sơ đồ hạ tầng đang chạy thật cho
tetra.vn.
Người dùng ──► Cloudflare (CDN/edge, TLS) ──► Traefik (Dokploy) ──► Next.js app ──► PostgreSQL
(proxy: tetra.vn, www) (160.25.166.20) (container) (volume)
Hosting & deploy
- Dokploy tại
wedeploy.carp.vnquản lý Docker Compose (docker-compose.dokploy.yml); projecttetra-software, serviceweb. Reverse proxy là Traefik (của Dokploy) + Let's Encrypt cho origin. Server IP160.25.166.20. - Deploy headless qua REST API:
node scripts/dokploy.mjs deploy|status|reinit(token trong.env). Nếu Nodefetchlỗi mạng tới wedeploy, drive bằngcurltới/api/compose.deploy(xemdocs/memory). - Đổi schema đi qua
scripts/migrate.cjs(chạy deploy-time, trướcserver.js, idempotent, fail-safe) — KHÔNG chạy DDL trongonInit.
DNS, SSL & CDN (Cloudflare)
- Nameserver: Cloudflare (zone
tetra.vn, gói Free). SSL/TLS mode = Full; Universal SSL (edge) Active. - Proxy/CDN bật cho:
tetra.vn(apex, A → 160.25.166.20) vàwww.tetra.vn.www→ 301 redirect về apex (Redirect Rule "WWW to root"). Mọi record khác để DNS-only (eoffice, staging, ops, id, smartca… và các app tự cấp LE riêng). - Mail: Zoho (record MX
mx.zoho.com… — DNS-only, không proxy). - AI crawlers: ALLOW — tắt "Block AI bots" + "AI Labyrinth" trong Cloudflare → Security → Settings → Bot traffic (để AI engine index & trả lời về Tetra; xem mục AEO).
- Lưu ý gia hạn cert: apex proxied → LE renewal qua TLS-ALPN sẽ fail, nhưng Full mode bao dung. Để chuẩn hoá: cài Cloudflare Origin Certificate vào Dokploy + chuyển Full (Strict) (tuỳ chọn hardening).
Nội dung (content sync)
content/là snapshot JSON +content/mediacủa toàn bộ dữ liệu CMS (clone + seed dựng lại đủ).pnpm content pull|push [--prod]đồng bộ DB ↔content/. Sửa qua admin/prod →content pull --prodrồi commit.
Hiệu năng
- Content reads được cache (
unstable_cache300s,src/lib/payload.ts) → giảm tải DB; nội dung mới trễ tối đa ~5 phút. - Cloudflare cache static (
/_next/static, ảnh, font) ở edge + TLS gần người dùng. Log Lighthouse:docs/perf-log.md.
SEO / AEO (AI-answerability)
sitemap.xml,robots.txt, structured data (Organization, WebSite, SoftwareApplication, ItemList, FAQPage, BlogPosting, JobPosting, Breadcrumb)./llms.txt+/llms-full.txt(cơ sở tri thức cho LLM), trang FAQ (/cau-hoi-thuong-gap,/faq) — nguồn Q&A 1 chỗ ởsrc/lib/knowledge.ts.- IndexNow:
node scripts/indexnow.mjsbáo Bing/Yandex sau khi đổi nội dung (key file ởpublic/<key>.txt,INDEXNOW_KEYtrong.env). - Google Search Console: cần verify domain + submit sitemap (token vào SiteSettings →
googleSiteVerification).
Analytics: Umami self-host tại analytics.tetra.vn (project Dokploy riêng, cookieless).
📘 Runbook tái sử dụng: docs/runbook-cloudflare-dokploy.md — tổng hợp đầy đủ kỹ thuật + setting Dokploy/Traefik & Cloudflare (CDN, SSL, mở khoá AI crawler cho AEO, cache rules, gotchas, checklist) để dựng đúng cho website marketing mới.
⚡ Tối ưu Lighthouse: docs/lighthouse-nextjs-payload.md — kỹ thuật tối ưu Performance/A11y/Best-Practices/SEO cho stack Next.js + Payload (TTFB/cache, LCP, TBT, CLS, images, checklist). Log số đo: docs/perf-log.md.
Sản phẩm
Website giới thiệu tổng quan hệ sinh thái Tetra và dẫn sang subdomain riêng của từng sản phẩm:
- Tetra eOffice (chủ lực) — nền tảng văn phòng số hợp nhất: công văn, tài liệu, quy trình, nhân sự, ký số.
- Molly — số hóa & rút trích dữ liệu có cấu trúc bằng AI chạy on-premise.
- Otter Test — nền tảng thi & đào tạo nội bộ (mã nguồn mở MIT).
- Orca WAAP — bảo vệ ứng dụng web & API (WAAP, self-host).
- Manta Security — quản lý thiết bị & bảo mật mạng doanh nghiệp.
- Goby Care — quản lý trung tâm bảo hành & sửa chữa.