nulis.top

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.