@vira-ui/cli 0.3.3-alpha → 0.4.1-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/go/appYaml.js +34 -0
- package/dist/go/backendEnvExample.js +21 -0
- package/dist/go/backendReadme.js +18 -0
- package/dist/go/channelHelpers.js +29 -0
- package/dist/go/configGo.js +262 -0
- package/dist/go/dbGo.js +47 -0
- package/dist/go/dbYaml.js +11 -0
- package/dist/go/dockerCompose.js +38 -0
- package/dist/go/dockerComposeProd.js +54 -0
- package/dist/go/dockerfile.js +19 -0
- package/dist/go/eventHandlerTemplate.js +34 -0
- package/dist/go/eventsAPI.js +414 -0
- package/dist/go/goMod.js +20 -0
- package/dist/go/kafkaGo.js +71 -0
- package/dist/go/kafkaYaml.js +10 -0
- package/dist/go/kanbanHandlers.js +221 -0
- package/dist/go/mainGo.js +527 -0
- package/dist/go/readme.js +14 -0
- package/dist/go/redisGo.js +35 -0
- package/dist/go/redisYaml.js +8 -0
- package/dist/go/registryGo.js +47 -0
- package/dist/go/sqlcYaml.js +17 -0
- package/dist/go/stateStore.js +119 -0
- package/dist/go/typesGo.js +15 -0
- package/dist/go/useViraState.js +160 -0
- package/dist/go/useViraStream.js +167 -0
- package/dist/index.js +644 -192
- package/dist/react/appTsx.js +52 -0
- package/dist/react/envExample.js +7 -0
- package/dist/react/envLocal.js +5 -0
- package/dist/react/indexCss.js +22 -0
- package/dist/react/indexHtml.js +16 -0
- package/dist/react/kanbanAppTsx.js +34 -0
- package/dist/react/kanbanBoard.js +63 -0
- package/dist/react/kanbanCard.js +65 -0
- package/dist/react/kanbanColumn.js +67 -0
- package/dist/react/kanbanModels.js +37 -0
- package/dist/react/kanbanService.js +119 -0
- package/dist/react/mainTsx.js +16 -0
- package/dist/react/tsconfig.js +25 -0
- package/dist/react/viteConfig.js +31 -0
- package/package.json +3 -4
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.eventsAPI = void 0;
|
|
4
|
+
exports.eventsAPI = `package events
|
|
5
|
+
|
|
6
|
+
import (
|
|
7
|
+
"context"
|
|
8
|
+
"encoding/json"
|
|
9
|
+
"sync"
|
|
10
|
+
"time"
|
|
11
|
+
|
|
12
|
+
jsonpatch "github.com/evanphx/json-patch/v5"
|
|
13
|
+
"github.com/gorilla/websocket"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
// VRP_VERSION is the current protocol version.
|
|
17
|
+
const VRP_VERSION = "0.1"
|
|
18
|
+
|
|
19
|
+
// WSMessage matches protocol message schema.
|
|
20
|
+
type WSMessage struct {
|
|
21
|
+
Type string \`json:"type"\`
|
|
22
|
+
Name string \`json:"name,omitempty"\`
|
|
23
|
+
Channel string \`json:"channel,omitempty"\`
|
|
24
|
+
Channels []string \`json:"channels,omitempty"\`
|
|
25
|
+
Data json.RawMessage \`json:"data,omitempty"\`
|
|
26
|
+
Patch json.RawMessage \`json:"patch,omitempty"\`
|
|
27
|
+
Ts int64 \`json:"ts,omitempty"\`
|
|
28
|
+
Client string \`json:"client,omitempty"\`
|
|
29
|
+
Version string \`json:"version,omitempty"\`
|
|
30
|
+
Auth string \`json:"authToken,omitempty"\`
|
|
31
|
+
Session string \`json:"session,omitempty"\`
|
|
32
|
+
Interval int64 \`json:"interval,omitempty"\`
|
|
33
|
+
VersionNo int64 \`json:"versionNo,omitempty"\`
|
|
34
|
+
MsgID string \`json:"msgId,omitempty"\` // for idempotency
|
|
35
|
+
Code string \`json:"code,omitempty"\` // error code
|
|
36
|
+
Message string \`json:"message,omitempty"\` // error message
|
|
37
|
+
Retry bool \`json:"retry,omitempty"\` // error retry flag
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// EventHandler signature for domain events.
|
|
41
|
+
type EventHandler func(ctx context.Context, hub EventEmitter, conn *websocket.Conn, msg WSMessage)
|
|
42
|
+
|
|
43
|
+
// EventEmitter exposes server-side emit/update/diff for handlers.
|
|
44
|
+
type EventEmitter interface {
|
|
45
|
+
Emit(channel string, payload any)
|
|
46
|
+
Update(channel string, payload any)
|
|
47
|
+
Diff(channel string, patch any)
|
|
48
|
+
Snapshot(channel string) (json.RawMessage, int64, bool)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// DiffMode controls how diffs are generated.
|
|
52
|
+
type DiffMode int
|
|
53
|
+
|
|
54
|
+
const (
|
|
55
|
+
DiffModeMerge DiffMode = iota // JSON Merge Patch (RFC 7396)
|
|
56
|
+
DiffModePatch // JSON Patch (RFC 6902)
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
// Hub is an in-memory event hub with state and versions.
|
|
60
|
+
type Hub struct {
|
|
61
|
+
mu sync.Mutex
|
|
62
|
+
clients map[*websocket.Conn]bool
|
|
63
|
+
subs map[string]map[*websocket.Conn]bool
|
|
64
|
+
sessions map[*websocket.Conn]string
|
|
65
|
+
state map[string]json.RawMessage
|
|
66
|
+
versions map[string]int64
|
|
67
|
+
events map[string]EventHandler
|
|
68
|
+
diffMode DiffMode
|
|
69
|
+
history map[string][]StateSnapshot // for replay
|
|
70
|
+
maxHistory int
|
|
71
|
+
store StateStore
|
|
72
|
+
ttlSec int
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// StateSnapshot stores a versioned state snapshot.
|
|
76
|
+
type StateSnapshot struct {
|
|
77
|
+
Data json.RawMessage
|
|
78
|
+
VersionNo int64
|
|
79
|
+
Ts int64
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
func NewHub() *Hub {
|
|
83
|
+
return &Hub{
|
|
84
|
+
clients: make(map[*websocket.Conn]bool),
|
|
85
|
+
subs: make(map[string]map[*websocket.Conn]bool),
|
|
86
|
+
sessions: make(map[*websocket.Conn]string),
|
|
87
|
+
state: make(map[string]json.RawMessage),
|
|
88
|
+
versions: make(map[string]int64),
|
|
89
|
+
events: make(map[string]EventHandler),
|
|
90
|
+
diffMode: DiffModeMerge,
|
|
91
|
+
history: make(map[string][]StateSnapshot),
|
|
92
|
+
maxHistory: 100, // keep last 100 versions per channel
|
|
93
|
+
store: MemoryStore{},
|
|
94
|
+
ttlSec: 0,
|
|
95
|
+
msgIDs: make(map[string]int64),
|
|
96
|
+
maxMsgIDs: 1000, // keep last 1000 msgIds for dedup
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// SetDiffMode sets the diff generation mode.
|
|
101
|
+
func (h *Hub) SetDiffMode(mode DiffMode) {
|
|
102
|
+
h.mu.Lock()
|
|
103
|
+
defer h.mu.Unlock()
|
|
104
|
+
h.diffMode = mode
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// SetHistoryLimit limits how many snapshots are stored per channel.
|
|
108
|
+
func (h *Hub) SetHistoryLimit(limit int) {
|
|
109
|
+
h.mu.Lock()
|
|
110
|
+
defer h.mu.Unlock()
|
|
111
|
+
if limit > 0 {
|
|
112
|
+
h.maxHistory = limit
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// SetStore sets the state store (memory/redis).
|
|
117
|
+
func (h *Hub) SetStore(store StateStore) {
|
|
118
|
+
h.mu.Lock()
|
|
119
|
+
defer h.mu.Unlock()
|
|
120
|
+
if store != nil {
|
|
121
|
+
h.store = store
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// SetTTL sets TTL for persisted entries.
|
|
126
|
+
func (h *Hub) SetTTL(ttlSec int) {
|
|
127
|
+
h.mu.Lock()
|
|
128
|
+
defer h.mu.Unlock()
|
|
129
|
+
if ttlSec >= 0 {
|
|
130
|
+
h.ttlSec = ttlSec
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// CheckMsgID returns true if msgId was already seen (and records it), false otherwise.
|
|
135
|
+
// Used for idempotency: duplicate messages with same msgId are ignored.
|
|
136
|
+
func (h *Hub) CheckMsgID(msgID string) bool {
|
|
137
|
+
if msgID == "" {
|
|
138
|
+
return false
|
|
139
|
+
}
|
|
140
|
+
h.mu.Lock()
|
|
141
|
+
defer h.mu.Unlock()
|
|
142
|
+
now := time.Now().UnixMilli()
|
|
143
|
+
// Clean old entries (older than 5 minutes)
|
|
144
|
+
cutoff := now - 5*60*1000
|
|
145
|
+
for id, ts := range h.msgIDs {
|
|
146
|
+
if ts < cutoff {
|
|
147
|
+
delete(h.msgIDs, id)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Check if exists
|
|
151
|
+
if _, exists := h.msgIDs[msgID]; exists {
|
|
152
|
+
return true // duplicate
|
|
153
|
+
}
|
|
154
|
+
// Record
|
|
155
|
+
h.msgIDs[msgID] = now
|
|
156
|
+
// Trim if too many
|
|
157
|
+
if len(h.msgIDs) > h.maxMsgIDs {
|
|
158
|
+
// Remove oldest 100 entries
|
|
159
|
+
type entry struct {
|
|
160
|
+
id string
|
|
161
|
+
ts int64
|
|
162
|
+
}
|
|
163
|
+
var entries []entry
|
|
164
|
+
for id, ts := range h.msgIDs {
|
|
165
|
+
entries = append(entries, entry{id, ts})
|
|
166
|
+
}
|
|
167
|
+
// Sort by timestamp (oldest first)
|
|
168
|
+
for i := 0; i < len(entries)-1; i++ {
|
|
169
|
+
for j := i + 1; j < len(entries); j++ {
|
|
170
|
+
if entries[i].ts > entries[j].ts {
|
|
171
|
+
entries[i], entries[j] = entries[j], entries[i]
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
// Remove oldest 100
|
|
176
|
+
for i := 0; i < 100 && i < len(entries); i++ {
|
|
177
|
+
delete(h.msgIDs, entries[i].id)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return false // new message
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Emit aliases Update with force update.
|
|
184
|
+
func (h *Hub) Emit(channel string, payload any) {
|
|
185
|
+
h.applyUpdate(channel, payload, true)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Update applies merge-patch optimization when possible.
|
|
189
|
+
func (h *Hub) Update(channel string, payload any) {
|
|
190
|
+
h.applyUpdate(channel, payload, false)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Diff applies a raw patch and broadcasts.
|
|
194
|
+
func (h *Hub) Diff(channel string, patch any) {
|
|
195
|
+
h.applyDiff(channel, patch)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
func (h *Hub) Snapshot(channel string) (json.RawMessage, int64, bool) {
|
|
199
|
+
h.mu.Lock()
|
|
200
|
+
snap, ok := h.state[channel]
|
|
201
|
+
v := h.versions[channel]
|
|
202
|
+
store := h.store
|
|
203
|
+
h.mu.Unlock()
|
|
204
|
+
|
|
205
|
+
if ok {
|
|
206
|
+
return snap, v, true
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Try loading from store if available
|
|
210
|
+
if store != nil {
|
|
211
|
+
if snap, ok, _ := store.LoadSnapshot(context.Background(), channel); ok {
|
|
212
|
+
// Update in-memory cache
|
|
213
|
+
h.mu.Lock()
|
|
214
|
+
h.state[channel] = snap.Data
|
|
215
|
+
h.versions[channel] = snap.VersionNo
|
|
216
|
+
h.mu.Unlock()
|
|
217
|
+
return snap.Data, snap.VersionNo, true
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return nil, 0, false
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Replay returns state snapshots for a channel from a given version.
|
|
225
|
+
func (h *Hub) Replay(channel string, fromVersion int64) []StateSnapshot {
|
|
226
|
+
h.mu.Lock()
|
|
227
|
+
hist := h.history[channel]
|
|
228
|
+
store := h.store
|
|
229
|
+
h.mu.Unlock()
|
|
230
|
+
|
|
231
|
+
var result []StateSnapshot
|
|
232
|
+
for _, snap := range hist {
|
|
233
|
+
if snap.VersionNo > fromVersion {
|
|
234
|
+
result = append(result, snap)
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Try loading from store if available and in-memory is empty/incomplete
|
|
239
|
+
if store != nil && len(result) == 0 {
|
|
240
|
+
if stored, err := store.LoadHistory(context.Background(), channel, fromVersion); err == nil {
|
|
241
|
+
result = stored
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return result
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// internal helpers
|
|
249
|
+
func (h *Hub) applyUpdate(channel string, payload any, force bool) {
|
|
250
|
+
newData, err := json.Marshal(payload)
|
|
251
|
+
if err != nil {
|
|
252
|
+
return
|
|
253
|
+
}
|
|
254
|
+
var prev []byte
|
|
255
|
+
h.mu.Lock()
|
|
256
|
+
if s, ok := h.state[channel]; ok {
|
|
257
|
+
prev = append([]byte{}, s...)
|
|
258
|
+
}
|
|
259
|
+
h.state[channel] = newData
|
|
260
|
+
h.versions[channel]++
|
|
261
|
+
version := h.versions[channel]
|
|
262
|
+
|
|
263
|
+
// Save snapshot for replay
|
|
264
|
+
snap := StateSnapshot{
|
|
265
|
+
Data: append([]byte{}, newData...),
|
|
266
|
+
VersionNo: version,
|
|
267
|
+
Ts: time.Now().UnixMilli(),
|
|
268
|
+
}
|
|
269
|
+
hist := h.history[channel]
|
|
270
|
+
hist = append(hist, snap)
|
|
271
|
+
if len(hist) > h.maxHistory {
|
|
272
|
+
hist = hist[len(hist)-h.maxHistory:]
|
|
273
|
+
}
|
|
274
|
+
h.history[channel] = hist
|
|
275
|
+
store := h.store
|
|
276
|
+
ttl := h.ttlSec
|
|
277
|
+
|
|
278
|
+
h.mu.Unlock()
|
|
279
|
+
|
|
280
|
+
if store != nil {
|
|
281
|
+
_ = store.SaveSnapshot(context.Background(), channel, snap, ttl)
|
|
282
|
+
_ = store.AppendHistory(context.Background(), channel, snap, h.maxHistory, ttl)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if !force && prev != nil {
|
|
286
|
+
var patch json.RawMessage
|
|
287
|
+
var err error
|
|
288
|
+
if h.diffMode == DiffModePatch {
|
|
289
|
+
// Generate RFC 6902 JSON Patch
|
|
290
|
+
patchOps, err := jsonpatch.CreatePatch(prev, newData)
|
|
291
|
+
if err == nil {
|
|
292
|
+
patch, _ = json.Marshal(patchOps)
|
|
293
|
+
}
|
|
294
|
+
} else {
|
|
295
|
+
// Generate RFC 7396 JSON Merge Patch
|
|
296
|
+
patch, err = jsonpatch.CreateMergePatch(prev, newData)
|
|
297
|
+
}
|
|
298
|
+
if err == nil && len(patch) > 2 {
|
|
299
|
+
h.applyDiff(channel, patch)
|
|
300
|
+
return
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
msg := WSMessage{
|
|
305
|
+
Type: "update",
|
|
306
|
+
Channel: channel,
|
|
307
|
+
Data: newData,
|
|
308
|
+
VersionNo: version,
|
|
309
|
+
Ts: time.Now().UnixMilli(),
|
|
310
|
+
}
|
|
311
|
+
raw, err := json.Marshal(msg)
|
|
312
|
+
if err != nil {
|
|
313
|
+
return
|
|
314
|
+
}
|
|
315
|
+
h.broadcast(channel, raw)
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
func (h *Hub) applyDiff(channel string, patch any) {
|
|
319
|
+
data, err := json.Marshal(patch)
|
|
320
|
+
if err != nil {
|
|
321
|
+
return
|
|
322
|
+
}
|
|
323
|
+
h.mu.Lock()
|
|
324
|
+
prev := h.state[channel]
|
|
325
|
+
merged := prev
|
|
326
|
+
if prev != nil {
|
|
327
|
+
if h.diffMode == DiffModePatch {
|
|
328
|
+
// Apply RFC 6902 JSON Patch
|
|
329
|
+
ops, err := jsonpatch.DecodePatch(data)
|
|
330
|
+
if err == nil {
|
|
331
|
+
applied, err := ops.Apply(prev)
|
|
332
|
+
if err == nil {
|
|
333
|
+
merged = applied
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
} else {
|
|
337
|
+
// Apply RFC 7396 JSON Merge Patch
|
|
338
|
+
if applied, err := jsonpatch.MergePatch(prev, data); err == nil {
|
|
339
|
+
merged = applied
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
} else {
|
|
343
|
+
merged = data
|
|
344
|
+
}
|
|
345
|
+
h.state[channel] = merged
|
|
346
|
+
h.versions[channel]++
|
|
347
|
+
version := h.versions[channel]
|
|
348
|
+
|
|
349
|
+
// Save snapshot
|
|
350
|
+
snap := StateSnapshot{
|
|
351
|
+
Data: append([]byte{}, merged...),
|
|
352
|
+
VersionNo: version,
|
|
353
|
+
Ts: time.Now().UnixMilli(),
|
|
354
|
+
}
|
|
355
|
+
hist := h.history[channel]
|
|
356
|
+
hist = append(hist, snap)
|
|
357
|
+
if len(hist) > h.maxHistory {
|
|
358
|
+
hist = hist[len(hist)-h.maxHistory:]
|
|
359
|
+
}
|
|
360
|
+
h.history[channel] = hist
|
|
361
|
+
store := h.store
|
|
362
|
+
ttl := h.ttlSec
|
|
363
|
+
|
|
364
|
+
h.mu.Unlock()
|
|
365
|
+
|
|
366
|
+
if store != nil {
|
|
367
|
+
_ = store.SaveSnapshot(context.Background(), channel, snap, ttl)
|
|
368
|
+
_ = store.AppendHistory(context.Background(), channel, snap, h.maxHistory, ttl)
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
msg := WSMessage{
|
|
372
|
+
Type: "diff",
|
|
373
|
+
Channel: channel,
|
|
374
|
+
Patch: data,
|
|
375
|
+
Ts: time.Now().UnixMilli(),
|
|
376
|
+
VersionNo: version,
|
|
377
|
+
}
|
|
378
|
+
raw, err := json.Marshal(msg)
|
|
379
|
+
if err != nil {
|
|
380
|
+
return
|
|
381
|
+
}
|
|
382
|
+
h.broadcast(channel, raw)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Broadcast sends a message to all subscribers of a channel.
|
|
386
|
+
// This is called by applyUpdate/applyDiff after creating the message.
|
|
387
|
+
func (h *Hub) Broadcast(channel string, raw json.RawMessage) {
|
|
388
|
+
// This will be implemented by wsHub wrapper in main.go
|
|
389
|
+
// Hub only manages state, wsHub manages connections
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Get returns an event handler by name.
|
|
393
|
+
func (h *Hub) Get(name string) (EventHandler, bool) {
|
|
394
|
+
h.mu.Lock()
|
|
395
|
+
defer h.mu.Unlock()
|
|
396
|
+
handler, ok := h.events[name]
|
|
397
|
+
return handler, ok
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// SetBroadcaster sets a custom broadcast function (used by wsHub).
|
|
401
|
+
type Broadcaster func(channel string, raw json.RawMessage)
|
|
402
|
+
|
|
403
|
+
var globalBroadcaster Broadcaster
|
|
404
|
+
|
|
405
|
+
func SetBroadcaster(fn Broadcaster) {
|
|
406
|
+
globalBroadcaster = fn
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
func (h *Hub) broadcast(channel string, raw json.RawMessage) {
|
|
410
|
+
if globalBroadcaster != nil {
|
|
411
|
+
globalBroadcaster(channel, raw)
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
`;
|
package/dist/go/goMod.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.goMod = void 0;
|
|
4
|
+
exports.goMod = `module vira-engine-backend
|
|
5
|
+
|
|
6
|
+
go 1.21
|
|
7
|
+
|
|
8
|
+
require (
|
|
9
|
+
github.com/go-chi/chi/v5 v5.0.10
|
|
10
|
+
github.com/go-chi/cors v1.2.1
|
|
11
|
+
github.com/gorilla/websocket v1.5.1
|
|
12
|
+
github.com/google/uuid v1.5.0
|
|
13
|
+
github.com/rs/zerolog v1.32.0
|
|
14
|
+
github.com/jackc/pgx/v5 v5.5.4
|
|
15
|
+
github.com/redis/go-redis/v9 v9.5.1
|
|
16
|
+
github.com/segmentio/kafka-go v0.4.45
|
|
17
|
+
github.com/evanphx/json-patch/v5 v5.9.0
|
|
18
|
+
gopkg.in/yaml.v3 v3.0.1
|
|
19
|
+
)
|
|
20
|
+
`;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.kafkaGo = void 0;
|
|
4
|
+
exports.kafkaGo = `package events
|
|
5
|
+
|
|
6
|
+
import (
|
|
7
|
+
"context"
|
|
8
|
+
"fmt"
|
|
9
|
+
"time"
|
|
10
|
+
|
|
11
|
+
"github.com/segmentio/kafka-go"
|
|
12
|
+
"github.com/rs/zerolog"
|
|
13
|
+
|
|
14
|
+
"vira-engine-backend/internal/config"
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
type Client struct {
|
|
18
|
+
dialer *kafka.Dialer
|
|
19
|
+
cfg config.Config
|
|
20
|
+
logger zerolog.Logger
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
func NewKafka(cfg config.Config, logger zerolog.Logger) (*Client, error) {
|
|
24
|
+
d := &kafka.Dialer{
|
|
25
|
+
Timeout: 5 * time.Second,
|
|
26
|
+
DualStack: true,
|
|
27
|
+
}
|
|
28
|
+
c := &Client{dialer: d, cfg: cfg, logger: logger}
|
|
29
|
+
if err := c.Ping(context.Background()); err != nil {
|
|
30
|
+
return nil, err
|
|
31
|
+
}
|
|
32
|
+
return c, nil
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
func (c *Client) Ping(ctx context.Context) error {
|
|
36
|
+
if len(c.cfg.Kafka.Brokers) == 0 {
|
|
37
|
+
return fmt.Errorf("no kafka brokers configured")
|
|
38
|
+
}
|
|
39
|
+
conn, err := c.dialer.DialContext(ctx, "tcp", c.cfg.Kafka.Brokers[0])
|
|
40
|
+
if err != nil {
|
|
41
|
+
return fmt.Errorf("dial kafka: %w", err)
|
|
42
|
+
}
|
|
43
|
+
_ = conn.Close()
|
|
44
|
+
return nil
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
func (c *Client) NewWriter(topic string) *kafka.Writer {
|
|
48
|
+
return &kafka.Writer{
|
|
49
|
+
Addr: kafka.TCP(c.cfg.Kafka.Brokers...),
|
|
50
|
+
Topic: topic,
|
|
51
|
+
RequiredAcks: kafka.RequireAll,
|
|
52
|
+
BatchTimeout: 10 * time.Millisecond,
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
func (c *Client) NewReader(topic string) *kafka.Reader {
|
|
57
|
+
return kafka.NewReader(kafka.ReaderConfig{
|
|
58
|
+
Brokers: c.cfg.Kafka.Brokers,
|
|
59
|
+
GroupID: c.cfg.Kafka.GroupID,
|
|
60
|
+
Topic: topic,
|
|
61
|
+
StartOffset: kafka.FirstOffset,
|
|
62
|
+
SessionTimeout: 10 * time.Second,
|
|
63
|
+
HeartbeatInterval: 3 * time.Second,
|
|
64
|
+
CommitInterval: 5 * time.Second,
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
func (c *Client) Close() error {
|
|
69
|
+
return nil
|
|
70
|
+
}
|
|
71
|
+
`;
|