# ncode **Repository Path**: cng1985/ncode ## Basic Information - **Project Name**: ncode - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-10-13 - **Last Updated**: 2026-10-13 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # NCode NCode 是一个中文项目协作与交付平台。当前实现依据所提供的 SRS v4.0,需求文档标注为「待评审」。本仓库仍处于开发阶段,不是生产发布,也不构成 P0 验收结论。 ## 当前范围 - 用户可注册并验证邮箱、登录和注销;支持一次性密码重置,重置后撤销旧会话和个人访问令牌。团队空间 owner/admin 必须启用 TOTP 双因素认证,并使用单次恢复码;开发环境可在未接入邮件时使用一次性令牌。 - 用户可使用个人工作区或创建团队工作区,通过七天有效的一次性令牌邀请已验证账户、撤销待处理邀请,并切换所属工作区。项目、工作项、活动记录和幂等重放均按当前工作区隔离。 - 用户可创建项目并填写目标、标识、类型和可见范围;内部项目供当前工作区成员使用,私有项目仅对负责人和工作区管理员开放,项目页显示按工作项状态计算的进度。 - 项目可以关联多个仓库;同一工作区仓库可供多个项目引用,解除关联不会删除仓库。 - 用户可创建和编辑工作项,维护负责人、标签、估算点数、优先级、截止日期、验收清单和变更历史。项目提供列表/看板切换,以及成员、状态、标签筛选。工作项可在待处理、就绪、进行中、待评审、待验收和已验收之间流转。 - 用户可在项目内创建和跟踪 Issue,支持标签、负责人、讨论评论、关闭与重新打开;评论以纯文本展示,不执行用户 HTML。 - Issue 可关联同一项目仓库的 Pull Request;Issue 描述和评论可通过 `@成员邮箱` 提醒当前工作区成员。 - 仓库支持按源/目标分支创建 PR 草稿或开放请求、固定 SHA 评审、批准/请求修改、普通讨论及基于当前 Diff 快照的行内评论、关闭/重开和 Merge Commit。行内评论服务端校验文件、行号和源提交;更新代码快照后旧评论保留为历史记录,不会伪装成当前行评审。合并前再次检查分支 SHA、所需批准和请求修改状态;目标 ref 使用 Git compare-and-swap 更新,保护分支拒绝直接 push、强推和删除。 - 仓库默认分支支持配置最低批准数、CI 要求和作者自批规则。Pull Request 创建时会为固定源提交排队运行 Go 测试,页面显示运行状态并支持重跑或取消。启用 CI 门禁后,只有独立 Runner 对当前 SHA、固定检查配置返回可信通过,且服务端重新确认 PR 快照有效时才允许合并。 - CI Runner 使用单独部署的 Docker 主机和 SHA256 固定的沙箱镜像;每个任务禁用网络、只读容器根目录、无宿主机挂载、非 root 用户运行,并限制 CPU、内存、进程数和时长。当前只提供 `go-test-v1` 检查模板;实际启用 CI 门禁前,必须先部署 Runner 并准备匹配的离线 Go 依赖镜像。 - Pull Request 可关联同项目仓库的工作项;关联变化写入工作项活动,合并后记录交付事件并通知项目订阅者进入业务验收。 - 项目文档和设计决策保存 Markdown 源文、固定仓库提交链接及不可变版本快照。 - 作品页支持创建自述型证据,可引用固定提交或本人 PR 当前提交上的他人批准记录;后者只证明该 SHA 收到过 PR 人工批准,不等同于导师或整体能力认证。展示文字修改会生成新版本,既有分享固定到签发时的版本。证据默认私有,可公开发布个人说明或公开仓库来源。定向分享只发给邮箱已验证的 NCode 账户,绑定用途、期限和摘要/公开来源范围;可查看访问记录并撤销。私有来源只可在本人明确确认有权披露后分享脱敏摘要。 - 站内通知支持工作项、Issue、项目文档和 PR 事件,按用户事件键去重,可单条或批量标为已读;项目支持订阅、取消订阅和最多七天静默。 - 验收前必须勾选当前验收清单的每一项。更新请求携带 `expectedVersion`;过期写入返回 HTTP 409。项目和工作项创建支持 `Idempotency-Key` 重放。 - 用户可在工作区内创建私有、工作区可见或公开 Git 仓库,并关联到一个或多个项目。创建时可选择空仓库、README、Go/Node.js `.gitignore` 和 MIT LICENSE;`main` 的首次引导提交可建立该分支,之后仍受保护。系统提供分支/Tag 切换、目录与 UTF-8 文本浏览、最近 100 条提交历史、相对父提交的 Diff 及文件下载。仓库所有者和工作区管理员可改名;Git 地址固定使用仓库 ID。删除后进入 30 天回收期,可恢复;到期后台清理对象并保留墓碑。文件预览上限为 1 MiB、下载上限为 20 MiB、Diff 上限为 1 MiB;超限时提供下载或提示。 - 用户可创建仅显示一次的个人访问令牌,选择 Git 只读或读写范围、到期时间并随时撤销。Git 智能 HTTP 支持标准客户端 clone/fetch/push;每次请求校验令牌、权限范围和当前工作区成员关系。私有仓库仅仓库所有者及工作区 owner/admin 可访问;内部仓库只允许当前工作区成员访问;读写令牌可推送到非保护分支,`main` 拒绝直接推送。 - 桌面与移动端宽度使用同一套界面。 项目进度按工作项状态计算:待处理或已取消为 0%,就绪为 20%,进行中为 50%,待评审为 75%,待业务验收为 90%,已验收为 100%,然后对项目内工作项取平均。没有工作项的项目显示 0%。 仍未完成的主要范围包括统一资源权限检查(目前已覆盖项目内部/私有可见范围,但尚无独立项目成员 ACL 或跨租户公开投影)、PR 行级讨论线程与完整分支规则优先级、学习路径与隔离评测、在线工作区、从评测和部署生成的核验证据、导师与组织证明签发、证据申诉及简历导出、经过审核的招聘与申请、在线应用发布、配额结算、举报申诉和备份恢复。CI 的服务端队列与独立执行器已实现,但本机尚未部署 Docker 执行环境,也未完成线上运行验收。PR 人工批准目前只作为指定提交的来源事实,不构成技能认证。当前仓库未达到 SRS P0 发布门槛;请勿将其暴露到网络或用于真实候选人、企业及生产数据。Git 读写目前需由部署层提供 HTTPS;应用本身默认只绑定回环地址。 ## 技术栈 - Go 1.25+(已在 Go 1.27.2 上测试)、Gin、GORM、Fx - PostgreSQL 17:账户、工作区、项目、仓库元数据、工作项、清单、活动记录和幂等记录 - 原生 Git:创建本地 bare 仓库、读取仓库文件并通过 Git HTTP Backend 提供智能 HTTP;默认将仓库对象存储在 `var/git` - 仓库生命周期:固定 ID 的 Git 地址在改名后保持不变;删除先进入 30 天可恢复状态并停止新读写,到期后台清理 Git 对象和仓库级 PR 关联,保留仓库墓碑记录 - Vue 3、Vite、Element Plus 和 JavaScript - Compose 中提供 Redis 7,供后续队列与协调功能使用;当前范围不依赖 Redis ## 后端模块 Fx 组合根位于 `internal/app`,组合配置、日志、数据库、API 和 HTTP 服务模块。账户/工作区逻辑位于 `internal/identity`,仓库逻辑位于 `internal/repository`;`cmd/api` 负责启动应用。数据库模块负责连接检查、结构迁移、演示数据灌入和连接池清理。HTTP 服务模块在 Fx 启动时绑定端口、报告绑定错误,并在数据库关闭前优雅关闭。 ## 配置 应用从 `configs/config.yaml` 读取服务、PostgreSQL 和 Git 存储设置。使用 `CONFIG_FILE` 加载其他 YAML 文件。`DATABASE_PASSWORD` 可以覆盖 YAML 中的密码;`DATABASE_URL` 可以覆盖完整 PostgreSQL 地址。`API_ADDR`、`APP_ENV`、`SEED_DEMO` 和 `GIT_ROOT` 也可以覆盖 YAML 配置。API 可通过 `CI_RUNNER_SECRET` 开放给 Runner 的任务接口;该密钥至少 32 个字符,必须同时安全配置到 API 和独立执行主机。生产环境始终关闭演示数据灌入。 已提交的 YAML 只包含本地 Compose 数据库的开发凭据。真实部署凭据应保存在版本控制之外。若要连接私有 PostgreSQL,可把 JDBC 地址和用户名写入被忽略的 `configs/config.local.yaml`,不要在文件中保存密码,通过环境密钥提供 `DATABASE_PASSWORD`,并设置 `CONFIG_FILE=configs/config.local.yaml`。生产环境应设置 32 字节的 `AUTH_ENCRYPTION_KEY`(hex 或 Base64 编码)以加密 MFA 密钥,并配置 `SMTP_HOST`、`SMTP_PORT`、`SMTP_FROM` 及可选的 `SMTP_USER`/`SMTP_PASSWORD`;SMTP 必须支持 STARTTLS。API 启动时会执行 GORM AutoMigrate,因此只在确认目标数据库允许结构变更时启动服务。 ## 本地启动 启动 PostgreSQL: ```sh cd /workspace/ncode docker compose up -d --wait postgres ``` 构建 Vue 并将静态资源嵌入 Go 程序: ```sh cd /workspace/ncode export PATH=/workspace/.tools/go/bin:$PATH export GOCACHE=/workspace/.cache/go-build export GOPATH=/workspace/.cache/go make build ``` 启动 API 与内嵌 Vue: ```sh cd /workspace/ncode SEED_DEMO=false ./bin/ncode ``` 打开 `http://127.0.0.1:8088`。注册后使用页面显示的开发验证令牌确认邮箱。`make build` 会先将 Vue 构建到 `web/dist`,再通过 `//go:embed all:dist` 编入可执行文件;缺少 Vite 时会按锁文件安装依赖。使用 `make web-deps` 可重新安装前端依赖。 开发 Vue 时可在一个终端运行 `go run ./cmd/api`,另一个终端进入 `web` 运行 `npm run dev`;Vite 会代理 API 请求并提供热更新。普通运行不需要单独启动 Vite。默认配置使用本地 Compose PostgreSQL;开发环境默认会向空数据库写入演示数据,设置 `SEED_DEMO=false` 可关闭。没有配置 Runner 时,CI 任务会停留在队列中;开启分支 CI 门禁后,当前提交没有可信通过结果时无法合并。 ## 独立 CI Runner 执行器应运行在专用主机上,主机安装 rootless Docker,并通过 HTTPS 访问 API。Runner 会拒绝连接非 rootless daemon;不要把 Docker daemon 或 Runner 放在 API 容器里。沙箱模板会预先下载本项目 `go.mod` 中的依赖;任务容器本身没有网络,因此新的 Go 依赖需先更新并重建镜像。 构建 Runner 程序及仅包含 Go 依赖清单的沙箱镜像: ```sh make ci-runner docker build -f ci/Dockerfile.sandbox -t ncode-ci-sandbox:go-test-v1 . ``` 将沙箱镜像推送到镜像仓库并按仓库返回的 `sha256` 摘要固定;不要在 `CI_SANDBOX_IMAGE` 中使用可变标签。在执行主机上先拉取这个固定摘要的镜像。API 服务设置 `CI_RUNNER_SECRET`。在专用执行主机上设置同一密钥以及以下环境变量,然后启动 `bin/ncode-ci-runner`: ```sh export CI_RUNNER_API_URL=https://ncode.example.com export CI_RUNNER_SECRET='replace-with-the-same-random-secret-as-the-api' export CI_RUNNER_ID=ci-worker-01 export CI_SANDBOX_IMAGE='registry.example.com/ncode-ci-sandbox@sha256:replace-with-the-64-character-digest' docker pull "$CI_SANDBOX_IMAGE" ./bin/ncode-ci-runner ``` 本机调试可将 API URL 设为 `http://127.0.0.1:8088`。Runner 每次只运行固定的 `go test ./...` 检查;任务使用无网络、只读根文件系统和临时内存盘,不挂载 Git 仓库、宿主机路径或密钥。API 与 Runner 之间需使用受信任的 HTTPS,线上 API 应由部署层代理到应用默认的回环监听地址。 ## 验证 ```sh cd /workspace/ncode docker compose up -d --wait postgres export PATH=/workspace/.tools/go/bin:$PATH export GOCACHE=/workspace/.cache/go-build export GOPATH=/workspace/.cache/go export TEST_DATABASE_URL='postgres://ncode:ncode_local_dev@127.0.0.1:54329/ncode?sslmode=disable' make test go vet ./... make build ``` 测试覆盖环境校验、Fx 依赖图、HTTP 服务生命周期、登录保护、生产环境邮件配置边界、内嵌静态资源、账户与工作区邀请、工作区数据隔离、项目标识范围,以及本地 Git 仓库初始化和文件浏览。启动构建后的二进制后,可用 `curl -fsS http://127.0.0.1:8088/healthz` 验证服务。浏览器账户到仓库的完整交互验收仍待补充。 ## API 业务操作使用 `POST /api/v1/{resource}/{action}`,返回 `{ "code", "msg", "data" }`。分页响应还会在根级包含 `page`、`size`、`total`、`totalPage` 和 `responseType`。ID 为 JavaScript 安全整数;状态为数字并带有 `stateName`;日期格式为 `yyyy-MM-dd`;更新操作携带 `expectedVersion`。项目和工作项创建需要 `Idempotency-Key`。 主要路由: - 账户:`POST /api/v1/auth/register`、`auth/verify`、`auth/verify/resend`、`auth/login`、`auth/logout`、`auth/password-reset/request`、`auth/password-reset/complete`、`auth/mfa/setup`、`auth/mfa/enable`;`GET /api/v1/auth/me` - 工作区:`POST /api/v1/workspace/create`、`workspace/invite/create`、`workspace/invite/accept`、`workspace/invite/revoke` - 项目:`POST /api/v1/project/create`、`project/query/page`、`project/query/one` - 项目仓库关联:`POST /api/v1/project/repository/query/page`、`project/repository/link`、`project/repository/unlink` - 工作项:`POST /api/v1/workItem/create`、`workItem/update`、`workItem/transition`、`workItem/accept`、`workItem/query/page`、`workItem/query/activity` - Issue:`POST /api/v1/issue/create`、`issue/query/page`、`issue/query/comments`、`issue/comment`、`issue/transition` - Issue 与 PR 关联:`POST /api/v1/issue/link-pull-request` - Pull Request:`POST /api/v1/pullRequest/create`、`pullRequest/query/page`、`pullRequest/query/one`、`pullRequest/review`、`pullRequest/comment`、`pullRequest/transition`、`pullRequest/merge`、`pullRequest/link-work-item`、`pullRequest/unlink-work-item`、`pullRequest/query/work-items`、`pullRequest/query/work-item-options` - CI:登录用户可调用 `POST /api/v1/ci/run/create`、`ci/run/query` 和 `ci/run/cancel`;独立 Runner 使用 `POST /api/v1/ci/runner/claim`、`GET /api/v1/ci/runner/jobs/{id}/source`、`POST /api/v1/ci/runner/jobs/{id}/heartbeat` 和 `POST /api/v1/ci/runner/jobs/{id}/result`。Runner 接口使用独立共享密钥和短期单任务租约。 - 分支、Tag、提交历史、Diff、文件下载及保护规则:`POST /api/v1/repository/query/branches`、`repository/query/tags`、`repository/query/history`、`repository/query/diff`、`repository/file/download`、`repository/protection/query`、`repository/protection/update` - 通知和项目订阅:`POST /api/v1/notification/query/page`、`notification/read`、`notification/read-all`、`project/subscription/query`、`project/subscription/update` - 项目文档:`POST /api/v1/projectDocument/create`、`projectDocument/update`、`projectDocument/query/page`、`projectDocument/query/revisions` - 仓库:`POST /api/v1/repository/create`、`repository/rename`、`repository/delete`、`repository/restore`、`repository/query/page`、`repository/query/deleted`、`repository/query/tree`、`repository/query/file`;Git 客户端地址为 `/git/{repositoryId}.git` - 个人访问令牌:`GET /api/v1/auth/tokens`、`POST /api/v1/auth/tokens/create`、`POST /api/v1/auth/tokens/revoke` - 能力证据:`POST /api/v1/evidence/create`、`evidence/query/mine`、`evidence/query/review-options`、`evidence/presentation/update`、`evidence/revoke`、`evidence/share/create`、`evidence/share/query/mine`、`evidence/share/query/accesses`、`evidence/share/revoke`、`evidence/share/read`;公开展示通过 `GET /api/v1/evidence/public/{id}?version={version}` 读取 - 保护的业务 API 需要会话 Cookie;`X-Workspace-ID` 用于选择工作区,服务端会验证用户的有效成员关系。 - 健康检查:`GET /healthz` 仓库改名只修改显示名称和逻辑标识,Git 地址由不可变仓库 ID 组成,因此已有克隆地址可继续使用。删除会进入 30 天恢复期;期间仓库停止新访问,可由仓库所有者或工作区管理员恢复。到期后清理任务每小时检查并清理 Git 对象、PR 讨论数据及项目关联,之后保留墓碑记录以避免 ID 被重用。