feat: support private repo cloning with HTTP basic auth
Deploy PR-Helper / deploy (push) Successful in 30s

- Add SSH URL detection with friendly prompt to use HTTPS instead
- Add private repo toggle (default off) with username/password fields
- Persist auth_type and credential in repositories table
- Pass stored credentials to PullRepo for authenticated pulls
- Incremental migration adds auth_type and credential columns
This commit is contained in:
2026-06-21 14:58:12 +08:00
parent 82666cff26
commit a6bd46db34
5 changed files with 108 additions and 21 deletions
+31 -5
View File
@@ -149,8 +149,9 @@ func (h *ReposHandler) PullRepo(c *gin.Context) {
}
id := c.Param("id")
var localPath string
err := h.db.QueryRow(`SELECT local_path FROM repositories WHERE id = ? AND user_id = ?`, id, user.ID).Scan(&localPath)
var localPath, authType string
var credential sql.NullString
err := h.db.QueryRow(`SELECT local_path, auth_type, credential FROM repositories WHERE id = ? AND user_id = ?`, id, user.ID).Scan(&localPath, &authType, &credential)
if err == sql.ErrNoRows {
c.JSON(http.StatusNotFound, gin.H{"error": "repository not found"})
return
@@ -160,7 +161,19 @@ func (h *ReposHandler) PullRepo(c *gin.Context) {
return
}
result, err := services.PullRepo(localPath)
// Parse stored credentials
var username, password string
if authType == "basic" && credential.Valid {
var cred struct {
Username string `json:"username"`
Password string `json:"password"`
}
json.Unmarshal([]byte(credential.String), &cred)
username = cred.Username
password = cred.Password
}
result, err := services.PullRepo(localPath, username, password)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
@@ -238,10 +251,23 @@ func (h *ReposHandler) CloneRepo(c *gin.Context) {
return
}
// Determine auth type and credential for persistence
authType := "none"
var credential *string
if req.Username != "" || req.Password != "" {
authType = "basic"
credJSON, _ := json.Marshal(map[string]string{
"username": req.Username,
"password": req.Password,
})
s := string(credJSON)
credential = &s
}
// Save to database with user_id
now := time.Now().Format("2006-01-02 15:04:05")
res, err := h.db.Exec(`INSERT INTO repositories (user_id, url, local_path, size_bytes, cloned_at, last_used) VALUES (?, ?, ?, ?, ?, ?)`,
user.ID, req.URL, repoDir, result.SizeBytes, now, now)
res, err := h.db.Exec(`INSERT INTO repositories (user_id, url, local_path, size_bytes, cloned_at, last_used, auth_type, credential) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
user.ID, req.URL, repoDir, result.SizeBytes, now, now, authType, credential)
if err != nil {
sendEvent("error", map[string]interface{}{"message": "save to db: " + err.Error()})
return