package devcontainer import ( "context" "encoding/json" "io" "net/http" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/log" ) type Devcontainer struct { Id int64 `xorm:"BIGINT pk NOT NULL autoincr 'id' comment('主键,devContainerId')"` Name string `xorm:"VARCHAR(64) charset=utf8mb4 collate=utf8mb4_bin UNIQUE NOT NULL 'name' comment('devContainer名称,自动生成')"` DevcontainerHost string `xorm:"VARCHAR(256) charset=utf8mb4 collate=utf8mb4_bin NOT NULL 'devcontainer_host' comment('SSH Host')"` DevcontainerPort uint16 `xorm:"SMALLINT UNSIGNED NOT NULL 'devcontainer_port' comment('SSH Port')"` DevcontainerStatus uint16 `xorm:"SMALLINT UNSIGNED NOT NULL 'devcontainer_status' comment('SSH Status')"` DevcontainerUsername string `xorm:"VARCHAR(32) charset=utf8mb4 collate=utf8mb4_bin NOT NULL 'devcontainer_username' comment('SSH Username')"` DevcontainerWorkDir string `xorm:"VARCHAR(256) charset=utf8mb4 collate=utf8mb4_bin NOT NULL 'devcontainer_work_dir' comment('SSH 工作路径,典型值 ~/${project_name},256字节以内')"` RepoId int64 `xorm:"BIGINT NOT NULL FK('repo_id') REFERENCES repository(id) ON DELETE CASCADE 'repo_id' comment('repository表主键')"` UserId int64 `xorm:"BIGINT NOT NULL FK('user_id') REFERENCES user(id) ON DELETE CASCADE 'user_id' comment('user表主键')"` CreatedUnix int64 `xorm:"BIGINT 'created_unix' comment('创建时间戳')"` UpdatedUnix int64 `xorm:"BIGINT 'updated_unix' comment('更新时间戳')"` } type DevcontainerOutput struct { Id int64 `xorm:"BIGINT pk NOT NULL autoincr 'id' comment('主键,devContainerId')"` RepoId int64 `xorm:"BIGINT NOT NULL unique(uniquename) 'repo_id' comment('repository表主键')"` UserId int64 `xorm:"BIGINT NOT NULL unique(uniquename) 'user_id' comment('user表主键')"` Status string `xorm:"VARCHAR(255) charset=utf8mb4 collate=utf8mb4_bin NOT NULL 'status' comment('status')"` Output string `xorm:"TEXT 'output' comment('output')"` Command string `xorm:"TEXT 'command' comment('command')"` ListId int64 `xorm:"BIGINT NOT NULL unique(uniquename) 'list_id' comment('list_id')"` DevcontainerId int64 `xorm:"BIGINT NOT NULL FK('devcontainer_id') REFERENCES devcontainer(id) ON DELETE CASCADE 'devcontainer_id' comment('devcontainer表主键')"` } type DevcontainerScript struct { Id int64 `xorm:"BIGINT pk NOT NULL autoincr 'id' comment('主键,devContainerId')"` RepoId int64 `xorm:"BIGINT NOT NULL 'repo_id' comment('repository表主键')"` UserId int64 `xorm:"BIGINT NOT NULL 'user_id' comment('user表主键')"` VariableName string `xorm:"NOT NULL 'variable_name' comment('user表主键')"` } func init() { db.RegisterModel(new(Devcontainer)) db.RegisterModel(new(DevcontainerScript)) db.RegisterModel(new(DevcontainerOutput)) } func GetScript(ctx context.Context, userId, repoID int64) (map[string]string, error) { variables := make(map[string]string) var devstarVariables []*DevcontainerVariable var name []string // Devstar level // 从远程获取Devstar变量 client := &http.Client{} req, err := http.NewRequest("GET", "http://devstar.cn/variables/export", nil) if err != nil { log.Error("Failed to create request for devstar variables: %v", err) } else { resp, err := client.Do(req) if err != nil { log.Error("Failed to fetch devstar variables: %v", err) } else { defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { log.Error("Failed to read devstar variables response: %v", err) } else { err = json.Unmarshal(body, &devstarVariables) if err != nil { log.Error("Failed to unmarshal devstar variables: %v", err) } } } } // Global err = db.GetEngine(ctx). Select("variable_name"). Table("devcontainer_script"). Where("user_id = ? AND repo_id = ?", 0, 0). Find(&name) globalVariables, err := db.Find[DevcontainerVariable](ctx, FindVariablesOpts{}) if err != nil { log.Error("find global variables: %v", err) return nil, err } // 过滤出name在variableNames中的变量 globalVariables = append(devstarVariables, globalVariables...) var filteredGlobalVars []*DevcontainerVariable for _, v := range globalVariables { if contains(name, v.Name) { filteredGlobalVars = append(filteredGlobalVars, v) } } // Org / User level err = db.GetEngine(ctx). Select("variable_name"). Table("devcontainer_script"). Where("user_id = ? AND repo_id = ?", userId, 0). Find(&name) ownerVariables, err := db.Find[DevcontainerVariable](ctx, FindVariablesOpts{OwnerID: userId}) if err != nil { log.Error("find variables of org: %d, error: %v", userId, err) return nil, err } // 过滤出name在variableNames中的变量 ownerVariables = append(devstarVariables, ownerVariables...) var filteredOwnerVars []*DevcontainerVariable for _, v := range ownerVariables { if contains(name, v.Name) { filteredOwnerVars = append(filteredOwnerVars, v) } } // Repo level err = db.GetEngine(ctx). Select("variable_name"). Table("devcontainer_script"). Where("repo_id = ?", repoID). Find(&name) repoVariables, err := db.Find[DevcontainerVariable](ctx, FindVariablesOpts{RepoID: repoID}) if err != nil { log.Error("find variables of repo: %d, error: %v", repoID, err) return nil, err } // 过滤出name在variableNames中的变量 repoVariables = append(devstarVariables, repoVariables...) var filteredRepoVars []*DevcontainerVariable for _, v := range repoVariables { if contains(name, v.Name) { filteredRepoVars = append(filteredRepoVars, v) } } // Level precedence: Org / User > Repo > Global for _, v := range append(filteredGlobalVars, append(filteredRepoVars, filteredOwnerVars...)...) { variables[v.Name] = v.Data } return variables, nil } // contains 检查字符串切片中是否包含指定的字符串 func contains(slice []string, item string) bool { for _, s := range slice { if s == item { return true } } return false }