@@ -18,8 +18,13 @@ import (
1818 "context"
1919 "encoding/json"
2020 "errors"
21+ "fmt"
22+ "strconv"
23+ "strings"
2124 "sync"
25+ "time"
2226
27+ "github.com/openGemini/opengemini-client-go/opengemini"
2328 "github.com/wailsapp/wails/v2/pkg/runtime"
2429 bolt "go.etcd.io/bbolt"
2530)
@@ -59,11 +64,24 @@ func (app *App) startup(ctx context.Context) {
5964}
6065
6166func (app * App ) shutdown (ctx context.Context ) {
62- if err := app .db .Close (); err != nil {
63- app .logger .Error ("close database failed" , "reason" , err )
67+ // Close all HTTP client connections first
68+ app .connects .Range (func (key , value interface {}) bool {
69+ app .connects .Delete (key )
70+ return true
71+ })
72+
73+ // Close database connection
74+ if app .db != nil {
75+ if err := app .db .Close (); err != nil {
76+ app .logger .Error ("close database failed" , "reason" , err )
77+ }
78+ app .db = nil
6479 }
6580
66- app .logger .Close ()
81+ // Close logger last
82+ if app .logger != nil {
83+ app .logger .Close ()
84+ }
6785}
6886
6987func (app * App ) ListConnects () []* ConnectConfig {
@@ -194,6 +212,7 @@ func (app *App) OpenFileDialog() (string, error) {
194212
195213func (app * App ) DialConnect (name string ) ([]string , error ) {
196214 app .connects .Delete (name )
215+ app .logger .Info ("dial connect" , "name" , name )
197216 cc , err := app .GetConnect (name )
198217 if err != nil {
199218 app .logger .Error ("dial connect failed: find connect config failed" , "reason" , err )
@@ -246,3 +265,203 @@ func (app *App) GetDatabaseMetadata(connectName, databaseName string) (*Database
246265
247266 return & DatabaseMetadata {RetentionPolicy : policies , Measurements : measurements }, nil
248267}
268+
269+ func (app * App ) CloseConnect (connectName string ) {
270+ app .logger .Info ("close connect" , "name" , connectName )
271+ app .connects .Delete (connectName )
272+ }
273+
274+ func (app * App ) ExecuteCommand (data * ExecuteRequest ) (* ExecuteResponse , error ) {
275+ if data .ConnectName == "" {
276+ return nil , errors .New ("connect name required" )
277+ }
278+ if data .Command == "" {
279+ return nil , errors .New ("command required" )
280+ }
281+ httpClient , err := app .getDialer (data .ConnectName )
282+ if err != nil {
283+ app .logger .Error ("get opengemini client failed" , "reason" , err , "command" , data .Command )
284+ return nil , err
285+ }
286+
287+ app .logger .Info ("execute command data" , "data" , data )
288+
289+ var startTime = time .Now ()
290+
291+ if strings .HasPrefix (strings .ToLower (data .Command ), "insert" ) {
292+ if data .Database == "" {
293+ return nil , errors .New ("database required" )
294+ }
295+ if data .RetentionPolicy == "" {
296+ data .RetentionPolicy = "autogen"
297+ }
298+ if data .Precision == "" {
299+ data .Precision = "ns"
300+ }
301+ lineProtocol := strings .TrimSpace (data .Command [6 :])
302+ err := httpClient .Write (app .ctx , data .Database , data .RetentionPolicy , lineProtocol , data .Precision )
303+ executionTime := time .Since (startTime ).Milliseconds ()
304+
305+ // Save history record
306+ history := & History {
307+ ID : strconv .FormatInt (time .Now ().UnixMilli (), 10 ),
308+ Query : data .Command ,
309+ Timestamp : time .Now ().UnixMilli (),
310+ ExecutionTime : float64 (executionTime ),
311+ Database : data .Database ,
312+ RetentionPolicy : data .RetentionPolicy ,
313+ Success : err == nil ,
314+ }
315+ if err != nil {
316+ history .Error = err .Error ()
317+ app .logger .Error ("execute command failed" , "reason" , err , "command" , data .Command )
318+ _ = app .AddHistory (history )
319+ return nil , err
320+ }
321+ _ = app .AddHistory (history )
322+ return & ExecuteResponse {NoContent : true , Message : "write success" , ExecutionTime : float64 (executionTime )}, nil
323+ }
324+ response , err := httpClient .Query (app .ctx , & opengemini.Query {
325+ Database : data .Database ,
326+ Command : data .Command ,
327+ RetentionPolicy : data .RetentionPolicy ,
328+ Precision : opengemini .ToPrecision (data .Precision ),
329+ })
330+ executionTime := time .Since (startTime ).Milliseconds ()
331+
332+ // Save history record
333+ history := & History {
334+ ID : strconv .FormatInt (time .Now ().UnixMilli (), 10 ),
335+ Query : data .Command ,
336+ Timestamp : time .Now ().UnixMilli (),
337+ ExecutionTime : float64 (executionTime ),
338+ Database : data .Database ,
339+ RetentionPolicy : data .RetentionPolicy ,
340+ Success : false ,
341+ }
342+
343+ if err != nil {
344+ history .Error = err .Error ()
345+ app .logger .Error ("execute command failed" , "reason" , err , "name" , data .ConnectName )
346+ _ = app .AddHistory (history )
347+ return nil , err
348+ }
349+ if response .Error != "" {
350+ history .Error = response .Error
351+ app .logger .Error ("execute command failed" , "reason" , response .Error )
352+ _ = app .AddHistory (history )
353+ return nil , fmt .Errorf ("execute command failed: %s" , response .Error )
354+ }
355+ if len (response .Results ) == 0 {
356+ history .Success = true
357+ _ = app .AddHistory (history )
358+ return & ExecuteResponse {NoContent : true , ExecutionTime : float64 (executionTime )}, nil
359+ }
360+
361+ if response .Results [0 ].Error != "" {
362+ history .Error = response .Results [0 ].Error
363+ app .logger .Error ("execute command failed" , "reason" , response .Results [0 ].Error )
364+ _ = app .AddHistory (history )
365+ return nil , fmt .Errorf ("execute command failed: %s" , response .Results [0 ].Error )
366+ }
367+
368+ if len (response .Results [0 ].Series ) == 0 {
369+ history .Success = true
370+ _ = app .AddHistory (history )
371+ return & ExecuteResponse {NoContent : true , ExecutionTime : float64 (executionTime )}, nil
372+ }
373+
374+ var (
375+ seriesValues = response .Results [0 ].Series [0 ].Values
376+ columns = response .Results [0 ].Series [0 ].Columns
377+ values = make ([][]any , 0 , len (seriesValues ))
378+ )
379+ for _ , v := range seriesValues {
380+ // Directly append the row data from seriesValues
381+ values = append (values , v )
382+ }
383+
384+ history .Success = true
385+ _ = app .AddHistory (history )
386+ return & ExecuteResponse {NoContent : false , Columns : columns , Values : values , ExecutionTime : float64 (executionTime )}, nil
387+ }
388+
389+ func (app * App ) GetHistories () ([]* History , error ) {
390+ var histories []* History
391+ err := app .db .View (func (tx * bolt.Tx ) error {
392+ bucket := tx .Bucket ([]byte (BucketHistories ))
393+ err := bucket .ForEach (func (k , v []byte ) error {
394+ var history = & History {}
395+ err := json .Unmarshal (v , history )
396+ if err != nil {
397+ return err
398+ }
399+ histories = append (histories , history )
400+ return nil
401+ })
402+ return err
403+ })
404+ if err != nil {
405+ app .logger .Error ("list histories failed" , "reason" , err )
406+ return nil , err
407+ }
408+
409+ // Sort by timestamp descending (newest first)
410+ for i := 0 ; i < len (histories )- 1 ; i ++ {
411+ for j := i + 1 ; j < len (histories ); j ++ {
412+ if histories [i ].Timestamp < histories [j ].Timestamp {
413+ histories [i ], histories [j ] = histories [j ], histories [i ]
414+ }
415+ }
416+ }
417+
418+ // Limit to maxHistoryCount from settings
419+ settings , err := app .GetSetting ()
420+ if err == nil && settings .MaxHistoryCount > 0 && len (histories ) > settings .MaxHistoryCount {
421+ histories = histories [:settings .MaxHistoryCount ]
422+ }
423+
424+ return histories , nil
425+ }
426+
427+ func (app * App ) AddHistory (history * History ) error {
428+ data , err := json .Marshal (history )
429+ if err != nil {
430+ app .logger .Error ("marshal history failed" , "reason" , err )
431+ return err
432+ }
433+
434+ err = app .db .Update (func (tx * bolt.Tx ) error {
435+ bucket := tx .Bucket ([]byte (BucketHistories ))
436+ return bucket .Put ([]byte (history .ID ), data )
437+ })
438+
439+ if err != nil {
440+ app .logger .Error ("save history failed" , "reason" , err )
441+ return err
442+ }
443+
444+ // Clean up old histories if exceeds max count
445+ settings , err := app .GetSetting ()
446+ if err == nil && settings .MaxHistoryCount > 0 {
447+ histories , err := app .GetHistories ()
448+ if err == nil && len (histories ) > settings .MaxHistoryCount {
449+ // Delete oldest histories
450+ toDelete := histories [settings .MaxHistoryCount :]
451+ err = app .db .Update (func (tx * bolt.Tx ) error {
452+ bucket := tx .Bucket ([]byte (BucketHistories ))
453+ for _ , h := range toDelete {
454+ if err := bucket .Delete ([]byte (h .ID )); err != nil {
455+ return err
456+ }
457+ }
458+ return nil
459+ })
460+ if err != nil {
461+ app .logger .Error ("delete old histories failed" , "reason" , err )
462+ }
463+ }
464+ }
465+
466+ return nil
467+ }
0 commit comments