feat(docs): finish docs service frontend switch
This commit is contained in:
parent
9a822a5874
commit
986985a63d
@ -13,19 +13,20 @@
|
||||
|
||||
The frontend is built in GitHub Actions and shipped as a prebuilt `linux/amd64` image. The host only pulls the image and starts containers; it does not build locally.
|
||||
|
||||
`yarn prebuild` bundles the docs, blog, and static content needed by the console. During that phase the CI container runs `scripts/sync-doc-content.sh` (pulling docs from this repo plus `accounts.svc.plus`, `rag-server.svc.plus`, and `postgresql.svc.plus`) and `scripts/sync-blog-content.sh` (cloning `https://github.com/cloud-neutral-workshop/knowledge.git`), so the `knowledge/` directory and all documentation assets already live inside the image before the runtime stage begins.
|
||||
`yarn prebuild` now generates only console-owned marketing artifacts. `/docs` and `/blogs` no longer bundle `knowledge/` or synced markdown content into the frontend image. Those routes fetch rendered content from `docs.svc.plus` at request time through the server-side `docsServiceClient`.
|
||||
|
||||
The stack is static-first:
|
||||
|
||||
- Caddy serves `/_next/static/*` and public assets from a shared read-only volume.
|
||||
- The Next.js standalone container serves dynamic HTML, auth endpoints, and API proxy routes. Static assets and hashed CSS/JS files are extracted by the `frontend-assets` helper task, so the runtime no longer needs to compile anything on the single-node host.
|
||||
- `knowledge/` and the synced docs/blog assets are copied into the image during the Docker build via the GitHub Actions workflow.
|
||||
- `docs.svc.plus` is the source of truth for rendered docs/blog pages; the browser does not call it directly.
|
||||
|
||||
Releases are orchestrated through `.github/workflows/service_release_frontend-deploy.yml`. That workflow clones the knowledge repository, runs the Docker build/push sequence, renders `.env.runtime`, and ships `docker-compose.yml`, `Caddyfile`, and the runtime env file to the host. The control-plane workflow `.github/workflows/service_release_apiserver-deploy.yml` then updates Cloudflare DNS for the release domain (via `scripts/github-actions/update-release-dns.sh`) so `cn.svc.plus` and the redirected alias `cn.onwalk.net` point at the new environment.
|
||||
Releases are orchestrated through `.github/workflows/service_release_frontend-deploy.yml`. That workflow builds/pushes the image, renders `.env.runtime` including `DOCS_SERVICE_URL` / `DOCS_SERVICE_INTERNAL_URL`, and ships `docker-compose.yml`, `Caddyfile`, and the runtime env file to the host. The control-plane workflow `.github/workflows/service_release_apiserver-deploy.yml` then updates Cloudflare DNS for the release domain (via `scripts/github-actions/update-release-dns.sh`) so `cn.svc.plus` and the redirected alias `cn.onwalk.net` point at the new environment.
|
||||
|
||||
This baseline is intentional for the weak-IO single-node host (47.120.61.35). No images are built on the target machine, keeping the deployment lightweight: the host only logs into GHCR, pulls the `dashboard` image, extracts assets into `frontend_static`, and starts `dashboard` plus `caddy` containers via `docker compose`.
|
||||
|
||||
If `docs.svc.plus` is later refactored into a dedicated API service, revisit this writeup (and the runbook) so the GitHub Actions pipeline only bundles the API payloads that belong to that new service.
|
||||
`docs.svc.plus` is now the dedicated docs/blog service for the frontend delivery path.
|
||||
|
||||
## Related Docs
|
||||
|
||||
|
||||
@ -17,19 +17,15 @@ The production frontend is deployed as a prebuilt container image from GitHub Ac
|
||||
- The target host does not build images locally.
|
||||
- The workflow builds an `linux/amd64` image and pushes it to `ghcr.io/<owner>/dashboard:<sha>`.
|
||||
- The host only performs `docker login`, `docker compose pull`, static asset extraction, and `docker compose up`.
|
||||
- `knowledge/` is cloned during CI build (via `scripts/sync-blog-content.sh`) and synced with other docs (via `scripts/sync-doc-content.sh`) before being packed into the image.
|
||||
- `/docs` and `/blogs` fetch their content from `docs.svc.plus` at runtime; the frontend image no longer packs `knowledge/` or synced markdown payloads.
|
||||
- Static assets are extracted from the image into a shared Docker volume so Caddy can serve `/_next/static/*` and checked-in public files directly.
|
||||
|
||||
This is intentionally static-first for the current weak-IO single-node host. Dynamic HTML, auth routes, and API proxy routes still run through the Next.js container. When `docs.svc.plus` is later split into an API/service, revisit this runbook and remove docs content from the frontend image.
|
||||
This is intentionally static-first for the current weak-IO single-node host. Dynamic HTML, auth routes, and API proxy routes still run through the Next.js container, but docs/blog content delivery is now delegated to `docs.svc.plus`.
|
||||
|
||||
## Control Plane & DNS Stage
|
||||
|
||||
The control repo (`github-org-x-evor`) tracks `console.svc.plus` through `console.svc.plus.code-workspace` and keeps the `subrepos/accounts.svc.plus` pointer in sync via `skills/cross-repo-upstream-submodule-sync`. Releases resolve metadata with that workspace and the `config/single-node-release` manifests. After `.github/workflows/service_release_frontend-deploy.yml` finishes pushing the new image, the control-plane workflow `.github/workflows/service_release_apiserver-deploy.yml` calls `scripts/github-actions/update-release-dns.sh` to update Cloudflare DNS so the new endpoint is reachable under `cn.svc.plus` and `cn.onwalk.net`.
|
||||
|
||||
## Future Docs Strategy
|
||||
|
||||
Because the frontend currently ships docs content directly (knowledge/blog + rendered markdown), any future split where `docs.svc.plus` becomes an API-backed service should include a repo-level migration plan: stop syncing docs into the frontend image, move documentation storage/serving into the dedicated API, and adjust the runbook/workflow notes above accordingly.
|
||||
|
||||
## Runtime Layout
|
||||
|
||||
Remote directory:
|
||||
@ -83,6 +79,8 @@ Repository/environment variables recommended:
|
||||
- `NEXT_PUBLIC_SERVER_SERVICE_URL`
|
||||
- `RUNTIME_HOSTNAME`
|
||||
- `DEPLOYMENT_HOSTNAME`
|
||||
- `DOCS_SERVICE_URL`
|
||||
- `DOCS_SERVICE_INTERNAL_URL`
|
||||
- `NEXT_PUBLIC_RUNTIME_ENVIRONMENT`
|
||||
- `NEXT_PUBLIC_RUNTIME_REGION`
|
||||
- `NEXT_PUBLIC_GISCUS_*`
|
||||
@ -94,14 +92,13 @@ Repository/environment variables recommended:
|
||||
## Release Flow
|
||||
|
||||
1. GitHub Actions checks out the repo.
|
||||
2. GitHub Actions clones `knowledge/`.
|
||||
3. Docker builds the frontend image with the public `NEXT_PUBLIC_*` values needed at build time.
|
||||
4. The image is pushed to GHCR.
|
||||
5. The workflow runs a matrix DNS stage, updating one public domain per job.
|
||||
6. The workflow renders `.env.runtime`.
|
||||
7. The workflow uploads `docker-compose.yml`, `Caddyfile`, and `.env.runtime` to the host.
|
||||
8. The host pulls the new image, refreshes the static asset volume, and starts `dashboard + caddy`.
|
||||
9. The workflow verifies `cn.svc.plus` and `cn.onwalk.net`.
|
||||
2. Docker builds the frontend image with the public `NEXT_PUBLIC_*` values needed at build time.
|
||||
3. The image is pushed to GHCR.
|
||||
4. The workflow runs a matrix DNS stage, updating one public domain per job.
|
||||
5. The workflow renders `.env.runtime`, including docs service runtime endpoints.
|
||||
6. The workflow uploads `docker-compose.yml`, `Caddyfile`, and `.env.runtime` to the host.
|
||||
7. The host pulls the new image, refreshes the static asset volume, and starts `dashboard + caddy`.
|
||||
8. The workflow verifies `cn.svc.plus` and `cn.onwalk.net`.
|
||||
|
||||
## Verification Commands
|
||||
|
||||
|
||||
@ -13,19 +13,19 @@
|
||||
|
||||
前端镜像在 GitHub Actions 中完成构建并推送到镜像仓库,目标主机只负责拉取镜像和启动容器,不在机器上本地构建。
|
||||
|
||||
`yarn prebuild` 会同步 docs、博客和其它静态内容。CI 在该阶段执行 `scripts/sync-doc-content.sh`(从 `console.svc.plus`、`accounts.svc.plus`、`rag-server.svc.plus` 和 `postgresql.svc.plus` 拉取文档)以及 `scripts/sync-blog-content.sh`(克隆 `https://github.com/cloud-neutral-workshop/knowledge.git`),因此 `knowledge/` 目录和所有文档/博客资产在构建镜像时就已存在。
|
||||
`yarn prebuild` 现在只生成 console 自己的营销内容工件。`/docs` 与 `/blogs` 不再把 `knowledge/` 或 Markdown 文档打进前端镜像,而是在请求时通过服务端 `docsServiceClient` 从 `docs.svc.plus` 拉取渲染后的内容。
|
||||
|
||||
当前方案尽量以静态模式运行:
|
||||
|
||||
- Caddy 直接服务 `/_next/static/*` 与 `public/` 里的静态资源,并配合 `frontend_static` 共享卷。
|
||||
- Next.js standalone 容器只承接动态页面、认证接口和代理接口,`frontend-assets` 任务会把所有静态文件(包括哈希后的 CSS/JS)拷贝到 `frontend_static`。
|
||||
- `knowledge/` 与同步的文档/博客内容在 GitHub Actions 的 Docker 构建阶段就被写入镜像。
|
||||
- `docs.svc.plus` 是 docs/blog 的运行时内容源,浏览器不会直接访问它。
|
||||
|
||||
发布由 `.github/workflows/service_release_frontend-deploy.yml` 驱动,CI 构建/推送镜像、渲染 `.env.runtime`,然后将 `docker-compose.yml`、`Caddyfile` 与运行时环境文件发送到主机。随后控制平面工作流 `.github/workflows/service_release_apiserver-deploy.yml` 通过 `scripts/github-actions/update-release-dns.sh` 更新 Cloudflare DNS,使 `cn.svc.plus` 与别名 `cn.onwalk.net` 指向更新后的环境。
|
||||
发布由 `.github/workflows/service_release_frontend-deploy.yml` 驱动,CI 构建/推送镜像、渲染包含 `DOCS_SERVICE_URL` / `DOCS_SERVICE_INTERNAL_URL` 的 `.env.runtime`,然后将 `docker-compose.yml`、`Caddyfile` 与运行时环境文件发送到主机。随后控制平面工作流 `.github/workflows/service_release_apiserver-deploy.yml` 通过 `scripts/github-actions/update-release-dns.sh` 更新 Cloudflare DNS,使 `cn.svc.plus` 与别名 `cn.onwalk.net` 指向更新后的环境。
|
||||
|
||||
这是针对弱 IO 单机主机 `47.120.61.35` 的部署权衡:主机不会在本地构建镜像,只需登录 GHCR、拉取 `dashboard` 镜像、解包静态资源到 `frontend_static`,再通过 `docker compose` 启动 `dashboard` 与 `caddy`。
|
||||
|
||||
未来如果 `docs.svc.plus` 被拆分成独立的 API 服务,必须同步更新这份说明(以及运行手册),让 GitHub Actions 只打包属于新服务的内容。
|
||||
`docs.svc.plus` 已经是前端 docs/blog 内容的独立服务。
|
||||
|
||||
## 相关文档
|
||||
|
||||
|
||||
@ -1,10 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
KNOWLEDGE_DIR="${REPO_ROOT}/knowledge"
|
||||
KNOWLEDGE_REPO="${KNOWLEDGE_REPO:-https://github.com/Cloud-Neutral-Workshop/knowledge.git}"
|
||||
|
||||
rm -rf "${KNOWLEDGE_DIR}"
|
||||
git clone --depth=1 "${KNOWLEDGE_REPO}" "${KNOWLEDGE_DIR}"
|
||||
echo "frontend build context no longer syncs docs/blog content; using docs.svc.plus at runtime."
|
||||
|
||||
@ -50,6 +50,8 @@ append_env NEXT_PUBLIC_ACCOUNT_SERVICE_URL "${NEXT_PUBLIC_ACCOUNT_SERVICE_URL:-$
|
||||
append_env SERVER_SERVICE_URL "${SERVER_SERVICE_URL:-https://api.svc.plus}"
|
||||
append_env NEXT_PUBLIC_SERVER_SERVICE_URL "${NEXT_PUBLIC_SERVER_SERVICE_URL:-${SERVER_SERVICE_URL:-https://api.svc.plus}}"
|
||||
append_env SERVER_SERVICE_INTERNAL_URL "${SERVER_SERVICE_INTERNAL_URL-}"
|
||||
append_env DOCS_SERVICE_URL "${DOCS_SERVICE_URL:-https://docs.svc.plus}"
|
||||
append_env DOCS_SERVICE_INTERNAL_URL "${DOCS_SERVICE_INTERNAL_URL-}"
|
||||
append_env OPENCLAW_GATEWAY_REMOTE_URL "${OPENCLAW_GATEWAY_REMOTE_URL-}"
|
||||
append_env OPENCLAW_GATEWAY_TOKEN "${OPENCLAW_GATEWAY_TOKEN-}"
|
||||
append_env VAULT_SERVER_URL "${VAULT_SERVER_URL-}"
|
||||
|
||||
@ -11,12 +11,12 @@ echo "======================================"
|
||||
echo "Starting prebuild process..."
|
||||
echo "======================================"
|
||||
|
||||
# Step 1: Sync documentation from service repositories
|
||||
# Step 1: Generate local marketing content artifacts
|
||||
echo ""
|
||||
echo "[1/2] Generating static content..."
|
||||
echo "[1/2] Generating marketing content..."
|
||||
npx tsx scripts/generate-content.ts
|
||||
|
||||
# Step 2: Build contentlayer
|
||||
# Step 2: Build contentlayer artifacts used by non-doc pages
|
||||
echo ""
|
||||
echo "[2/2] Building contentlayer..."
|
||||
node scripts/build-contentlayer.mjs
|
||||
|
||||
@ -6,7 +6,6 @@ import { Suspense } from "react";
|
||||
|
||||
import BlogList from "@components/blog/BlogList";
|
||||
import { PublicPageShell } from "@/components/public/PublicPageShell";
|
||||
import type { BlogCategoryPayload, BlogPostPayload } from "@lib/docsServiceClient";
|
||||
import { getBlogList } from "@lib/docsServiceClient";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
@ -17,9 +16,15 @@ export const metadata: Metadata = {
|
||||
|
||||
export default async function BlogPage() {
|
||||
const listing = await getBlogList({ page: 1, pageSize: 200 });
|
||||
const categories: BlogCategoryPayload[] = listing.categories;
|
||||
const categories = listing.categories;
|
||||
const postsWithoutContent = listing.posts.map(
|
||||
({ html: _html, plaintext: _plaintext, sourcePath: _sourcePath, language: _language, ...post }: BlogPostPayload) => post,
|
||||
({
|
||||
html: _html,
|
||||
plaintext: _plaintext,
|
||||
sourcePath: _sourcePath,
|
||||
language: _language,
|
||||
...post
|
||||
}) => post,
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@ -9,7 +9,21 @@ import BrandCTA from "@components/BrandCTA";
|
||||
import { PublicPageIntro } from "@/components/public/PublicPageShell";
|
||||
import SearchComponent from "@components/search";
|
||||
import { useLanguage } from "@i18n/LanguageProvider";
|
||||
import type { BlogCategory, BlogPostSummary } from "@lib/blogContent";
|
||||
|
||||
type BlogCategory = {
|
||||
key: string;
|
||||
label: string;
|
||||
};
|
||||
|
||||
type BlogPostSummary = {
|
||||
slug: string;
|
||||
title: string;
|
||||
author?: string;
|
||||
date?: string;
|
||||
tags: string[];
|
||||
excerpt: string;
|
||||
category?: BlogCategory;
|
||||
};
|
||||
|
||||
function formatDate(
|
||||
dateStr: string | undefined,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user