feat: add deploy webhook endpoint (POST /api/v1/_deploy)

This commit is contained in:
user
2026-04-28 16:13:39 +09:00
parent dced20a9af
commit 510d95abd2
5 changed files with 56 additions and 0 deletions

1
.env
View File

@@ -8,6 +8,7 @@ THIRDONE_BIN=thirdone
TUTOR_PUBLIC_URL=https://tutor.uljisoft.com
# third-one endpoint (no API key needed — auth handled by third-one):
TUTOR_LLM_ENDPOINT=http://localhost:11434/v1
TUTOR_DEPLOY_SECRET=
# For direct API access (e.g. OpenAI, DeepSeek), set endpoint + key:
# TUTOR_LLM_ENDPOINT=https://api.deepseek.com
# TUTOR_LLM_API_KEY=sk-your-key-here

15
deploy.sh Normal file
View File

@@ -0,0 +1,15 @@
#!/bin/bash
set -euo pipefail
cd "$(dirname "$0")"
echo "[deploy] pulling latest code..."
git pull origin master
echo "[deploy] building..."
go build -o tutor-api ./cmd/tutor-api
echo "[deploy] restarting service..."
sudo systemctl restart tutor-api
echo "[deploy] done"

View File

@@ -23,6 +23,7 @@ type Config struct {
LLMEndpoint string
GoogleClientID string
JWTSecret string
DeploySecret string
}
func LoadFromEnv() Config {
@@ -38,6 +39,7 @@ func LoadFromEnv() Config {
LLMEndpoint: envOrDefault("TUTOR_LLM_ENDPOINT", ""),
GoogleClientID: envOrDefault("GOOGLE_CLIENT_ID", ""),
JWTSecret: envOrDefault("JWT_SECRET", ""),
DeploySecret: envOrDefault("TUTOR_DEPLOY_SECRET", ""),
}
}
@@ -45,6 +47,10 @@ func (c Config) HasLLM() bool {
return c.LLMEndpoint != ""
}
func (c Config) HasDeploy() bool {
return c.DeploySecret != ""
}
func envOrDefault(key string, fallback string) string {
value := os.Getenv(key)
if value == "" {

View File

@@ -0,0 +1,31 @@
package httpapi
import (
"log"
"net/http"
"os/exec"
)
func (h Handler) handleDeploy(w http.ResponseWriter, r *http.Request) {
if !h.cfg.HasDeploy() {
writeError(w, http.StatusNotFound, "deploy endpoint not configured")
return
}
if r.Header.Get("X-Deploy-Secret") != h.cfg.DeploySecret {
writeError(w, http.StatusUnauthorized, "invalid deploy secret")
return
}
writeJSON(w, http.StatusAccepted, map[string]string{"status": "deploy started"})
go func() {
cmd := exec.Command("/bin/bash", "deploy.sh")
output, err := cmd.CombinedOutput()
if err != nil {
log.Printf("[deploy] failed: %v, output: %s", err, string(output))
return
}
log.Printf("[deploy] success: %s", string(output))
}()
}

View File

@@ -53,6 +53,9 @@ func (h Handler) Routes() http.Handler {
mux.HandleFunc("GET /api/v1/ontology", h.getOntology)
mux.HandleFunc("POST /api/v1/teaching-assets/prompts", h.generateTeachingAssetPrompt)
mux.HandleFunc("GET /api/v1/teaching-assets", h.getTeachingAssets)
if h.cfg.HasDeploy() {
mux.HandleFunc("POST /api/v1/_deploy", h.handleDeploy)
}
mux.Handle("GET /", webapp.Handler())
return mux
}