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
23 changes: 15 additions & 8 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
# ================================================
# scnu-thesis-portal (merged with Story2Paper)
# scnu-thesis-portal
# ================================================

# SCNU Exporter (always required)
OUTPUT_DIR=./output
# SCNU Workbench / exporter
APP_ENV=development
MAX_DOCX_SIZE_BYTES=4194304
SCNU_DATABASE_URL=
SCNU_STORAGE_DIR=
CORS_ALLOWED_ORIGINS=http://127.0.0.1:5173,http://localhost:5173

# Story2Paper — LiteLLM (required for AI generation)
LITELLM_API_KEY=***
# Optional private deployment gate.
SCNU_ACCESS_CODE=

# Story2Paper — CORS (comma-separated origins)
CORS_ORIGINS=https://scnu-thesis-portal.vercel.app,http://localhost:3000
# Required for real deployments that save Provider API keys.
SCNU_SECRET_KEY=

# Story2Paper — SQLite paper store (optional, defaults to /data/papers.db)
# Legacy Story2Paper experiment settings.
OUTPUT_DIR=./output
LITELLM_API_KEY=***
CORS_ORIGINS=https://scnu-thesis-portal.vercel.app,http://localhost:3000
PAPER_DB_PATH=/data/papers.db
35 changes: 35 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Bug report
description: Report a reproducible problem in SC-TH.
title: "[Bug]: "
labels: ["bug"]
body:
- type: textarea
id: what-happened
attributes:
label: What happened?
description: Describe the behavior and what you expected instead.
validations:
required: true
- type: textarea
id: steps
attributes:
label: Reproduction steps
placeholder: "1. Open...\n2. Upload...\n3. Click..."
validations:
required: true
- type: textarea
id: verification
attributes:
label: Verification output
description: Paste relevant test/build/compliance output. Do not include thesis private content or API keys.
- type: dropdown
id: area
attributes:
label: Area
options:
- Quick export
- Workbench
- Provider settings
- DOCX compliance
- Deployment
- Documentation
27 changes: 27 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Feature request
description: Propose a focused SC-TH improvement.
title: "[Feature]: "
labels: ["enhancement"]
body:
- type: textarea
id: problem
attributes:
label: Problem
description: What user workflow should this improve?
validations:
required: true
- type: textarea
id: proposal
attributes:
label: Proposed behavior
description: Describe the smallest useful version.
validations:
required: true
- type: checkboxes
id: boundaries
attributes:
label: Boundaries
options:
- label: This does not require fabricating data, references, comments, or school rules.
- label: This keeps AI-generated正文 behind Proposal / Approval.
- label: This describes any privacy or remote Provider impact.
36 changes: 36 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
## Summary

-

## User Scenarios

-

## Data / API Impact

- [ ] No schema or API change
- [ ] Schema change
- [ ] API change
- [ ] Migration or compatibility handling included

## Export / Privacy Impact

- [ ] No export behavior change
- [ ] Export behavior changed
- [ ] No privacy or Provider behavior change
- [ ] Privacy or Provider behavior changed

## Verification

```bash
uv run pytest tests -q
npm run test:smoke --prefix web
npm run build --prefix web
uv run python scripts/build_web_public.py
uv run python scripts/export_compliance_fixture.py tmp/fixture-export.docx
uv run python scripts/check_docx_compliance.py tmp/fixture-export.docx
```

## Screenshots

Add screenshots for UI changes.
8 changes: 7 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,10 @@ jobs:
run: npm run build --prefix web

- name: Build public bundle
run: python3 scripts/build_web_public.py
run: uv run python scripts/build_web_public.py

- name: Export compliance fixture
run: uv run python scripts/export_compliance_fixture.py tmp/ci-fixture-export.docx

- name: Check DOCX compliance fixture
run: uv run python scripts/check_docx_compliance.py tmp/ci-fixture-export.docx
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Changelog

## Unreleased

- Added Workbench project metadata for SCNU undergraduate thesis projects.
- Added project-level privacy mode and explicit remote Provider authorization.
- Added Access Code protection for private Workbench deployments.
- Added Provider config listing, verification, deletion, and server-side key sealing.
- Added Workbench project wizard, project settings, Provider settings, privacy banner, model status badge, and access-code gate.
- Added GitHub contribution, security, PR, issue, and compliance fixture CI documentation.

## 0.2.0

- Established the standards-driven `.docx` export mainline.
- Added Workbench v1 project, file, version, Proposal, Agent event, and export-record skeleton.
- Added Provider metadata and SSRF guard skeleton.
54 changes: 54 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Contributing

SC-TH keeps the fast `.docx` export path stable while Workbench evolves in small phases.

## Branches

- Use `main` for released baseline.
- Use `codex/<feature-name>` for feature work.
- Keep each PR focused on one phase or one user-facing capability.

## Commits

Use:

```text
<type>(scope): <summary>
```

Allowed types:

- `feat`
- `fix`
- `docs`
- `test`
- `refactor`
- `chore`
- `security`

Examples:

```text
feat(workbench): add project wizard and privacy consent
security(provider): verify base urls before saving provider configs
docs(api): document access code guard
```

## Required Checks

Run before opening a PR:

```bash
uv run pytest tests -q
npm run test:smoke --prefix web
npm run build --prefix web
uv run python scripts/build_web_public.py
uv run python scripts/export_compliance_fixture.py tmp/fixture-export.docx
uv run python scripts/check_docx_compliance.py tmp/fixture-export.docx
```

If UI changed, include a screenshot. If export changed, include the compliance script result.

## Product Boundary

This project is an AI-assisted thesis workbench and formatter. It must not present itself as a thesis ghostwriting system, fabricate data, fabricate references, or bypass user approval for AI-generated正文.
16 changes: 12 additions & 4 deletions README-local.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

Workbench v1 本地骨架为:

`创建项目 → 上传材料 → 解析任务 → baseline version → Issue / Proposal → 用户确认 → 导出记录`
`项目向导 → 隐私模式 → 上传材料 → 解析任务 → baseline version → Issue / Proposal → 用户确认 → 导出记录`

## 依赖

Expand Down Expand Up @@ -69,13 +69,13 @@ docker compose up --build
生成前端类型:

```bash
python3 scripts/generate_frontend_types.py
uv run python scripts/generate_frontend_types.py
```

构建前端并写入 `public/`:

```bash
python3 scripts/build_web_public.py
uv run python scripts/build_web_public.py
```

## 本地验收
Expand All @@ -87,11 +87,19 @@ python3 scripts/build_web_public.py
3. 检查预检弹窗是否显示“缺失章节保留留白位”和“复杂元素需人工复核”
4. 检查正式封面已作为主线输出的一部分
5. 通过预检后导出 `.docx`
6. 运行 `python3 scripts/check_docx_compliance.py <导出文件>`
6. 运行 `uv run python scripts/check_docx_compliance.py <导出文件>`
7. 在 Word 中更新目录并抽查页眉页脚、页码和分页
8. 进入 `#/workbench` 新建项目
9. 上传 `.docx` 或文本文件并触发解析
10. 检查版本、Agent 事件、Proposal 队列与导出历史
11. 检查 Provider 配置不会向前端返回 API key,内网 base URL 默认被拦截
12. 如需访问码保护,设置 `SCNU_ACCESS_CODE` 并确认未验证请求会返回 `ACCESS_CODE_REQUIRED`

CI 使用的导出合规 fixture 可本地生成:

```bash
uv run python scripts/export_compliance_fixture.py tmp/fixture-export.docx
uv run python scripts/check_docx_compliance.py tmp/fixture-export.docx
```

更细的人工验收项见 `docs/local-validation-word.md`。
24 changes: 18 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

面向华南师范大学本科毕业论文的规范驱动 Word 导出工具与 Agent Workbench 骨架。

![CI](https://img.shields.io/badge/CI-pytest%20%2B%20vitest%20%2B%20build-blue)
![Template](https://img.shields.io/badge/template-SCNU%20undergraduate-green)
![Privacy](https://img.shields.io/badge/privacy-local%20first-6b7280)

**当前主线:**
1. **快速导出入口** — 上传 `.docx` 或粘贴论文文本,生成按华师规范组织的 Word 文档
2. **Workbench v1 骨架** — 项目空间、文件库、版本、导出记录、Issue Ledger、Proposal 队列和可追溯 Agent 事件
Expand Down Expand Up @@ -68,7 +72,8 @@ flowchart LR
- Agent 事件骨架:解析任务、事件流、规则建议、用户确认 / 拒绝 / 暂存
- 多输入解析 registry:`.docx`、文本、PDF 本地粗解析、图片/OCR 占位、参考文献文件
- 导出 registry:`.docx`、Markdown、自检报告、PDF 降级占位
- Provider 配置骨架:OpenAI、Gemini、DeepSeek、MiniMax、Ollama 元数据与 SSRF 防护
- Provider 设置:OpenAI、Gemini、DeepSeek、MiniMax、Ollama 元数据、服务端密钥保存、验证状态与 SSRF 防护
- 项目创建向导、项目设置、隐私模式提示、远程 Provider 授权与访问码保护
- 规范驱动的正式封面渲染
- 中文摘要 / 英文摘要 / 目录 / 正文 / 参考文献 / 附录 / 致谢固定生成
- Word TOC 字段、页眉、页脚、页码与分节控制
Expand All @@ -82,7 +87,7 @@ flowchart LR
- 表格、图片、脚注、复杂浮动对象不作为阻塞项,但默认进入人工复核范围
- 参考文献只做有限格式整理,不补造作者、刊名、卷期等缺失元数据
- 当前只覆盖本科论文导出主线,不提供研究生模板入口
- Workbench v1 当前是可运行 MVP 骨架,真实 OCR、Celery 队列、MinIO/S3 SDK 与真实 LLM 调用仍需后续接入
- Workbench v1 当前是可运行 MVP 骨架,真实 OCR、Celery 队列、MinIO/S3 SDK、真实 LLM 调用与 Director Runtime 仍需后续接入
- PDF 导出当前保留 `.docx` 结果并记录转换降级,不承诺 PDF 高保真

## 在线预览
Expand Down Expand Up @@ -138,31 +143,38 @@ VITE_API_BASE_URL=http://127.0.0.1:8000 npm run dev --prefix web
docker compose up --build
```

私有部署可复制 `.env.example`,设置 `SCNU_ACCESS_CODE` 保护 API,并设置 `SCNU_SECRET_KEY` 用于服务端封存 Provider API key。

本地构建:

```bash
python3 scripts/generate_frontend_types.py
python3 scripts/build_web_public.py
uv run python scripts/generate_frontend_types.py
uv run python scripts/build_web_public.py
```

## 质量护栏

- `uv run pytest tests -q`
- `npm run test:smoke --prefix web`
- `npm run build --prefix web`
- `python3 scripts/build_web_public.py`
- `python3 scripts/check_docx_compliance.py <docx-path>`
- `uv run python scripts/build_web_public.py`
- `uv run python scripts/export_compliance_fixture.py tmp/fixture-export.docx`
- `uv run python scripts/check_docx_compliance.py tmp/fixture-export.docx`

## 关键文档

- [主线说明](docs/product-mainline-word-v1.md)
- [Workbench v1 说明](docs/workbench-v1.md)
- [API 说明](docs/api.md)
- [规范映射表](docs/scnu-undergraduate-format-spec-map.md)
- [合规清单](docs/quality-checklist-compliance.md)
- [已知限制](docs/known-limitations-word-export.md)
- [审计报告](docs/compliance/scnu-undergraduate-export-audit-report-v1.md)
- [实施与验收记录](docs/compliance/scnu-undergraduate-export-implementation-record-v1.md)
- [本地运行说明](README-local.md)
- [Changelog](CHANGELOG.md)
- [Contributing](CONTRIBUTING.md)
- [Security](SECURITY.md)

## 仓库结构

Expand Down
33 changes: 33 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Security

## Supported Scope

The current Workbench is designed for private or local deployments. It is not a multi-tenant SaaS permission system.

## Access Code

Set `SCNU_ACCESS_CODE` to protect API routes in private deployments.

- `/api/health`
- `/api/access-code/status`
- `/api/access-code/verify`

remain public so the app can verify access. Other `/api/*` routes require the access cookie when `SCNU_ACCESS_CODE` is set.

## Provider Secrets

Provider API keys are accepted only by the backend and are never returned to the frontend. Responses expose only `has_api_key`.

Set `SCNU_SECRET_KEY` in real deployments. Development mode derives an explicitly insecure local key only so the app can run without extra setup.

## SSRF Guard

Custom Provider `base_url` values are validated before storage and verification.

- Remote providers reject loopback and private addresses.
- Link-local, reserved, and multicast addresses are always rejected.
- Ollama can use local addresses only when `allow_local=true`.

## Reporting

Open a private issue or contact the maintainer if a bug could expose thesis content, Provider keys, local files, or internal network access.
20 changes: 20 additions & 0 deletions backend/app/config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import os
from hashlib import sha256
from pathlib import Path

PROJECT_ROOT = Path(__file__).resolve().parents[2]
Expand Down Expand Up @@ -56,3 +57,22 @@ def read_csv_env(name: str, default: list[str]) -> list[str]:
"CORS_ALLOWED_ORIGINS",
["http://127.0.0.1:5173", "http://localhost:5173"] if APP_ENV != "production" else [],
)


ACCESS_CODE_COOKIE_NAME = "scnu_access_token"


def access_code() -> str:
return os.getenv("SCNU_ACCESS_CODE", "").strip()


def secret_key() -> str:
configured = os.getenv("SCNU_SECRET_KEY", "").strip()
if configured:
return configured
seed = f"insecure-local-dev-key:{PROJECT_ROOT}:{APP_ENV}"
return sha256(seed.encode("utf-8")).hexdigest()


def using_insecure_local_secret() -> bool:
return not bool(os.getenv("SCNU_SECRET_KEY", "").strip())
Loading
Loading