fix(cli): support repairing api-auth and stripe-key records
put --name now accepts api-auth (JSON) and stripe-key (the raw pk_live_ key), so status failure messages can point at a working repair path instead of the setup wizard, which refuses an existing vault. The wizard also prints explicit recovery instructions when it aborts after creating the vault, and validates the site origin exactly like the web server.
This commit is contained in:
1 parent
a8e7565448
commit
f18083f33e
2 files changed
+47
-28
No files matched your search
+30
-16
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@@ -71,7 +72,7 @@ func run() error {
|
||||
pay := f.Bool("pay", false, "pay only at the exact allowed BDT total")
|
||||
name := f.String("name", "", "secret name for put")
|
||||
f.Usage = func() {
|
||||
fmt.Fprintln(f.Output(), "Usage: xgift <setup|init|status|billing|import-chrome|put|proxy|check|username> [flags]\nsetup is the interactive first-time wizard; init reads a JSON object from stdin; put reads one JSON value from stdin.")
|
||||
fmt.Fprintln(f.Output(), "Usage: xgift <setup|init|status|billing|import-chrome|put|proxy|check|username> [flags]\nsetup is the interactive first-time wizard; init reads a JSON object from stdin; put reads one JSON value from stdin (stripe-key: the raw pk_live_ key).")
|
||||
f.PrintDefaults()
|
||||
}
|
||||
if err := f.Parse(rest); err != nil {
|
||||
@@ -191,22 +192,22 @@ func run() error {
|
||||
}
|
||||
raw, e := v.Get("api-auth")
|
||||
if e != nil {
|
||||
return errors.New("record api-auth is missing; run setup")
|
||||
return errors.New("record api-auth is missing; fix with put --name api-auth")
|
||||
}
|
||||
var auth struct{ Authorization string }
|
||||
if e = json.Unmarshal(raw, &auth); e != nil || !strings.HasPrefix(auth.Authorization, "Bearer ") {
|
||||
clear(raw)
|
||||
return errors.New("invalid api-auth record; run setup to rewrite it")
|
||||
return errors.New("invalid api-auth record; rewrite with put --name api-auth")
|
||||
}
|
||||
clear(raw)
|
||||
fmt.Println("api-auth: encrypted record verified")
|
||||
key, e := v.Get("stripe-key")
|
||||
if e != nil {
|
||||
return errors.New("record stripe-key is missing; run setup")
|
||||
return errors.New("record stripe-key is missing; fix with put --name stripe-key")
|
||||
}
|
||||
if !stripeKeyPattern.Match(key) {
|
||||
clear(key)
|
||||
return errors.New("invalid stripe-key record; run setup to rewrite it")
|
||||
return errors.New("invalid stripe-key record; rewrite with put --name stripe-key")
|
||||
}
|
||||
clear(key)
|
||||
fmt.Println("stripe-key: encrypted record verified")
|
||||
@@ -223,18 +224,31 @@ func run() error {
|
||||
fmt.Println("X cookies refreshed in encrypted SQLite vault")
|
||||
return nil
|
||||
case "put":
|
||||
if *name != "proxy" && *name != "card" && *name != "cookies" {
|
||||
return errors.New("--name must be proxy, card or cookies")
|
||||
switch *name {
|
||||
case "proxy", "card", "cookies", "api-auth":
|
||||
b, e := io.ReadAll(io.LimitReader(os.Stdin, 1024*1024))
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
defer clear(b)
|
||||
if !json.Valid(b) {
|
||||
return errors.New("stdin must be valid JSON")
|
||||
}
|
||||
return v.Put(*name, b)
|
||||
case "stripe-key":
|
||||
b, e := io.ReadAll(io.LimitReader(os.Stdin, 4096))
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
defer clear(b)
|
||||
b = bytes.TrimSpace(b)
|
||||
if !stripeKeyPattern.Match(b) {
|
||||
return errors.New("stdin must be the pk_live_ publishable key")
|
||||
}
|
||||
return v.Put(*name, b)
|
||||
default:
|
||||
return errors.New("--name must be proxy, card, cookies, api-auth or stripe-key")
|
||||
}
|
||||
b, e := io.ReadAll(io.LimitReader(os.Stdin, 1024*1024))
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
defer clear(b)
|
||||
if !json.Valid(b) {
|
||||
return errors.New("stdin must be valid JSON")
|
||||
}
|
||||
return v.Put(*name, b)
|
||||
}
|
||||
if command != "proxy" && command != "check" && !regexp.MustCompile(`^@?[A-Za-z0-9_]{1,15}$`).MatchString(command) {
|
||||
return errors.New("invalid command or X username")
|
||||
|
||||
+17
-12
@@ -189,17 +189,22 @@ func runSetup(ctx context.Context, db, passwordFileFlag string) error {
|
||||
defer v.Close()
|
||||
fmt.Printf("加密保管库已创建:%s\n\n", db)
|
||||
|
||||
if err = w.setupXCredentials(v); err != nil {
|
||||
return err
|
||||
steps := []func() error{
|
||||
func() error { return w.setupXCredentials(v) },
|
||||
func() error { return w.setupCard(v) },
|
||||
func() error { return w.setupProxy(ctx, v) },
|
||||
func() error { return w.setupStripeKey(v) },
|
||||
}
|
||||
if err = w.setupCard(v); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = w.setupProxy(ctx, v); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = w.setupStripeKey(v); err != nil {
|
||||
return err
|
||||
for _, step := range steps {
|
||||
if err = step(); err != nil {
|
||||
fmt.Println()
|
||||
fmt.Printf("配置未完成:%v\n", err)
|
||||
fmt.Println("已创建的保管库会保留已写入的记录。修复方式:")
|
||||
fmt.Printf(" 1) 删除 %s、%s 和旁边的 password-path 后重新运行 setup;或\n", db, passwordFile)
|
||||
fmt.Println(" 2) 用 put --name <proxy|card|cookies|api-auth|stripe-key> / billing / import-chrome 补写缺失记录,")
|
||||
fmt.Println(" 再以 status 确认全部记录就绪。")
|
||||
return err
|
||||
}
|
||||
}
|
||||
siteConfigured, err := w.setupSite(db, passwordFile)
|
||||
if err != nil {
|
||||
@@ -550,8 +555,8 @@ func (w *wizard) setupSite(db, passwordFile string) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
u, err := url.Parse(origin)
|
||||
if err != nil || u.Scheme != "https" || u.Host == "" || (u.Path != "" && u.Path != "/") || u.RawQuery != "" || u.Fragment != "" {
|
||||
return false, errors.New("Origin 必须是 https://<主机名> 形式")
|
||||
if err != nil || u.Scheme != "https" || u.Host == "" || u.Path != "" || u.RawQuery != "" || u.Fragment != "" || u.User != nil {
|
||||
return false, errors.New("Origin 必须是 https://<主机名> 形式(不带路径、查询或用户信息)")
|
||||
}
|
||||
listen, err := w.prompt("监听地址", "127.0.0.1:8787")
|
||||
if err != nil {
|
||||
|
||||
Reference in new issue
Block a user