Add frontend #1

Merged
KartoffelChipss merged 50 commits from feature/frontend into main 2026-05-06 20:16:59 +02:00
6 changed files with 94 additions and 5 deletions
Showing only changes of commit 1aba69cfb5 - Show all commits
+17 -2
View File
@@ -6,6 +6,7 @@ import (
"log" "log"
"quay/app/models" "quay/app/models"
"quay/app/repository" "quay/app/repository"
"quay/internal/security"
"github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3"
) )
@@ -145,7 +146,7 @@ func validateIncomingSite(site *models.Site) error {
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Param site body models.Site true "Site details" // @Param site body models.Site true "Site details"
// @Success 200 {object} models.Site // @Success 200 {object} models.CreateSiteResponse
// @Failure 400 {object} models.APIError // @Failure 400 {object} models.APIError
// @Failure 500 {object} models.APIError // @Failure 500 {object} models.APIError
// @Router /sites [post] // @Router /sites [post]
@@ -165,6 +166,17 @@ func (h *SiteHandler) PostSite(c fiber.Ctx) error {
}) })
} }
rawDeployToken, hashedDeployToken, err := security.CreateDeployToken()
if err != nil {
log.Println("Error creating deploy token: ", err)
return c.Status(fiber.StatusInternalServerError).JSON(&models.APIError{
Message: "Unexpected error while creating deploy token: " + err.Error(),
})
}
site.DeployToken = hashedDeployToken
if err := h.Repo.CreateSite(&site); err != nil { if err := h.Repo.CreateSite(&site); err != nil {
log.Println("Error creating site: ", err) log.Println("Error creating site: ", err)
return c.Status(fiber.StatusInternalServerError).JSON(&models.APIError{ return c.Status(fiber.StatusInternalServerError).JSON(&models.APIError{
@@ -172,7 +184,10 @@ func (h *SiteHandler) PostSite(c fiber.Ctx) error {
}) })
} }
return c.JSON(site) return c.JSON(&models.CreateSiteResponse{
Site: site,
RawDeployToken: rawDeployToken,
})
} }
// PutSite godoc // PutSite godoc
+5
View File
@@ -39,3 +39,8 @@ type GetAllSitesResponse struct {
Sites []Site `json:"sites"` Sites []Site `json:"sites"`
Total int `json:"total"` Total int `json:"total"`
} }
type CreateSiteResponse struct {
Site Site `json:"site"`
RawDeployToken string `json:"raw_deploy_token"`
}
+12 -1
View File
@@ -573,7 +573,7 @@ const docTemplate = `{
"200": { "200": {
"description": "OK", "description": "OK",
"schema": { "schema": {
"$ref": "#/definitions/models.Site" "$ref": "#/definitions/models.CreateSiteResponse"
} }
}, },
"400": { "400": {
@@ -943,6 +943,17 @@ const docTemplate = `{
} }
} }
}, },
"models.CreateSiteResponse": {
"type": "object",
"properties": {
"raw_deploy_token": {
"type": "string"
},
"site": {
"$ref": "#/definitions/models.Site"
}
}
},
"models.CustomHeaders": { "models.CustomHeaders": {
"type": "object", "type": "object",
"properties": { "properties": {
+12 -1
View File
@@ -567,7 +567,7 @@
"200": { "200": {
"description": "OK", "description": "OK",
"schema": { "schema": {
"$ref": "#/definitions/models.Site" "$ref": "#/definitions/models.CreateSiteResponse"
} }
}, },
"400": { "400": {
@@ -937,6 +937,17 @@
} }
} }
}, },
"models.CreateSiteResponse": {
"type": "object",
"properties": {
"raw_deploy_token": {
"type": "string"
},
"site": {
"$ref": "#/definitions/models.Site"
}
}
},
"models.CustomHeaders": { "models.CustomHeaders": {
"type": "object", "type": "object",
"properties": { "properties": {
+8 -1
View File
@@ -5,6 +5,13 @@ definitions:
message: message:
type: string type: string
type: object type: object
models.CreateSiteResponse:
properties:
raw_deploy_token:
type: string
site:
$ref: '#/definitions/models.Site'
type: object
models.CustomHeaders: models.CustomHeaders:
properties: properties:
headers: headers:
@@ -456,7 +463,7 @@ paths:
"200": "200":
description: OK description: OK
schema: schema:
$ref: '#/definitions/models.Site' $ref: '#/definitions/models.CreateSiteResponse'
"400": "400":
description: Bad Request description: Bad Request
schema: schema:
+40
View File
@@ -0,0 +1,40 @@
package security
import (
"crypto/rand"
"crypto/sha256"
"crypto/subtle"
"encoding/hex"
"fmt"
)
func generateToken() (rawToken string, err error) {
bytes := make([]byte, 32)
if _, err = rand.Read(bytes); err != nil {
return "", err
}
return "quay_" + hex.EncodeToString(bytes), nil
}
func hashToken(rawToken string) string {
sum := sha256.Sum256([]byte(rawToken))
return hex.EncodeToString(sum[:])
}
func CreateDeployToken() (rawToken, hashedToken string, err error) {
rawToken, err = generateToken()
if err != nil {
return "", "", fmt.Errorf("failed to generate token: %w", err)
}
hashedToken = hashToken(rawToken)
return rawToken, hashedToken, nil
}
func CompareDeployTokens(incomingRawToken, storedHashedToken string) bool {
incomingHash := hashToken(incomingRawToken)
return subtle.ConstantTimeCompare(
[]byte(incomingHash),
[]byte(storedHashedToken),
) == 1
}