gitea, notion webhook
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

89 lines
2.3 KiB

package notion
import (
"encoding/json"
"log/slog"
"os"
"path/filepath"
"sync"
)
// snapshot is the last-known state of a page, used to diff the next event.
// 페이지당 1개만 유지(덮어쓰기) — 과거 이력을 누적하지 않는다.
type snapshot struct {
Title string `json:"title"`
Props map[string]string `json:"props"` // 속성명 → 렌더된 현재 값
Blocks map[string]string `json:"blocks,omitempty"` // 블록 ID → 텍스트 (편집된 블록만 점진 저장)
}
// SnapshotStore persists page snapshots to a JSON file (thread-safe).
// 비활성화하려면 path를 비우면 된다(메모리에만 유지, diff는 동작).
type SnapshotStore struct {
mu sync.Mutex
path string
data map[string]snapshot // pageID → snapshot
}
// NewSnapshotStore loads snapshots from path (missing file is fine).
func NewSnapshotStore(path string) (*SnapshotStore, error) {
s := &SnapshotStore{path: path, data: make(map[string]snapshot)}
if path == "" {
return s, nil
}
b, err := os.ReadFile(path)
switch {
case err == nil:
if e := json.Unmarshal(b, &s.data); e != nil {
return nil, e
}
case os.IsNotExist(err):
// 최초 실행: 빈 스토어로 시작
default:
return nil, err
}
return s, nil
}
func (s *SnapshotStore) Get(pageID string) (snapshot, bool) {
s.mu.Lock()
defer s.mu.Unlock()
snap, ok := s.data[pageID]
return snap, ok
}
func (s *SnapshotStore) Put(pageID string, snap snapshot) {
s.mu.Lock()
defer s.mu.Unlock()
s.data[pageID] = snap
s.persist()
}
func (s *SnapshotStore) Delete(pageID string) {
s.mu.Lock()
defer s.mu.Unlock()
delete(s.data, pageID)
s.persist()
}
// persist writes the whole map atomically (tmp + rename). 호출자가 lock 보유.
func (s *SnapshotStore) persist() {
if s.path == "" {
return
}
if dir := filepath.Dir(s.path); dir != "" && dir != "." {
_ = os.MkdirAll(dir, 0o755)
}
b, err := json.MarshalIndent(s.data, "", " ")
if err != nil {
slog.Warn("notion snapshot marshal failed", "err", err)
return
}
tmp := s.path + ".tmp"
if err := os.WriteFile(tmp, b, 0o644); err != nil {
slog.Warn("notion snapshot write failed", "err", err)
return
}
if err := os.Rename(tmp, s.path); err != nil {
slog.Warn("notion snapshot rename failed", "err", err)
}
}