Custom CMS — Go¶
Contoh HTTP handler untuk webhook nulis.top.
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"io"
"net/http"
"strings"
)
func verifySignature(secret string, body []byte, header string) bool {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(body)
expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(expected), []byte(header))
}
func nulisWebhook(w http.ResponseWriter, r *http.Request) {
body, _ := io.ReadAll(r.Body)
if !verifySignature(os.Getenv("NULIS_WEBHOOK_SECRET"), body, r.Header.Get("X-Nulis-Signature")) {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
var envelope struct {
Event string `json:"event"`
Data struct {
ArticleUUID string `json:"article_uuid"`
Title struct {
H1 string `json:"h1"`
Slug string `json:"slug"`
} `json:"title"`
Content struct {
BodyHTML string `json:"body_html"`
} `json:"content"`
SeoPackage struct {
Seo struct {
MetaTitle string `json:"meta_title"`
MetaDescription string `json:"meta_description"`
FocusKeyword string `json:"focus_keyword"`
} `json:"seo"`
} `json:"seo_package"`
} `json:"data"`
}
if err := json.Unmarshal(body, &envelope); err != nil {
http.Error(w, "bad json", http.StatusBadRequest)
return
}
uuid := envelope.Data.ArticleUUID
if uuid == "" {
http.Error(w, "missing uuid", http.StatusUnprocessableEntity)
return
}
// Upsert by uuid — implement your storage layer
_ = upsertPost(uuid, envelope.Data)
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"ok":true}`))
}
Pull via API¶
req, _ := http.NewRequest("GET", "https://studio.nulis.top/api/v1/articles/"+uuid, nil)
req.Header.Set("Authorization", "Bearer "+apiKey)
Idempotency: gunakan article_uuid sebagai unique key.