Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ VITE_SUPABASE_ANON_KEY=your-anon-key-here

# --- Cloudflare Pages Functions only (never expose in client code) ---
SUPABASE_URL=https://YOUR_PROJECT.supabase.co
SUPABASE_ANON_KEY=your-anon-key-here
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key-here
43 changes: 43 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Pull Request CI

on:
pull_request:
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
quality:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm

- name: Install dependencies
run: npm ci

- name: Check text health
run: npm run check:text

- name: Lint
run: npm run lint

- name: Test
run: npm test

- name: Build
run: npm run build

- name: Verify build output
run: npm run check:dist
3 changes: 3 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ jobs:
- name: Build
run: npm run build

- name: Verify build output
run: npm run check:dist

- name: Setup Pages
uses: actions/configure-pages@v5

Expand Down
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
## [Unreleased]

### Added
- Pull request CI for text health, lint, tests, production build, and required-page verification
- Production-path API handler tests and authenticated community signal contract coverage
- Dashboard production build entry and explicit missing-auth-configuration state
- `npm run check:dist` verification for all six public pages
- Text health check script (`npm run check:text`) to catch mojibake regressions
- Rate limiting to API (60 req/min/IP, Cloudflare-aware)
- GET /api/vaults/:id - single node detail endpoint
Expand All @@ -15,14 +19,17 @@ All notable changes to this project will be documented in this file.
- Enhanced .gitignore (OS files, .vite/, supabase local)

### Changed
- Community signals for real nodes now require a verified Supabase user session and execute under RLS
- Demo community signals remain non-persisted and no longer attempt database writes
- API helper tests import the production implementation instead of copied functions
- lib/auth.ts: fixed NEXT_PUBLIC_* env vars to VITE_*
- API jsonResponse now accepts optional extraHeaders

### To Do
- pgvector semantic search integration
- Frontend React migration from vanilla JS pages
- WebSocket for real-time community signals
- CI/CD pipeline beyond GitHub Pages deploy
- Production data seeding and Cloudflare-native rate limiting

## [1.0.0] - 2026-06-10

Expand Down
2 changes: 2 additions & 0 deletions DEPLOYMENT_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
- Cloudflare Dashboard → Pages → `aiknowledgebank` → Settings → Environment variables
- 添加:
- `SUPABASE_URL`
- `SUPABASE_ANON_KEY`
- `SUPABASE_SERVICE_ROLE_KEY`
- `VITE_SUPABASE_URL` (如需浏览器侧登录)
- `VITE_SUPABASE_ANON_KEY` (如需浏览器侧登录)
Expand Down Expand Up @@ -137,6 +138,7 @@

```env
SUPABASE_URL=https://YOUR_PROJECT.supabase.co
SUPABASE_ANON_KEY=YOUR_ANON_KEY
SUPABASE_SERVICE_ROLE_KEY=YOUR_SERVICE_ROLE_KEY
VITE_SUPABASE_URL=https://YOUR_PROJECT.supabase.co
VITE_SUPABASE_ANON_KEY=YOUR_ANON_KEY
Expand Down
10 changes: 7 additions & 3 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ Run these before committing user-facing or runtime changes:
```bash
npm run check:text
npm run lint
npm test
npm run build
npm run check:dist
```

Use `npm run preview` after `npm run build` when visual inspection is needed.
Expand All @@ -45,6 +47,7 @@ AI Knowledge Bank
|-- tools.html # Tools Vault page
|-- cases.html # Cases Vault page
|-- community.html # Community validation page
|-- dashboard.html # Authentication, profile, and contribution dashboard
|-- assets/ # Shared CSS, JS, and visual assets
|-- functions/api/ # Cloudflare Pages Functions backend
|-- lib/ # TypeScript utilities and workflow code
Expand All @@ -61,20 +64,21 @@ AI Knowledge Bank
- Phase 2: improved dedicated vault pages with readable fallbacks, filtering, node details, and community signals.
- Phase 3: added Cross-Vault Dispatch panels to all dedicated vault pages.
- Phase 4: added text health verification and documented the guardrail.
- Phase 5: restored the Dashboard build, secured authenticated community writes, and added production-path API and PR checks.

## Backlog

High priority:

- Add focused API contract tests for `/api/search`, `/api/vaults`, and `/api/community-signals`.
- Add a contribution flow for submitting knowledge, tool, and case candidates.
- Add UI smoke checks for the dedicated vault pages.
- Publish the existing seed corpus so production vault reads no longer use fallback data.

Medium priority:

- Add a lightweight analytics dashboard for evolution signals.
- Improve Supabase Realtime integration for community signals.
- Add authenticated authoring flows when credentials are available.
- Add authenticated authoring flows beyond community validation.

Future:

Expand All @@ -85,5 +89,5 @@ Future:
## Commit Expectations

- Keep commits scoped to one phase or one bugfix.
- Run `npm run check:text`, `npm run lint`, and `npm run build` before pushing.
- Run `npm run check:text`, `npm run lint`, `npm test`, `npm run build`, and `npm run check:dist` before pushing.
- Update `CHANGELOG.md` under `[Unreleased]` when adding visible behavior, scripts, docs, or deployment changes.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,9 @@ Build and verify / 构建与验证:
```bash
npm run check:text
npm run lint
npm test
npm run build
npm run check:dist
npm run preview
```

Expand All @@ -144,10 +146,11 @@ Then configure / 然后配置:
VITE_SUPABASE_URL=your-project-url
VITE_SUPABASE_ANON_KEY=your-anon-key
SUPABASE_URL=your-project-url
SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
```

`VITE_*` keys are for browser-side auth flows. `SUPABASE_URL` and `SUPABASE_SERVICE_ROLE_KEY` are Cloudflare Pages Function variables and must not be exposed in client code.
`VITE_*` keys are for browser-side auth flows. `SUPABASE_ANON_KEY` lets Pages Functions perform authenticated, user-scoped writes that respect RLS. `SUPABASE_SERVICE_ROLE_KEY` is reserved for trusted server-side reads and must never be exposed in client code.

`VITE_*` 用于浏览器侧登录流程;`SUPABASE_URL` 和 `SUPABASE_SERVICE_ROLE_KEY` 用于 Cloudflare Pages Functions,不能写入前端代码。

Expand All @@ -163,7 +166,7 @@ Cloudflare Pages 通过 `functions/api/[[path]].js` 提供真实后端接口。G
| `GET /api/vaults` | Three-vault grouped node data and stats |
| `GET /api/vaults/:id` | Single node detail endpoint |
| `GET /api/search?q=&locale=&limit=` | Cross-Vault RAG search contract returning knowledge, tools, and cases |
| `POST /api/community-signals` | Community validation, usage, fork, merge, comment, and dispute signals |
| `POST /api/community-signals` | Authenticated community signals for real nodes; node-less requests remain non-persisted demos |
| `GET /api/leaderboard` | User ranking endpoint |

Related docs / 相关文档:
Expand Down
58 changes: 29 additions & 29 deletions assets/vault-page.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { buildCommunitySignalRequest, fetchApi } from '../lib/shared/api.js';

const pageType = document.body.dataset.page || 'knowledge';
const vaultKeyByPage = {
knowledge: 'knowledge',
Expand Down Expand Up @@ -91,6 +93,7 @@ const dictionaries = {
validationPersisted: '验证信号已写入后端,节点演化权重已更新',
validationQueued: '验证信号已进入演示队列',
validationFailed: '后端暂不可用,已保留为本地演示状态',
validationAuthRequired: '请先在控制台登录,再提交真实社区验证。',
panelTitleCommunity: '实时验证信号',
panelCopyCommunity: '点击后会向现有 Cloudflare API 提交一次社区验证信号,用来模拟内容从个人经验进入公共演化循环。',
footer: 'AI Knowledge Bank:AI 时代的公共知识银行。'
Expand Down Expand Up @@ -180,6 +183,7 @@ const dictionaries = {
validationPersisted: 'Validation signal written to backend; node evolution weight updated',
validationQueued: 'Validation signal entered the demo queue',
validationFailed: 'Backend unavailable; kept as local demo state',
validationAuthRequired: 'Sign in from the dashboard before submitting a real community validation.',
panelTitleCommunity: 'Live Validation Signal',
panelCopyCommunity: 'This sends one community validation signal to the existing Cloudflare API and simulates how personal practice enters the shared evolution loop.',
footer: 'AI Knowledge Bank: Where AI experience grows together.'
Expand Down Expand Up @@ -321,29 +325,6 @@ const dispatchRunButton = document.getElementById('dispatch-run');
const dispatchStatus = document.getElementById('dispatch-status');
const dispatchResults = document.getElementById('dispatch-results');

function resolveApiBase() {
if (window.location.hostname.endsWith('github.io') || window.location.protocol === 'file:') {
return 'https://aiknowledgebank.pages.dev/api';
}

return `${window.location.origin}/api`;
}

const apiBase = resolveApiBase();

async function fetchApi(path, options = {}) {
const headers = { ...(options.headers || {}) };
if (options.body) headers['Content-Type'] = 'application/json';

const response = await fetch(`${apiBase}${path}`, {
...options,
headers
});

if (!response.ok) throw new Error(`API request failed with ${response.status}`);
return response.json();
}

function pageSuffix() {
return pageType.charAt(0).toUpperCase() + pageType.slice(1);
}
Expand Down Expand Up @@ -823,10 +804,18 @@ async function submitCommunityValidation() {
if (validationResult) validationResult.replaceChildren();

try {
const result = await fetchApi('/community-signals', {
method: 'POST',
body: JSON.stringify({
node_id: latestNodes[0]?.id || null,
let accessToken = null;
if (latestSource === 'supabase' && latestNodes[0]) {
const { getCurrentUser } = await import('../lib/auth.ts');
const session = await getCurrentUser();
accessToken = session?.accessToken || null;
}

const signalRequest = buildCommunitySignalRequest({
source: latestSource,
node: latestNodes[0],
accessToken,
signal: {
signal_type: 'validated',
impact_delta: 12,
confidence: 0.86,
Expand All @@ -835,14 +824,25 @@ async function submitCommunityValidation() {
language: currentLanguage,
source: 'community-page'
}
})
}
});

if (signalRequest.requiresAuthentication) {
statusLine.textContent = dictionaries[currentLanguage].validationAuthRequired;
return;
}

const result = await fetchApi('/community-signals', signalRequest.options);

statusLine.textContent = result.persisted
? dictionaries[currentLanguage].validationPersisted
: dictionaries[currentLanguage].validationQueued;
renderValidationResult(result);
} catch {
} catch (error) {
if (error?.status === 401) {
statusLine.textContent = dictionaries[currentLanguage].validationAuthRequired;
return;
}
statusLine.textContent = dictionaries[currentLanguage].validationFailed;
renderValidationResult({
ok: true,
Expand Down
Loading
Loading