This commit is contained in:
2026-06-24 10:04:03 +08:00
parent b103192fac
commit 0f961789dc
538 changed files with 128210 additions and 128008 deletions
+63 -63
View File
@@ -1,63 +1,63 @@
package jwtutil
import (
"errors"
"time"
"github.com/golang-jwt/jwt/v5"
)
// 密钥(后续可从配置中读取)
var secret = []byte("yunzer_jwt_secret_key")
// Claims 定义JWT的claims结构
type Claims struct {
UserID int `json:"user_id"`
Username string `json:"username"`
TenantId int `json:"tenant_id"` // 租户ID
UserType string `json:"user_type"` // 用户类型:"user" / "employee" / "platform" 等
jwt.RegisteredClaims
}
// GenerateToken 生成JWT token
func GenerateToken(userID int, username string, tenantId int, userType string) (string, error) {
expirationTime := time.Now().Add(24 * time.Hour)
claims := &Claims{
UserID: userID,
Username: username,
TenantId: tenantId,
UserType: userType,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(expirationTime),
IssuedAt: jwt.NewNumericDate(time.Now()),
NotBefore: jwt.NewNumericDate(time.Now()),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString(secret)
return tokenString, err
}
// ParseToken 解析JWT token
func ParseToken(tokenString string) (*Claims, error) {
claims := &Claims{}
token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, errors.New("unexpected signing method")
}
return secret, nil
})
if err != nil {
return nil, err
}
if !token.Valid {
return nil, errors.New("invalid token")
}
return claims, nil
}
package jwtutil
import (
"errors"
"time"
"github.com/golang-jwt/jwt/v5"
)
// 密钥(后续可从配置中读取)
var secret = []byte("yunzer_jwt_secret_key")
// Claims 定义JWT的claims结构
type Claims struct {
UserID int `json:"user_id"`
Username string `json:"username"`
TenantId int `json:"tenant_id"` // 租户ID
UserType string `json:"user_type"` // 用户类型:"user" / "employee" / "platform" 等
jwt.RegisteredClaims
}
// GenerateToken 生成JWT token
func GenerateToken(userID int, username string, tenantId int, userType string) (string, error) {
expirationTime := time.Now().Add(24 * time.Hour)
claims := &Claims{
UserID: userID,
Username: username,
TenantId: tenantId,
UserType: userType,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(expirationTime),
IssuedAt: jwt.NewNumericDate(time.Now()),
NotBefore: jwt.NewNumericDate(time.Now()),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString(secret)
return tokenString, err
}
// ParseToken 解析JWT token
func ParseToken(tokenString string) (*Claims, error) {
claims := &Claims{}
token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, errors.New("unexpected signing method")
}
return secret, nil
})
if err != nil {
return nil, err
}
if !token.Valid {
return nil, errors.New("invalid token")
}
return claims, nil
}
+55 -55
View File
@@ -1,55 +1,55 @@
package passwordutil
import (
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"errors"
"strings"
)
const (
saltBytes = 16
separator = "$"
hashLength = 64 // sha256 hex length
)
// Hash 生成 salt+hash 的存储串,格式:salt$hash(均为 hex
func Hash(plain string) (string, error) {
plain = strings.TrimSpace(plain)
if plain == "" {
return "", errors.New("password 不能为空")
}
salt := make([]byte, saltBytes)
if _, err := rand.Read(salt); err != nil {
return "", err
}
saltHex := hex.EncodeToString(salt)
hashHex := hashHex(saltHex, plain)
return saltHex + separator + hashHex, nil
}
// Verify 校验存储串(salt$hash)是否匹配输入明文密码。
func Verify(stored, plain string) bool {
stored = strings.TrimSpace(stored)
plain = strings.TrimSpace(plain)
if stored == "" || plain == "" {
return false
}
parts := strings.Split(stored, separator)
if len(parts) != 2 {
return false
}
saltHex := strings.TrimSpace(parts[0])
hashHexStored := strings.TrimSpace(parts[1])
if saltHex == "" || len(hashHexStored) != hashLength {
return false
}
return hashHex(saltHex, plain) == strings.ToLower(hashHexStored)
}
func hashHex(saltHex, plain string) string {
sum := sha256.Sum256([]byte(saltHex + plain))
return hex.EncodeToString(sum[:])
}
package passwordutil
import (
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"errors"
"strings"
)
const (
saltBytes = 16
separator = "$"
hashLength = 64 // sha256 hex length
)
// Hash 生成 salt+hash 的存储串,格式:salt$hash(均为 hex
func Hash(plain string) (string, error) {
plain = strings.TrimSpace(plain)
if plain == "" {
return "", errors.New("password 不能为空")
}
salt := make([]byte, saltBytes)
if _, err := rand.Read(salt); err != nil {
return "", err
}
saltHex := hex.EncodeToString(salt)
hashHex := hashHex(saltHex, plain)
return saltHex + separator + hashHex, nil
}
// Verify 校验存储串(salt$hash)是否匹配输入明文密码。
func Verify(stored, plain string) bool {
stored = strings.TrimSpace(stored)
plain = strings.TrimSpace(plain)
if stored == "" || plain == "" {
return false
}
parts := strings.Split(stored, separator)
if len(parts) != 2 {
return false
}
saltHex := strings.TrimSpace(parts[0])
hashHexStored := strings.TrimSpace(parts[1])
if saltHex == "" || len(hashHexStored) != hashLength {
return false
}
return hashHex(saltHex, plain) == strings.ToLower(hashHexStored)
}
func hashHex(saltHex, plain string) string {
sum := sha256.Sum256([]byte(saltHex + plain))
return hex.EncodeToString(sum[:])
}
File diff suppressed because it is too large Load Diff
+217 -217
View File
@@ -1,217 +1,217 @@
package tokenprobe
import (
"bytes"
"crypto/tls"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"time"
)
var httpClient = &http.Client{
Timeout: 12 * time.Second,
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
}
type Result struct {
OK bool `json:"ok"`
Detail string `json:"detail"`
HTTPStatus int `json:"httpStatus"`
ProbeMessage string `json:"probeMessage,omitempty"`
Endpoint string `json:"endpoint,omitempty"`
BytesRead int `json:"bytesRead,omitempty"`
RawPreview string `json:"rawPreview,omitempty"`
RequestBodyPrefixHex string `json:"requestBodyPrefixHex,omitempty"`
StreamProtocol string `json:"streamProtocol,omitempty"`
StreamNote string `json:"streamNote,omitempty"`
}
func ProbeOfficial(module, rawToken string) Result {
tok := normalizeBearerToken(strings.TrimSpace(rawToken))
if tok == "" {
return Result{OK: false, Detail: "Token 为空"}
}
switch module {
case "cursor":
return probeCursor(tok)
case "windsurf":
return probeWindsurf(tok)
case "krio":
return probeKiro(tok)
default:
return Result{OK: false, Detail: "未知模块"}
}
}
func normalizeBearerToken(s string) string {
s = strings.TrimSpace(s)
if i := strings.LastIndex(s, "::"); i >= 0 {
return strings.TrimSpace(s[i+2:])
}
return s
}
func probeCursor(token string) Result {
return probeCursorHiAgent(token)
}
func probeWindsurf(apiKey string) Result {
payload := map[string]interface{}{
"metadata": map[string]string{
"apiKey": apiKey,
"ideName": "windsurf",
"ideVersion": "0.0.0",
"extensionName": "windsurf",
"extensionVersion": "0.0.0",
"locale": "zh",
},
}
raw, err := json.Marshal(payload)
if err != nil {
return Result{OK: false, Detail: err.Error()}
}
req, err := http.NewRequest(
http.MethodPost,
"https://server.codeium.com/exa.seat_management_pb.SeatManagementService/GetUserStatus",
bytes.NewReader(raw),
)
if err != nil {
return Result{OK: false, Detail: err.Error()}
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Connect-Protocol-Version", "1")
resp, err := httpClient.Do(req)
if err != nil {
return Result{OK: false, Detail: "请求失败: " + err.Error()}
}
defer resp.Body.Close()
body, _ := io.ReadAll(io.LimitReader(resp.Body, 8192))
switch resp.StatusCode {
case http.StatusOK:
var wrap map[string]interface{}
if json.Unmarshal(body, &wrap) == nil {
if _, ok := wrap["userStatus"]; ok {
return Result{OK: true, Detail: "Codeium 云端接口响应正常", HTTPStatus: resp.StatusCode}
}
}
if bytes.Contains(body, []byte(`"planStatus"`)) || bytes.Contains(body, []byte(`"userStatus"`)) {
return Result{OK: true, Detail: "Codeium 云端接口响应正常", HTTPStatus: resp.StatusCode}
}
return Result{OK: true, Detail: fmt.Sprintf("HTTP %d,已收到响应", resp.StatusCode), HTTPStatus: resp.StatusCode}
case http.StatusUnauthorized, http.StatusForbidden:
return Result{OK: false, Detail: fmt.Sprintf("API Key 无效或已失效(HTTP %d", resp.StatusCode), HTTPStatus: resp.StatusCode}
default:
snip := strings.TrimSpace(string(body))
if len(snip) > 220 {
snip = snip[:220] + "…"
}
return Result{OK: false, Detail: fmt.Sprintf("HTTP %d %s", resp.StatusCode, snip), HTTPStatus: resp.StatusCode}
}
}
func probeKiro(accessToken string) Result {
arn := findProfileArnInJWT(accessToken)
if arn == "" {
return Result{
OK: false,
Detail: "无法从 Token 中解析 profileArnKiro 暂无法自动探测",
}
}
q := url.Values{}
q.Set("origin", "AI_EDITOR")
q.Set("profileArn", arn)
q.Set("resourceType", "AGENTIC_REQUEST")
u := "https://q.us-east-1.amazonaws.com/getUsageLimits?" + q.Encode()
req, err := http.NewRequest(http.MethodGet, u, nil)
if err != nil {
return Result{OK: false, Detail: err.Error()}
}
req.Header.Set("Authorization", "Bearer "+normalizeBearerToken(accessToken))
req.Header.Set("Accept", "application/json")
resp, err := httpClient.Do(req)
if err != nil {
return Result{OK: false, Detail: "请求失败: " + err.Error()}
}
defer resp.Body.Close()
body, _ := io.ReadAll(io.LimitReader(resp.Body, 4096))
switch resp.StatusCode {
case http.StatusOK:
return Result{OK: true, Detail: "KiroAWS Q)用量接口响应正常", HTTPStatus: resp.StatusCode}
case http.StatusUnauthorized, http.StatusForbidden:
return Result{OK: false, Detail: fmt.Sprintf("Token 无效或已过期(HTTP %d", resp.StatusCode), HTTPStatus: resp.StatusCode}
default:
snip := strings.TrimSpace(string(body))
if len(snip) > 220 {
snip = snip[:220] + "…"
}
return Result{OK: false, Detail: fmt.Sprintf("HTTP %d %s", resp.StatusCode, snip), HTTPStatus: resp.StatusCode}
}
}
func decodeJWTPayloadMap(raw string) (map[string]interface{}, error) {
tok := normalizeBearerToken(strings.TrimSpace(raw))
parts := strings.Split(tok, ".")
if len(parts) < 2 {
return nil, fmt.Errorf("not a JWT")
}
b, err := base64.RawURLEncoding.DecodeString(parts[1])
if err != nil {
return nil, err
}
var m map[string]interface{}
if err := json.Unmarshal(b, &m); err != nil {
return nil, err
}
return m, nil
}
func findProfileArnInJWT(raw string) string {
m, err := decodeJWTPayloadMap(raw)
if err != nil {
return ""
}
return findProfileArnValue(m)
}
func findProfileArnValue(v interface{}) string {
switch x := v.(type) {
case map[string]interface{}:
for k, val := range x {
lk := strings.ToLower(k)
if lk == "profilearn" || lk == "profile_arn" {
if s, ok := val.(string); ok && strings.Contains(s, "arn:") {
return s
}
}
}
for _, val := range x {
if s := findProfileArnValue(val); s != "" {
return s
}
}
case []interface{}:
for _, el := range x {
if s := findProfileArnValue(el); s != "" {
return s
}
}
case string:
if strings.Contains(x, "arn:aws:codewhisperer") && strings.Contains(x, ":profile/") {
return x
}
}
return ""
}
package tokenprobe
import (
"bytes"
"crypto/tls"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"time"
)
var httpClient = &http.Client{
Timeout: 12 * time.Second,
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
}
type Result struct {
OK bool `json:"ok"`
Detail string `json:"detail"`
HTTPStatus int `json:"httpStatus"`
ProbeMessage string `json:"probeMessage,omitempty"`
Endpoint string `json:"endpoint,omitempty"`
BytesRead int `json:"bytesRead,omitempty"`
RawPreview string `json:"rawPreview,omitempty"`
RequestBodyPrefixHex string `json:"requestBodyPrefixHex,omitempty"`
StreamProtocol string `json:"streamProtocol,omitempty"`
StreamNote string `json:"streamNote,omitempty"`
}
func ProbeOfficial(module, rawToken string) Result {
tok := normalizeBearerToken(strings.TrimSpace(rawToken))
if tok == "" {
return Result{OK: false, Detail: "Token 为空"}
}
switch module {
case "cursor":
return probeCursor(tok)
case "windsurf":
return probeWindsurf(tok)
case "krio":
return probeKiro(tok)
default:
return Result{OK: false, Detail: "未知模块"}
}
}
func normalizeBearerToken(s string) string {
s = strings.TrimSpace(s)
if i := strings.LastIndex(s, "::"); i >= 0 {
return strings.TrimSpace(s[i+2:])
}
return s
}
func probeCursor(token string) Result {
return probeCursorHiAgent(token)
}
func probeWindsurf(apiKey string) Result {
payload := map[string]interface{}{
"metadata": map[string]string{
"apiKey": apiKey,
"ideName": "windsurf",
"ideVersion": "0.0.0",
"extensionName": "windsurf",
"extensionVersion": "0.0.0",
"locale": "zh",
},
}
raw, err := json.Marshal(payload)
if err != nil {
return Result{OK: false, Detail: err.Error()}
}
req, err := http.NewRequest(
http.MethodPost,
"https://server.codeium.com/exa.seat_management_pb.SeatManagementService/GetUserStatus",
bytes.NewReader(raw),
)
if err != nil {
return Result{OK: false, Detail: err.Error()}
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Connect-Protocol-Version", "1")
resp, err := httpClient.Do(req)
if err != nil {
return Result{OK: false, Detail: "请求失败: " + err.Error()}
}
defer resp.Body.Close()
body, _ := io.ReadAll(io.LimitReader(resp.Body, 8192))
switch resp.StatusCode {
case http.StatusOK:
var wrap map[string]interface{}
if json.Unmarshal(body, &wrap) == nil {
if _, ok := wrap["userStatus"]; ok {
return Result{OK: true, Detail: "Codeium 云端接口响应正常", HTTPStatus: resp.StatusCode}
}
}
if bytes.Contains(body, []byte(`"planStatus"`)) || bytes.Contains(body, []byte(`"userStatus"`)) {
return Result{OK: true, Detail: "Codeium 云端接口响应正常", HTTPStatus: resp.StatusCode}
}
return Result{OK: true, Detail: fmt.Sprintf("HTTP %d,已收到响应", resp.StatusCode), HTTPStatus: resp.StatusCode}
case http.StatusUnauthorized, http.StatusForbidden:
return Result{OK: false, Detail: fmt.Sprintf("API Key 无效或已失效(HTTP %d", resp.StatusCode), HTTPStatus: resp.StatusCode}
default:
snip := strings.TrimSpace(string(body))
if len(snip) > 220 {
snip = snip[:220] + "…"
}
return Result{OK: false, Detail: fmt.Sprintf("HTTP %d %s", resp.StatusCode, snip), HTTPStatus: resp.StatusCode}
}
}
func probeKiro(accessToken string) Result {
arn := findProfileArnInJWT(accessToken)
if arn == "" {
return Result{
OK: false,
Detail: "无法从 Token 中解析 profileArnKiro 暂无法自动探测",
}
}
q := url.Values{}
q.Set("origin", "AI_EDITOR")
q.Set("profileArn", arn)
q.Set("resourceType", "AGENTIC_REQUEST")
u := "https://q.us-east-1.amazonaws.com/getUsageLimits?" + q.Encode()
req, err := http.NewRequest(http.MethodGet, u, nil)
if err != nil {
return Result{OK: false, Detail: err.Error()}
}
req.Header.Set("Authorization", "Bearer "+normalizeBearerToken(accessToken))
req.Header.Set("Accept", "application/json")
resp, err := httpClient.Do(req)
if err != nil {
return Result{OK: false, Detail: "请求失败: " + err.Error()}
}
defer resp.Body.Close()
body, _ := io.ReadAll(io.LimitReader(resp.Body, 4096))
switch resp.StatusCode {
case http.StatusOK:
return Result{OK: true, Detail: "KiroAWS Q)用量接口响应正常", HTTPStatus: resp.StatusCode}
case http.StatusUnauthorized, http.StatusForbidden:
return Result{OK: false, Detail: fmt.Sprintf("Token 无效或已过期(HTTP %d", resp.StatusCode), HTTPStatus: resp.StatusCode}
default:
snip := strings.TrimSpace(string(body))
if len(snip) > 220 {
snip = snip[:220] + "…"
}
return Result{OK: false, Detail: fmt.Sprintf("HTTP %d %s", resp.StatusCode, snip), HTTPStatus: resp.StatusCode}
}
}
func decodeJWTPayloadMap(raw string) (map[string]interface{}, error) {
tok := normalizeBearerToken(strings.TrimSpace(raw))
parts := strings.Split(tok, ".")
if len(parts) < 2 {
return nil, fmt.Errorf("not a JWT")
}
b, err := base64.RawURLEncoding.DecodeString(parts[1])
if err != nil {
return nil, err
}
var m map[string]interface{}
if err := json.Unmarshal(b, &m); err != nil {
return nil, err
}
return m, nil
}
func findProfileArnInJWT(raw string) string {
m, err := decodeJWTPayloadMap(raw)
if err != nil {
return ""
}
return findProfileArnValue(m)
}
func findProfileArnValue(v interface{}) string {
switch x := v.(type) {
case map[string]interface{}:
for k, val := range x {
lk := strings.ToLower(k)
if lk == "profilearn" || lk == "profile_arn" {
if s, ok := val.(string); ok && strings.Contains(s, "arn:") {
return s
}
}
}
for _, val := range x {
if s := findProfileArnValue(val); s != "" {
return s
}
}
case []interface{}:
for _, el := range x {
if s := findProfileArnValue(el); s != "" {
return s
}
}
case string:
if strings.Contains(x, "arn:aws:codewhisperer") && strings.Contains(x, ":profile/") {
return x
}
}
return ""
}
+57 -57
View File
@@ -1,57 +1,57 @@
package versionutil
import (
"strconv"
"strings"
)
// Compare 比较语义化版本号(按段数字比较,如 1.10.0 > 1.9.0)。不支持复杂 pre-release 规则。
// 返回 -1 表示 a < b0 表示相等,1 表示 a > b。
func Compare(a, b string) int {
pa := parseParts(a)
pb := parseParts(b)
maxLen := len(pa)
if len(pb) > maxLen {
maxLen = len(pb)
}
for i := 0; i < maxLen; i++ {
var xa, xb int64
if i < len(pa) {
xa = pa[i]
}
if i < len(pb) {
xb = pb[i]
}
if xa < xb {
return -1
}
if xa > xb {
return 1
}
}
return 0
}
func parseParts(s string) []int64 {
s = strings.TrimSpace(s)
if s == "" {
return []int64{0}
}
if i := strings.IndexByte(s, '-'); i >= 0 {
s = s[:i]
}
parts := strings.Split(s, ".")
out := make([]int64, 0, len(parts))
for _, p := range parts {
p = strings.TrimSpace(p)
n, err := strconv.ParseInt(p, 10, 64)
if err != nil {
n = 0
}
out = append(out, n)
}
if len(out) == 0 {
return []int64{0}
}
return out
}
package versionutil
import (
"strconv"
"strings"
)
// Compare 比较语义化版本号(按段数字比较,如 1.10.0 > 1.9.0)。不支持复杂 pre-release 规则。
// 返回 -1 表示 a < b0 表示相等,1 表示 a > b。
func Compare(a, b string) int {
pa := parseParts(a)
pb := parseParts(b)
maxLen := len(pa)
if len(pb) > maxLen {
maxLen = len(pb)
}
for i := 0; i < maxLen; i++ {
var xa, xb int64
if i < len(pa) {
xa = pa[i]
}
if i < len(pb) {
xb = pb[i]
}
if xa < xb {
return -1
}
if xa > xb {
return 1
}
}
return 0
}
func parseParts(s string) []int64 {
s = strings.TrimSpace(s)
if s == "" {
return []int64{0}
}
if i := strings.IndexByte(s, '-'); i >= 0 {
s = s[:i]
}
parts := strings.Split(s, ".")
out := make([]int64, 0, len(parts))
for _, p := range parts {
p = strings.TrimSpace(p)
n, err := strconv.ParseInt(p, 10, 64)
if err != nil {
n = 0
}
out = append(out, n)
}
if len(out) == 0 {
return []int64{0}
}
return out
}