第一期:家庭自学打卡系统
- 家长手机号登录 + 孩子设备一次性激活(长期会话) - 周模板自动生成每日任务快照,支持拍照凭证直传对象存储 - 连击计算(空白日自动达成)、家长补卡带标记 - 家长端三标签:今日概览 / 任务模板 / 设备管理 - Docker 独立输出,经 Traefik 接入 jump.hn12301.net Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
// 对象存储签名工具:浏览器直传用查询串签名(浏览器无法设置 Date 请求头)
|
||||
import crypto from "node:crypto";
|
||||
|
||||
const REGION = process.env.OSS_REGION ?? "oss-cn-hangzhou";
|
||||
const BUCKET = process.env.OSS_BUCKET ?? "";
|
||||
const ID = process.env.OSS_ACCESS_KEY_ID ?? "";
|
||||
const SECRET = process.env.OSS_ACCESS_KEY_SECRET ?? "";
|
||||
|
||||
const HOST = `${BUCKET}.${REGION}.aliyuncs.com`;
|
||||
|
||||
// 生成带签名的临时访问地址
|
||||
function signUrl(method: string, key: string, seconds: number, contentType = "") {
|
||||
const expires = Math.floor(Date.now() / 1000) + seconds;
|
||||
const resource = `/${BUCKET}/${key}`;
|
||||
const str = `${method}\n\n${contentType}\n${expires}\n${resource}`;
|
||||
const signature = crypto.createHmac("sha1", SECRET).update(str).digest("base64");
|
||||
const qs = new URLSearchParams({
|
||||
OSSAccessKeyId: ID,
|
||||
Expires: String(expires),
|
||||
Signature: signature,
|
||||
});
|
||||
return `https://${HOST}/${encodeURIComponent(key)}?${qs}`;
|
||||
}
|
||||
|
||||
// 直传地址:浏览器收到后直接 fetch PUT,有效期 10 分钟
|
||||
export function uploadUrl(key: string, contentType: string) {
|
||||
return signUrl("PUT", key, 600, contentType);
|
||||
}
|
||||
|
||||
// 展示地址:签名 GET,有效期 5 分钟,用完由页面刷新重新签发
|
||||
export function viewUrl(key: string) {
|
||||
return signUrl("GET", key, 300);
|
||||
}
|
||||
|
||||
// 照片键名:photos/孩子ID/日期/随机16进制.jpg
|
||||
export function photoKey(childId: number, date: string) {
|
||||
return `photos/${childId}/${date}/${crypto.randomBytes(8).toString("hex")}.jpg`;
|
||||
}
|
||||
Reference in New Issue
Block a user