feat(web): 新增更新日志、服务条款、隐私政策页面;关于页更新日志链接修正
- web/dist/changelog/ 版本历史页,收录 v1.0.0–v1.0.29 全部发版记录 - web/dist/terms/ 服务条款页(付费/授权/数据/免责等) - web/dist/privacy/ 隐私政策页(数据收集/使用/共享/用户权利等) - 全站页脚补充 更新日志、服务条款、隐私政策三个链接 - about_screen 「更新日志」按钮链接由 /downloads/ 改为 /changelog/ - 新增 backend/cmd/issue 工具,用于签发指定门店的授权码 token Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
// issue signs a license token for a specific shop.
|
||||
// Usage: go run ./cmd/issue -shop 1 -days 365 -type annual -key <base64-private-key>
|
||||
// Or use env var: LICENSE_ED25519_PRIVATE_KEY=<key> go run ./cmd/issue -shop 1 -days 365
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/wangjia/jiu/backend/internal/util"
|
||||
)
|
||||
|
||||
func main() {
|
||||
shopID := flag.Uint64("shop", 0, "shop ID (required)")
|
||||
licenseID := flag.Uint64("license", 0, "license record ID (optional, 0 = omit)")
|
||||
days := flag.Int("days", 365, "validity days; 0 = perpetual (no expiry)")
|
||||
licType := flag.String("type", "annual", "license type: trial | annual | lifetime")
|
||||
maxDevices := flag.Int("devices", 3, "max devices")
|
||||
privKey := flag.String("key", "", "Ed25519 private key (base64); falls back to LICENSE_ED25519_PRIVATE_KEY env")
|
||||
flag.Parse()
|
||||
|
||||
if *shopID == 0 {
|
||||
fmt.Fprintln(os.Stderr, "error: -shop is required")
|
||||
flag.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
key := *privKey
|
||||
if key == "" {
|
||||
key = os.Getenv("LICENSE_ED25519_PRIVATE_KEY")
|
||||
}
|
||||
if key == "" {
|
||||
fmt.Fprintln(os.Stderr, "error: provide -key or set LICENSE_ED25519_PRIVATE_KEY")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
payload := util.LicensePayload{
|
||||
ShopID: *shopID,
|
||||
Type: *licType,
|
||||
IssuedAt: now.Unix(),
|
||||
MaxDevices: *maxDevices,
|
||||
}
|
||||
if *licenseID > 0 {
|
||||
payload.LicenseID = *licenseID
|
||||
}
|
||||
if *days > 0 {
|
||||
exp := now.Add(time.Duration(*days) * 24 * time.Hour).Unix()
|
||||
payload.ExpiresAt = &exp
|
||||
}
|
||||
|
||||
token, err := util.IssueLicenseToken(payload, key)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "sign failed: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
out, _ := json.MarshalIndent(payload, "", " ")
|
||||
fmt.Println("=== License Token ===")
|
||||
fmt.Println(token)
|
||||
fmt.Println()
|
||||
fmt.Println("=== Payload ===")
|
||||
fmt.Println(string(out))
|
||||
if payload.ExpiresAt != nil {
|
||||
fmt.Printf("\nExpires: %s\n", time.Unix(*payload.ExpiresAt, 0).Format("2006-01-02 15:04:05"))
|
||||
} else {
|
||||
fmt.Println("\nExpires: never (perpetual)")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user