174 lines
5.3 KiB
JavaScript
174 lines
5.3 KiB
JavaScript
const express = require('express');
|
|
const axios = require('axios');
|
|
const router = express.Router();
|
|
|
|
const NOTION_TOKEN = process.env.NOTION_TOKEN;
|
|
const DEVELOPERS_DB_ID = process.env.DEVELOPERS_DB_ID || '';
|
|
const NOTION_VERSION = '2022-06-28';
|
|
const BASE_URL = 'https://api.notion.com/v1';
|
|
|
|
const headers = () => ({
|
|
'Authorization': 'Bearer ' + NOTION_TOKEN,
|
|
'Content-Type': 'application/json',
|
|
'Notion-Version': NOTION_VERSION
|
|
});
|
|
|
|
// Validate DEV-XXX format
|
|
const DEV_ID_RE = /^DEV-\d{3,}$/;
|
|
|
|
// Notion database property names (match your Notion schema)
|
|
const PROP = {
|
|
DEV_ID: 'DEV编号',
|
|
DEV_ID_ALT: 'DEV ID',
|
|
TITLE: '标题',
|
|
NAME: '姓名',
|
|
NAME_ALT: '名称',
|
|
ROLE: '角色',
|
|
JOB_TITLE: '职位',
|
|
JOB_TITLE_ALT: '岗位',
|
|
EMAIL: '邮箱',
|
|
GITHUB: 'GitHub',
|
|
STATUS: '状态',
|
|
};
|
|
|
|
// Build Notion filter query for developer lookup by DEV ID
|
|
function devIdFilter(devId) {
|
|
return {
|
|
or: [
|
|
{ property: PROP.DEV_ID, title: { equals: devId } },
|
|
{ property: PROP.DEV_ID_ALT, title: { equals: devId } },
|
|
{ property: PROP.TITLE, title: { equals: devId } }
|
|
]
|
|
};
|
|
}
|
|
|
|
// Health check
|
|
router.get('/test', (req, res) => {
|
|
res.json({
|
|
status: 'ok',
|
|
message: '开发者路由正常',
|
|
notion_configured: !!NOTION_TOKEN,
|
|
db_configured: !!DEVELOPERS_DB_ID
|
|
});
|
|
});
|
|
|
|
// Extract developer info from a Notion page result
|
|
function extractDevInfo(page) {
|
|
const props = page.properties || {};
|
|
const getText = (prop) => {
|
|
if (!prop) return '';
|
|
if (prop.title) return prop.title.map(t => t.plain_text).join('');
|
|
if (prop.rich_text) return prop.rich_text.map(t => t.plain_text).join('');
|
|
if (prop.select) return prop.select.name;
|
|
if (prop.email) return prop.email || '';
|
|
return '';
|
|
};
|
|
|
|
return {
|
|
id: page.id,
|
|
devId: getText(props[PROP.DEV_ID] || props[PROP.DEV_ID_ALT] || props[PROP.TITLE]),
|
|
name: getText(props[PROP.NAME] || props[PROP.NAME_ALT]),
|
|
role: getText(props[PROP.ROLE]),
|
|
title: getText(props[PROP.JOB_TITLE] || props[PROP.JOB_TITLE_ALT]),
|
|
email: getText(props[PROP.EMAIL]),
|
|
github: getText(props[PROP.GITHUB]),
|
|
status: getText(props[PROP.STATUS]),
|
|
notionUrl: page.url || ''
|
|
};
|
|
}
|
|
|
|
// GET /api/v1/developers/:devId — Look up developer by DEV-XXX ID from Notion
|
|
router.get('/:devId', async (req, res) => {
|
|
const devId = (req.params.devId || '').trim().toUpperCase();
|
|
|
|
if (!DEV_ID_RE.test(devId)) {
|
|
return res.status(400).json({ error: '开发者编号格式不正确,请使用 DEV-XXX 格式' });
|
|
}
|
|
|
|
if (!NOTION_TOKEN) {
|
|
return res.status(503).json({ error: 'Notion API 未配置', devId: devId, fallback: true });
|
|
}
|
|
|
|
if (!DEVELOPERS_DB_ID) {
|
|
return res.status(503).json({ error: '开发者数据库未配置', devId: devId, fallback: true });
|
|
}
|
|
|
|
try {
|
|
const response = await axios.post(
|
|
BASE_URL + '/databases/' + DEVELOPERS_DB_ID + '/query',
|
|
{ filter: devIdFilter(devId), page_size: 1 },
|
|
{ headers: headers() }
|
|
);
|
|
|
|
const results = response.data.results || [];
|
|
if (results.length > 0) {
|
|
const dev = extractDevInfo(results[0]);
|
|
return res.json({ found: true, developer: dev });
|
|
}
|
|
|
|
return res.json({ found: false, devId: devId, message: '未在 Notion 中找到该开发者,将使用默认配置' });
|
|
} catch (err) {
|
|
const errStatus = err.response ? err.response.status : 500;
|
|
const detail = err.response ? err.response.data : null;
|
|
return res.status(errStatus >= 400 && errStatus < 600 ? errStatus : 500).json({
|
|
error: '查询 Notion 失败: ' + err.message,
|
|
devId: devId,
|
|
fallback: true,
|
|
detail: detail
|
|
});
|
|
}
|
|
});
|
|
|
|
// POST /api/v1/developers — Register a new developer in Notion
|
|
router.post('/', async (req, res) => {
|
|
const { devId, name } = req.body || {};
|
|
|
|
if (!devId || !DEV_ID_RE.test(devId)) {
|
|
return res.status(400).json({ error: '开发者编号格式不正确' });
|
|
}
|
|
|
|
if (!NOTION_TOKEN || !DEVELOPERS_DB_ID) {
|
|
return res.status(503).json({ error: 'Notion 未配置', devId: devId, fallback: true });
|
|
}
|
|
|
|
try {
|
|
// Check if already exists
|
|
const checkResp = await axios.post(
|
|
BASE_URL + '/databases/' + DEVELOPERS_DB_ID + '/query',
|
|
{ filter: devIdFilter(devId), page_size: 1 },
|
|
{ headers: headers() }
|
|
);
|
|
|
|
if (checkResp.data.results && checkResp.data.results.length > 0) {
|
|
return res.json({ created: false, message: '开发者已存在', developer: extractDevInfo(checkResp.data.results[0]) });
|
|
}
|
|
|
|
// Create new developer page
|
|
const newPage = await axios.post(
|
|
BASE_URL + '/pages',
|
|
{
|
|
parent: { database_id: DEVELOPERS_DB_ID },
|
|
properties: {
|
|
[PROP.TITLE]: { title: [{ text: { content: devId } }] },
|
|
[PROP.NAME]: { rich_text: [{ text: { content: name || devId } }] },
|
|
[PROP.ROLE]: { select: { name: '开发者' } },
|
|
[PROP.STATUS]: { select: { name: '活跃' } }
|
|
}
|
|
},
|
|
{ headers: headers() }
|
|
);
|
|
|
|
return res.json({ created: true, message: '开发者已自动注册', developer: extractDevInfo(newPage.data) });
|
|
} catch (err) {
|
|
const detail = err.response ? err.response.data : null;
|
|
return res.status(500).json({
|
|
error: '注册失败: ' + err.message,
|
|
devId: devId,
|
|
fallback: true,
|
|
detail: detail
|
|
});
|
|
}
|
|
});
|
|
|
|
module.exports = router;
|