# Progressive Web App (PWA) Guide (Tiếng Việt) 🌐 **Languages:** 🇺🇸 [English](../../../../guides/PWA_GUIDE.md) · 🇪🇹 [am](../../../am/docs/guides/PWA_GUIDE.md) · 🇸🇦 [ar](../../../ar/docs/guides/PWA_GUIDE.md) · 🇦🇿 [az](../../../az/docs/guides/PWA_GUIDE.md) · 🇧🇬 [bg](../../../bg/docs/guides/PWA_GUIDE.md) · 🇧🇩 [bn](../../../bn/docs/guides/PWA_GUIDE.md) · 🇧🇦 [bs](../../../bs/docs/guides/PWA_GUIDE.md) · 🇨🇿 [cs](../../../cs/docs/guides/PWA_GUIDE.md) · 🇩🇰 [da](../../../da/docs/guides/PWA_GUIDE.md) · 🇩🇪 [de](../../../de/docs/guides/PWA_GUIDE.md) · 🇬🇷 [el](../../../el/docs/guides/PWA_GUIDE.md) · 🇪🇸 [es](../../../es/docs/guides/PWA_GUIDE.md) · 🇪🇪 [et](../../../et/docs/guides/PWA_GUIDE.md) · 🇮🇷 [fa](../../../fa/docs/guides/PWA_GUIDE.md) · 🇫🇮 [fi](../../../fi/docs/guides/PWA_GUIDE.md) · 🇫🇷 [fr](../../../fr/docs/guides/PWA_GUIDE.md) · 🇮🇪 [ga](../../../ga/docs/guides/PWA_GUIDE.md) · 🇮🇳 [gu](../../../gu/docs/guides/PWA_GUIDE.md) · 🇳🇬 [ha](../../../ha/docs/guides/PWA_GUIDE.md) · 🇮🇱 [he](../../../he/docs/guides/PWA_GUIDE.md) · 🇮🇳 [hi](../../../hi/docs/guides/PWA_GUIDE.md) · 🇭🇷 [hr](../../../hr/docs/guides/PWA_GUIDE.md) · 🇭🇺 [hu](../../../hu/docs/guides/PWA_GUIDE.md) · 🇦🇲 [hy](../../../hy/docs/guides/PWA_GUIDE.md) · 🇮🇩 [id](../../../id/docs/guides/PWA_GUIDE.md) · 🇳🇬 [ig](../../../ig/docs/guides/PWA_GUIDE.md) · 🇮🇹 [it](../../../it/docs/guides/PWA_GUIDE.md) · 🇯🇵 [ja](../../../ja/docs/guides/PWA_GUIDE.md) · 🇬🇪 [ka](../../../ka/docs/guides/PWA_GUIDE.md) · 🇰🇭 [km](../../../km/docs/guides/PWA_GUIDE.md) · 🇮🇳 [kn](../../../kn/docs/guides/PWA_GUIDE.md) · 🇰🇷 [ko](../../../ko/docs/guides/PWA_GUIDE.md) · 🇱🇹 [lt](../../../lt/docs/guides/PWA_GUIDE.md) · 🇱🇻 [lv](../../../lv/docs/guides/PWA_GUIDE.md) · 🇮🇳 [ml](../../../ml/docs/guides/PWA_GUIDE.md) · 🇮🇳 [mr](../../../mr/docs/guides/PWA_GUIDE.md) · 🇲🇾 [ms](../../../ms/docs/guides/PWA_GUIDE.md) · 🇲🇹 [mt](../../../mt/docs/guides/PWA_GUIDE.md) · 🇲🇲 [my](../../../my/docs/guides/PWA_GUIDE.md) · 🇳🇵 [ne](../../../ne/docs/guides/PWA_GUIDE.md) · 🇳🇱 [nl](../../../nl/docs/guides/PWA_GUIDE.md) · 🇳🇴 [no](../../../no/docs/guides/PWA_GUIDE.md) · 🇮🇳 [or](../../../or/docs/guides/PWA_GUIDE.md) · 🇮🇳 [pa](../../../pa/docs/guides/PWA_GUIDE.md) · 🇵🇭 [phi](../../../phi/docs/guides/PWA_GUIDE.md) · 🇵🇱 [pl](../../../pl/docs/guides/PWA_GUIDE.md) · 🇵🇹 [pt](../../../pt/docs/guides/PWA_GUIDE.md) · 🇧🇷 [pt-BR](../../../pt-BR/docs/guides/PWA_GUIDE.md) · 🇷🇴 [ro](../../../ro/docs/guides/PWA_GUIDE.md) · 🇷🇺 [ru](../../../ru/docs/guides/PWA_GUIDE.md) · 🇱🇰 [si](../../../si/docs/guides/PWA_GUIDE.md) · 🇸🇰 [sk](../../../sk/docs/guides/PWA_GUIDE.md) · 🇸🇮 [sl](../../../sl/docs/guides/PWA_GUIDE.md) · 🇷🇸 [sr](../../../sr/docs/guides/PWA_GUIDE.md) · 🇸🇪 [sv](../../../sv/docs/guides/PWA_GUIDE.md) · 🇰🇪 [sw](../../../sw/docs/guides/PWA_GUIDE.md) · 🇮🇳 [ta](../../../ta/docs/guides/PWA_GUIDE.md) · 🇮🇳 [te](../../../te/docs/guides/PWA_GUIDE.md) · 🇹🇭 [th](../../../th/docs/guides/PWA_GUIDE.md) · 🇹🇷 [tr](../../../tr/docs/guides/PWA_GUIDE.md) · 🇺🇦 [uk-UA](../../../uk-UA/docs/guides/PWA_GUIDE.md) · 🇵🇰 [ur](../../../ur/docs/guides/PWA_GUIDE.md) · 🇺🇿 [uz](../../../uz/docs/guides/PWA_GUIDE.md) · 🇳🇬 [yo](../../../yo/docs/guides/PWA_GUIDE.md) · 🇨🇳 [zh-CN](../../../zh-CN/docs/guides/PWA_GUIDE.md) · 🇹🇼 [zh-TW](../../../zh-TW/docs/guides/PWA_GUIDE.md) --- OmniRoute được cung cấp dưới dạng Progressive Web App có thể cài đặt hoàn chỉnh. Khi truy cập bảng điều khiển từ bất kỳ trình duyệt di động nào — Android (Chrome) hoặc iOS (Safari) — bạn có thể chọn "Thêm vào màn hình chính" để có trải nghiệm tương tự ứng dụng gốc mà không cần thông qua cửa hàng ứng dụng. ## PWA là gì? Progressive Web App biến bảng điều khiển web của OmniRoute thành một ứng dụng có giao diện và trải nghiệm giống như ứng dụng di động gốc. Sau khi được cài đặt, ứng dụng sẽ: - Khởi chạy từ màn hình chính với biểu tượng riêng - Mở ở chế độ toàn màn hình — không có thanh địa chỉ hoặc giao diện tab của trình duyệt - Hoạt động ngoại tuyến với một trang kết nối chuyên dụng - Lưu các tài nguyên tĩnh vào bộ nhớ đệm để tải nhanh hơn - Hỗ trợ cả hướng màn hình dọc và ngang ## Cài đặt ### Android (Chrome) 1. Mở bảng điều khiển OmniRoute trong Chrome: `http://YOUR_IP:20128` 2. Chrome sẽ tự động hiển thị biểu ngữ **"Thêm OmniRoute vào Màn hình chính"**, hoặc: - Nhấn vào menu **⋮** (ba dấu chấm) → **"Thêm vào Màn hình chính"** hoặc **"Cài đặt ứng dụng"** 3. Xác nhận lời nhắc 4. OmniRoute xuất hiện trên màn hình chính dưới dạng một ứng dụng độc lập ### iOS (Safari) 1. Mở bảng điều khiển OmniRoute trong Safari: `http://YOUR_IP:20128` 2. Nhấn vào nút **Chia sẻ** (hộp có mũi tên) 3. Cuộn xuống và nhấn vào **"Thêm vào Màn hình chính"** 4. Đặt tên cho ứng dụng (mặc định là "OmniRoute") và nhấn **Thêm** 5. OmniRoute xuất hiện trên màn hình chính với biểu tượng ứng dụng ### Máy tính để bàn (Chrome / Edge) 1. Mở bảng điều khiển OmniRoute 2. Nhấp vào **biểu tượng cài đặt** trên thanh địa chỉ (hoặc ⋮ → "Cài đặt OmniRoute...") 3. Xác nhận lời nhắc 4. OmniRoute mở dưới dạng một cửa sổ độc lập — không có tab, không có thanh địa chỉ ## Tính năng ### Trải nghiệm toàn màn hình Tệp manifest được cấu hình với `display: "fullscreen"`, nghĩa là ứng dụng đã cài đặt sẽ sử dụng toàn bộ màn hình — không có giao diện trình duyệt và không chồng lấn lên thanh trạng thái. Điều này mang lại cho bảng điều khiển cảm giác thực sự giống ứng dụng gốc. ### Hỗ trợ ngoại tuyến OmniRoute bao gồm một service worker (`sw.js`) cung cấp cơ chế lưu vào bộ nhớ đệm thông minh: | Loại tài nguyên | Chiến lược | Hành vi | | ------------------------------------------------------------------- | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | | **Khung ứng dụng** | Ưu tiên bộ nhớ đệm | `/`, `/offline`, manifest và các biểu tượng được lưu sẵn vào bộ nhớ đệm khi cài đặt | | **Tài nguyên tĩnh** (CSS, JS, hình ảnh, phông chữ) | Ưu tiên mạng, dự phòng bằng bộ nhớ đệm | Tải bản mới từ mạng; chuyển sang bộ nhớ đệm nếu ngoại tuyến | | **Các gói Next.js** (`/_next/`) | Ưu tiên mạng và cập nhật bộ nhớ đệm | Tải từ mạng và cập nhật bộ nhớ đệm; cung cấp phiên bản đã lưu nếu ngoại tuyến | | **Yêu cầu điều hướng** | Bỏ qua (không bao giờ bị chặn) | Trình duyệt xử lý việc chuyển đổi dự phòng HTTP/3→HTTP/2; socket QUIC ngắt kết nối không được trở thành `Response.error()` | | **API / các tuyến bảng điều khiển** (`/api/`, `/a2a`, `/dashboard`) | Bỏ qua (không bao giờ được lưu vào bộ nhớ đệm) | Luôn truy cập trực tiếp vào máy chủ — service worker không bao giờ chặn | ### Trang ngoại tuyến Khi mạng không khả dụng và người dùng điều hướng đến một trang mới, service worker sẽ cung cấp trang `/offline` chuyên dụng: - Hiển thị thông báo rõ ràng **"Sự cố kết nối"** - Hiển thị **chỉ báo trạng thái trực tuyến/ngoại tuyến** theo thời gian thực - Cung cấp nút **"Thử kết nối lại"** để tải lại khi kết nối được khôi phục - Liên kết đến **Trang trạng thái** để chẩn đoán ### Biểu tượng ứng dụng OmniRoute cung cấp các biểu tượng được tối ưu hóa cho từng nền tảng: | Tệp | Kích thước | Được sử dụng bởi | | ---------------------- | ---------------- | -------------------------------------------- | | `icon-512.png` | 512×512 | Lời nhắc cài đặt Android, màn hình khởi động | | `apple-touch-icon.png` | 180×180 | Biểu tượng trên màn hình chính của iOS | | `icon-192.svg` | 192×192 (vector) | Biểu tượng thích ứng của Android | | `apple-touch-icon.svg` | 180×180 (vector) | Phương án dự phòng cho Apple | | `favicon.svg` | Vector | Các tab trình duyệt | | `favicon.ico` | Nhiều kích thước | Các trình duyệt cũ | ### Đăng ký tự động Service worker được đăng ký tự động thông qua thành phần `` trong bố cục gốc. Người dùng không cần thực hiện bất kỳ thao tác nào — ứng dụng có thể được cài đặt ngay khi trình duyệt phát hiện manifest và service worker hợp lệ. ## Kiến trúc kỹ thuật ### Web App Manifest (`manifest.webmanifest`) Được Next.js tạo thông qua `src/app/manifest.ts`: ```json { "name": "OmniRoute", "short_name": "OmniRoute", "description": "OmniRoute is an AI gateway for multi-provider LLMs. One endpoint for all your AI providers.", "start_url": "/", "scope": "/", "display": "fullscreen", "orientation": "any", "background_color": "#0b0f1a", "theme_color": "#0b0f1a", "icons": [ { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable" }, { "src": "/apple-touch-icon.png", "sizes": "180x180", "type": "image/png" } ] } ``` ### Service Worker (`public/sw.js`) Một service worker thuần túy (không phụ thuộc framework) với: - **Giai đoạn cài đặt**: Lưu trước app shell vào bộ nhớ đệm (trang gốc, trang ngoại tuyến, manifest, biểu tượng) - **Giai đoạn kích hoạt**: Dọn dẹp các phiên bản bộ nhớ đệm cũ và tiếp quản tất cả client - **Giai đoạn tìm nạp**: Định tuyến thông minh dựa trên loại yêu cầu (điều hướng, tài nguyên tĩnh, API) - **Đánh phiên bản bộ nhớ đệm**: `omniroute-pwa-v3` — tăng giá trị này để buộc tạo bộ nhớ đệm mới khi cập nhật ### Metadata của layout (`src/app/layout.tsx`) Layout gốc cung cấp tất cả các thẻ meta cần thiết để tuân thủ PWA: - Liên kết `manifest` đến `/manifest.webmanifest` - `apple-web-app-capable: true` cho chế độ độc lập trên iOS - `apple-web-app-status-bar-style: black-translucent` - `mobile-web-app-capable: yes` cho Chrome trên Android - `theme-color: #0b0f1a` - `viewport-fit: cover` để hiển thị tràn viền ### Component: `PwaRegister` Nằm tại `src/shared/components/PwaRegister.tsx`, client component này: 1. Chạy khi được mount (chỉ ở phía client) 2. Kiểm tra trình duyệt có hỗ trợ `serviceWorker` hay không 3. Đăng ký `/sw.js` một cách âm thầm (các lỗi được bỏ qua để tránh chặn ứng dụng) 4. Không render nội dung nào (`return null`) — đây là component chỉ dùng để tạo hiệu ứng phụ ## Sử dụng với Termux (Android) Khi chạy OmniRoute trên Android thông qua Termux, PWA hoạt động liền mạch: 1. Khởi động OmniRoute trong Termux: `npx omniroute` 2. Mở Chrome trên cùng điện thoại: `http://localhost:20128` 3. Cài đặt PWA thông qua "Thêm vào màn hình chính" 4. PWA kết nối với máy chủ Termux cục bộ — mọi thứ đều chạy trên thiết bị Sự kết hợp này có nghĩa là điện thoại Android của bạn vừa là **máy chủ** (Termux), vừa là **client** (PWA) — một cổng AI hoàn chỉnh và khép kín. ## Sử dụng từ các thiết bị khác Cài đặt PWA trên bất kỳ thiết bị nào có thể truy cập máy chủ OmniRoute của bạn bằng trình duyệt: - **Điện thoại/máy tính bảng khác**: Truy cập `http://PHONE_IP:20128` và cài đặt PWA - **Máy tính xách tay**: Mở Chrome/Edge và cài đặt ứng dụng dưới dạng PWA dành cho máy tính - **Smart TV có trình duyệt**: Truy cập bảng điều khiển ở chế độ toàn màn hình ## Tùy chỉnh ### Tên phiên bản Tiêu đề PWA tuân theo cài đặt **Tên phiên bản** trong `Dashboard → Settings`. Nếu đổi tên phiên bản thành "Cổng AI của tôi", PWA đã cài đặt sẽ hiển thị tên đó. ### Favicon tùy chỉnh Nếu tải lên favicon tùy chỉnh thông qua `Dashboard → Settings`, biểu tượng PWA trên máy tính sẽ phản ánh biểu tượng tùy chỉnh đó. Các biểu tượng trên màn hình chính của thiết bị di động sử dụng các tệp `icon-512.png` và `apple-touch-icon.png` được tạo sẵn. ## Hạn chế - **Không có thông báo đẩy** — Service worker không triển khai Push API. Thay vào đó, các thông báo được xử lý bởi ứng dụng Electron. - **Không đồng bộ trong nền** — Các thao tác ngoại tuyến không được đưa vào hàng đợi để thực hiện lại. PWA chủ yếu được dùng để xem bảng điều khiển. - **Hạn chế trên iOS** — Safari trên iOS không hỗ trợ đầy đủ mọi tính năng của PWA (ví dụ: người dùng phải cài đặt thủ công và service worker chạy nền bị hạn chế). - **Kích thước bộ nhớ đệm** — Service worker chỉ lưu các tài nguyên tĩnh vào bộ nhớ đệm. Các tải trọng phản hồi lớn từ những tuyến `/api/` không bao giờ được lưu vào bộ nhớ đệm. - **Biểu tượng tùy chỉnh trên thiết bị di động** — Việc thay đổi favicon trong phần cài đặt không cập nhật biểu tượng trên màn hình chính của thiết bị di động (điều này yêu cầu tạo lại các biểu tượng PWA). ## Tham chiếu tệp | Tệp | Mục đích | | --------------------------------------- | --------------------------------------------------------------------- | | `src/app/manifest.ts` | Tuyến manifest của Next.js (tạo `manifest.webmanifest`) | | `public/sw.js` | Service worker với logic lưu vào bộ nhớ đệm | | `src/shared/components/PwaRegister.tsx` | Thành phần phía máy khách đăng ký service worker | | `src/app/offline/page.tsx` | Trang dự phòng ngoại tuyến với chỉ báo trạng thái theo thời gian thực | | `src/app/layout.tsx` | Bố cục gốc với siêu dữ liệu PWA (apple-web-app, theme-color, v.v.) | | `public/icon-512.png` | Biểu tượng PNG 512×512 (Android, màn hình khởi động) | | `public/apple-touch-icon.png` | Biểu tượng PNG 180×180 (màn hình chính iOS) | | `public/icon-192.svg` | Biểu tượng SVG 192×192 (biểu tượng thích ứng trên Android) | | `public/apple-touch-icon.svg` | Biểu tượng SVG 180×180 (biểu tượng dự phòng cho Apple) |