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.
 
 
 
 
 

113 lines
2.8 KiB

// Package mapping holds the manual source->Slack-email overrides that the admin
// page manages. It supplements automatic email matching for when the Gitea/Notion
// email differs from the Slack email (or isn't present).
package mapping
import (
"encoding/json"
"os"
"path/filepath"
"sort"
"sync"
)
// Entry is one override row, as shown in the admin UI and stored on disk.
type Entry struct {
Source string `json:"source"` // gitea/notion email OR login/username
SlackEmail string `json:"slack_email"` // email to look up in Slack
}
// Store is a thread-safe, file-backed map of Source -> SlackEmail.
type Store struct {
mu sync.RWMutex
path string
m map[string]string
}
// New loads the store from path (an empty store if the file is absent).
func New(path string) (*Store, error) {
s := &Store{path: path, m: make(map[string]string)}
data, err := os.ReadFile(path)
if os.IsNotExist(err) {
return s, nil
}
if err != nil {
return nil, err
}
var entries []Entry
if len(data) > 0 {
if err := json.Unmarshal(data, &entries); err != nil {
return nil, err
}
}
for _, e := range entries {
s.m[e.Source] = e.SlackEmail
}
return s, nil
}
// Resolve returns the Slack email to use for a recipient. It prefers an override
// keyed by email, then by login, otherwise falls back to the original email.
func (s *Store) Resolve(email, login string) string {
s.mu.RLock()
defer s.mu.RUnlock()
if email != "" {
if v, ok := s.m[email]; ok {
return v
}
}
if login != "" {
if v, ok := s.m[login]; ok {
return v
}
}
return email
}
// List returns all entries sorted by source (for stable UI rendering).
func (s *Store) List() []Entry {
s.mu.RLock()
defer s.mu.RUnlock()
out := make([]Entry, 0, len(s.m))
for k, v := range s.m {
out = append(out, Entry{Source: k, SlackEmail: v})
}
sort.Slice(out, func(i, j int) bool { return out[i].Source < out[j].Source })
return out
}
// Add inserts/updates an override and persists.
func (s *Store) Add(source, slackEmail string) error {
s.mu.Lock()
defer s.mu.Unlock()
s.m[source] = slackEmail
return s.save()
}
// Delete removes an override and persists.
func (s *Store) Delete(source string) error {
s.mu.Lock()
defer s.mu.Unlock()
delete(s.m, source)
return s.save()
}
// save writes the store to disk. Caller must hold the write lock.
func (s *Store) save() error {
entries := make([]Entry, 0, len(s.m))
for k, v := range s.m {
entries = append(entries, Entry{Source: k, SlackEmail: v})
}
sort.Slice(entries, func(i, j int) bool { return entries[i].Source < entries[j].Source })
data, err := json.MarshalIndent(entries, "", " ")
if err != nil {
return err
}
if err := os.MkdirAll(filepath.Dir(s.path), 0o755); err != nil {
return err
}
return os.WriteFile(s.path, data, 0o644)
}