File size: 2,284 Bytes
6d60378
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package syncer

import (
	"context"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"
	"time"
)

// RestartSpace 请求 Hugging Face 重启一个 Space。
//
// 为什么需要它:Space 上的网关只在**启动时**扫一遍凭证目录(没有文件监听),
// 所以往桶里推了新账号之后,正在运行的 Space 并不会加载它——签到时候就会漏掉这个号。
// 重启一次(30~60s 冷启动)即可让它从挂载卷重新读齐。
//
// 这也是配置成"只在推了 auths/ 下的文件时才做"的原因:改 state.json 之类不必重启,
// 白白打断正在进行中的签到任务。
func RestartSpace(ctx context.Context, endpoint, spaceID, token string, timeout time.Duration, insecureSkipVerify bool) error {
	if strings.TrimSpace(spaceID) == "" {
		return fmt.Errorf("restart_space 未配置")
	}
	if strings.TrimSpace(endpoint) == "" {
		endpoint = DefaultEndpoint
	}
	if timeout <= 0 {
		timeout = 60 * time.Second
	}
	endpoint = strings.TrimRight(endpoint, "/")

	u := fmt.Sprintf("%s/api/spaces/%s/restart", endpoint, strings.Trim(spaceID, "/"))
	req, err := http.NewRequestWithContext(ctx, http.MethodPost, u, nil)
	if err != nil {
		return err
	}
	if token != "" {
		req.Header.Set("Authorization", "Bearer "+token)
	}
	req.Header.Set("User-Agent", userAgent)

	cli := newHTTPClient(timeout, insecureSkipVerify, "")
	resp, err := cli.Do(req)
	if err != nil {
		// 把 URL 里的信息去掉,避免日志里出现带着鉴权语义的完整地址。
		return fmt.Errorf("请求重启 Space 失败: %w", err)
	}
	defer func() { _ = resp.Body.Close() }()
	body, _ := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
	if resp.StatusCode < 200 || resp.StatusCode > 299 {
		return fmt.Errorf("重启 Space 返回 %d: %s", resp.StatusCode, truncate(string(body), 300))
	}
	return nil
}

// SpaceIDFromBucket 不是必须的辅助函数,但留一个把桶 ID 归一化的入口,
// 便于调用方传 "a3216/wb2api-storage" 或 "https://huggingface.co/..." 两种写法。
func SpaceIDFromBucket(raw string) string {
	raw = strings.TrimSpace(raw)
	if raw == "" {
		return ""
	}
	if strings.Contains(raw, "://") {
		if u, err := url.Parse(raw); err == nil {
			raw = strings.Trim(u.Path, "/")
		}
	}
	return strings.Trim(raw, "/")
}