Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/HNU-himematsu/HNU-TimeLetter/llms.txt

Use this file to discover all available pages before exploring further.

HNU-TimeLetter uses Feishu (Lark) Bitable as its sole CMS. Stories, location pins, creation board ideas, and the contributor credits list all live in Feishu multi-dimensional tables. The npm run sync command (or the admin panel) pulls this data, uploads any new images to Aliyun OSS, back-fills OSS URLs into Feishu, and writes ready-to-serve JSON artifacts under src/data/. This guide walks through the one-time setup required before the first sync can run.
The Feishu app must be added directly to the Bitable (not just created in the developer console). Skipping this step causes a 403 error on every API call, even with correct credentials.

Setting Up the Feishu App

1

Create a Feishu Enterprise Self-Built App

  1. Open the Feishu Developer Console and click Create App → Enterprise Self-Built App.
  2. Give it a name (e.g. HNU-TimeLetter Sync) and save.
  3. Copy the App ID and App Secret from the Credentials page — these become FEISHU_APP_ID and FEISHU_APP_SECRET.
2

Enable Bitable Permissions

In the app’s Permissions & Scopes panel, enable:
PermissionPurpose
bitable:app:readonlyRead records from Bitable
bitable:appWrite back OSS URLs to attachment fields
Click Publish Version to activate the permissions.
3

Add the App to Your Bitable

Open your target Bitable, click ··· (top-right) → MoreAdd App, then select the app you just created.This step must be repeated for every Bitable that the sync touches (stories table, creation board table, contributors table).
4

Collect Table and View IDs

From the Bitable URL you can extract the token and table IDs:
https://xxx.feishu.cn/base/<APP_TOKEN>?table=<TABLE_ID>&view=<VIEW_ID>
Copy the relevant values into .env.local:
.env.local
FEISHU_APP_ID=cli_xxxxxxxxxxxx
FEISHU_APP_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxx
FEISHU_APP_TOKEN=Bxxxxxxxxxxxxxxxxxx
FEISHU_TABLE_ID=tblxxxxxxxxxxxxxxxxx
FEISHU_VIEW_ID=vewxxxxxxxxxxxxxxxxx
FEISHU_LOCATIONS_TABLE_ID=tblxxxxxxxxxxxxxxxxx
5

Configure the Creation Board Tables

The creation board uses two separate tables — a header table (card metadata) and a main table (idea entries):
.env.local
FEISHU_CREATION_TABLE_ID=tblxxxxxxxxxxxxxxxxx
FEISHU_CREATION_VIEW_ID=vewxxxxxxxxxxxxxxxxx
FEISHU_CREATION_HEADER_TABLE_ID=tblxxxxxxxxxxxxxxxxx
If these variables are left empty, the sync service uses built-in defaults and will warn you in the log output.
6

Configure Aliyun OSS

Create an OSS bucket with public-read ACL and allow CORS from * (required for browser image loading). Then add the credentials:
.env.local
ALIYUN_OSS_REGION=oss-cn-guangzhou
ALIYUN_OSS_BUCKET=your-bucket-name
ALIYUN_OSS_ACCESS_KEY_ID=your_access_key_id
ALIYUN_OSS_ACCESS_KEY_SECRET=your_access_key_secret
For least-privilege access, create a RAM sub-user with oss:PutObject and oss:GetObject permissions on the target bucket only.
7

Run the First Sync

With all variables set, trigger the initial sync:
npm run sync
The sync will:
  1. Authenticate with Feishu and pull all table records
  2. Download any image attachments from Feishu
  3. Compute MD5 hashes and upload images to Aliyun OSS
  4. Back-fill permanent OSS URLs into the Feishu record (头像OSS_URL, 大图OSS_URL fields)
  5. Write src/data/content.json, src/data/creation-board.json, src/data/contributors.json, and src/config/locations.json
A successful run produces output like:
[locations] ✓ 5 records written → src/config/locations.json
[stories]   ✓ 6 records written → src/data/content.json
[contributors] ✓ 12 records written → src/data/contributors.json

First-Time Field Initialization

If you are setting up the Feishu tables from scratch, three scripts create the required fields automatically:
# Create attachment fields (avatar, large image)
npx tsx src/scripts/add-attachment-fields.ts

# Create OSS URL back-fill fields
npx tsx src/scripts/add-oss-fields.ts
These scripts only need to run once per Bitable. After that, the sync service manages field updates automatically.

Image Sync Pipeline

Every time npm run sync runs, the image pipeline processes each record:
  1. Detect: Check if 头像OSS_URL / 大图OSS_URL is already populated
  2. Download: Fetch the raw attachment bytes from Feishu’s temporary CDN
  3. Hash: Compute MD5 to skip re-uploads of unchanged files
  4. Upload: PUT the file to Aliyun OSS at a stable path
  5. Back-fill: Write the permanent OSS URL back to the Feishu record
Images already on OSS are skipped (MD5 match), so incremental syncs are fast.

JSAPI Signature for Feishu Document Embeds

The frontend announcement popup embeds a Feishu document. When opened in a browser, Feishu’s JS SDK requires a server-signed URL. The GET /api/feishu-jsapi-signature?url=<current-page-url> endpoint handles this automatically — it needs only FEISHU_APP_ID and FEISHU_APP_SECRET to be set.

Webhook Auto-Sync

For real-time creation board updates, configure Feishu Automation to call the sync webhook whenever a new record is added:
POST https://your-domain.com/api/admin/sync/webhook
Authorization: Bearer <SYNC_WEBHOOK_SECRET>
The job runs asynchronously — the webhook returns 202 Accepted immediately and the sync completes in the background. See the Webhook API reference for the full configuration guide.
Set SYNC_WEBHOOK_SECRET to a value different from ADMIN_PASSWORD. If you use the ?secret= query-param authentication method (supported by some Feishu Automation configurations), the secret appears in server access logs — a separate secret limits the blast radius.

Build docs developers (and LLMs) love