gito/services/core/internal/protosocket/subscription.go
toki c9952509e9 feat(core): Forgejo 브랜치 이벤트 및 런타임 채널 인프라를 추가한다
- ControlPlane 라우터에 워커/에이전트 구독 채널을 추가한다
- ProtoSocket 기반 실시간 양방향 채널( dispatcher, envelope, server)을 구현한다
- Forgejo push 이벤트를 provider 어댑터로 처리하도록 연동한다
- PostgreSQL 스토리지 백엔드를 분리하여 postgres.go로 신설한다
- Git engine command에 diff/checkout/merge 연쇄 연산을 추가한다
- 런타임 모델에 agentSession, runtimeChannel 스키마를 추가한다
- 데이터베이스 마이그레이션에 agent_session, runtime_channel, lease 테이블을 추가한다
- agent-contract에 Forgejo branch events 계약 문서를 작성한다
- 로드맵 마일스톤과 아카이브 작업을 갱신한다
2026-06-13 19:16:03 +09:00

88 lines
1.8 KiB
Go

package protosocket
import "strings"
type EventSubscriber interface {
Subscribe(connectionID string, subscription EventSubscription) EventSubscription
}
type EventSubscription struct {
Events []string
RepoID string
Branch string
}
func (s EventSubscription) Normalized() EventSubscription {
events := make([]string, 0, len(s.Events))
seen := make(map[string]struct{}, len(s.Events))
for _, event := range s.Events {
event = strings.TrimSpace(event)
if event == "" {
continue
}
if _, ok := seen[event]; ok {
continue
}
seen[event] = struct{}{}
events = append(events, event)
}
return EventSubscription{
Events: events,
RepoID: strings.TrimSpace(s.RepoID),
Branch: strings.TrimSpace(s.Branch),
}
}
func (s EventSubscription) Allows(env Envelope) bool {
s = s.Normalized()
if env.Type != "event" {
return false
}
if !s.allowsEventName(env.Action, stringPayload(env.Payload, "type")) {
return false
}
if s.RepoID != "" && s.RepoID != stringPayload(env.Payload, "repo_id") {
return false
}
if s.Branch != "" && s.Branch != stringPayload(env.Payload, "branch") {
return false
}
return true
}
func (s EventSubscription) Payload() map[string]any {
s = s.Normalized()
events := make([]any, 0, len(s.Events))
for _, event := range s.Events {
events = append(events, event)
}
return map[string]any{
"events": events,
"repo_id": s.RepoID,
"branch": s.Branch,
}
}
func (s EventSubscription) allowsEventName(action, payloadType string) bool {
if len(s.Events) == 0 {
return true
}
for _, event := range s.Events {
if event == "*" || event == action || event == payloadType {
return true
}
}
return false
}
func stringPayload(payload map[string]any, key string) string {
value, ok := payload[key]
if !ok {
return ""
}
text, ok := value.(string)
if !ok {
return ""
}
return strings.TrimSpace(text)
}