From 6a83144900dc549130db975ddace072bf2349921 Mon Sep 17 00:00:00 2001 From: dev-warrior777 <> Date: Wed, 19 Feb 2025 15:27:22 +0800 Subject: [PATCH 1/2] client,webserver: Download the main log file - Replaces #3129 --- client/app/config.go | 1 + client/webserver/api.go | 36 ++++++++++++++++++++ client/webserver/locales/en-us.go | 1 + client/webserver/site/src/css/icons.scss | 6 ++++ client/webserver/site/src/html/settings.tmpl | 3 ++ client/webserver/site/src/js/settings.ts | 14 ++++++++ client/webserver/webserver.go | 10 ++++-- 7 files changed, 68 insertions(+), 3 deletions(-) diff --git a/client/app/config.go b/client/app/config.go index dbf39ed410..1529bd2245 100644 --- a/client/app/config.go +++ b/client/app/config.go @@ -200,6 +200,7 @@ func (cfg *Config) Web(c *core.Core, mm *mm.MarketMaker, log dex.Logger, utc boo AppVersion: userAppVersion(Version), Language: cfg.Language, Tor: cfg.Tor, + MainLogFilePath: cfg.LogPath, } } diff --git a/client/webserver/api.go b/client/webserver/api.go index 9dde92640e..adf216687f 100644 --- a/client/webserver/api.go +++ b/client/webserver/api.go @@ -4,11 +4,14 @@ package webserver import ( + "archive/zip" "encoding/hex" "encoding/json" "errors" "fmt" + "io" "net/http" + "os" "time" "decred.org/dcrdex/client/asset" @@ -2042,6 +2045,39 @@ func (s *WebServer) apiTakeAction(w http.ResponseWriter, r *http.Request) { writeJSON(w, simpleAck()) } +// apiExportAppLogs time stamps the application log, zips it and sends it back to +// the browser or webview as an attachment. Logfile names need to be distinct as +// webview will not overwite an existing file. +func (s *WebServer) apiExportAppLogs(w http.ResponseWriter, r *http.Request) { + timeString := time.Now().Format("2006-01-02T15_04_05") + zipAttachment := fmt.Sprintf("attachment; filename=bwlog_%s.zip", timeString) + + w.Header().Set("Content-Disposition", zipAttachment) + w.Header().Set("Content-Type", "application/octet-stream; type=zip") + w.WriteHeader(http.StatusOK) + + zipWriter := zip.NewWriter(w) + defer zipWriter.Close() + + lf, err := os.Open(s.mainLogFilePath) + if err != nil { + log.Errorf("error opening bisonw log file: %v", err) + return + } + defer lf.Close() + + iow, err := zipWriter.Create("bwlog.txt") // only 1 file in zip header + if err != nil { + log.Errorf("error creating an io.Writer: %v", err) + return + } + + if _, err := io.Copy(iow, lf); err != nil { + log.Errorf("error copying bisonw log to zip writer: %v", err) + return + } +} + func (s *WebServer) redeemGameCode(w http.ResponseWriter, r *http.Request) { var form struct { Code dex.Bytes `json:"code"` diff --git a/client/webserver/locales/en-us.go b/client/webserver/locales/en-us.go index 0db0ae7c77..9991478397 100644 --- a/client/webserver/locales/en-us.go +++ b/client/webserver/locales/en-us.go @@ -685,4 +685,5 @@ var EnUS = map[string]*intl.Translation{ "Wallet Balances": {T: "Wallet Balances"}, "Placements": {T: "Placements"}, "delete_bot": {T: "Delete Bot"}, + "export_logs": {T: "Export Logs"}, } diff --git a/client/webserver/site/src/css/icons.scss b/client/webserver/site/src/css/icons.scss index 098e4a42e6..d25d7b963c 100644 --- a/client/webserver/site/src/css/icons.scss +++ b/client/webserver/site/src/css/icons.scss @@ -155,6 +155,12 @@ display: inline-block; } +.ico-wide-headed-down-arrow::before { + content: "\e919"; + display: inline-block; + transform: rotate(270deg); +} + .ico-arrowup::before { content: "\e90c"; display: inline-block; diff --git a/client/webserver/site/src/html/settings.tmpl b/client/webserver/site/src/html/settings.tmpl index f9a1056d6b..eeaccf3471 100644 --- a/client/webserver/site/src/html/settings.tmpl +++ b/client/webserver/site/src/html/settings.tmpl @@ -76,6 +76,9 @@

[[[seed_implore_msg]]]

+
+ [[[export_logs]]] +
diff --git a/client/webserver/site/src/js/settings.ts b/client/webserver/site/src/js/settings.ts index 59e6758446..a00ce6f994 100644 --- a/client/webserver/site/src/js/settings.ts +++ b/client/webserver/site/src/js/settings.ts @@ -173,6 +173,8 @@ export default class SettingsPage extends BasePage { }) forms.bind(page.exportSeedAuth, page.exportSeedSubmit, () => this.submitExportSeedReq()) + Doc.bind(page.exportLogs, 'click', () => this.exportLogs()) + Doc.bind(page.gameCodeLink, 'click', () => this.showForm(page.gameCodeForm)) Doc.bind(page.gameCodeSubmit, 'click', () => this.submitGameCode()) @@ -473,6 +475,18 @@ export default class SettingsPage extends BasePage { Doc.show(form) } + /* exportLogs zips the main app log and sends it back as an attachment */ + async exportLogs () { + const url = new URL(window.location.href) + url.pathname = '/api/exportapplog' + const target = '_self' + if (window.isWebview !== undefined) { + window.open(url.toString(), target) // explicit + } else { + window.open(url.toString()) + } + } + async submitGameCode () { const page = this.page Doc.hide(page.gameCodeErr) diff --git a/client/webserver/webserver.go b/client/webserver/webserver.go index eac1bb6b51..745823c250 100644 --- a/client/webserver/webserver.go +++ b/client/webserver/webserver.go @@ -241,9 +241,10 @@ type Config struct { // should be used by default since site files from older distributions may // be present on the disk. When NoEmbed is true, this also implies reloading // and execution of html templates on each request. - NoEmbed bool - HttpProf bool - Tor bool + NoEmbed bool + HttpProf bool + Tor bool + MainLogFilePath string } type valStamp struct { @@ -280,6 +281,7 @@ type WebServer struct { appVersion string useDEXBranding bool + mainLogFilePath string } // New is the constructor for a new WebServer. CustomSiteDir in the Config can @@ -407,6 +409,7 @@ func New(cfg *Config) (*WebServer, error) { bondBuf: map[uint32]valStamp{}, appVersion: cfg.AppVersion, useDEXBranding: useDEXBranding, + mainLogFilePath: cfg.MainLogFilePath, } s.lang.Store(lang) @@ -578,6 +581,7 @@ func New(cfg *Config) (*WebServer, error) { apiAuth.Post("/txhistory", s.apiTxHistory) apiAuth.Post("/takeaction", s.apiTakeAction) apiAuth.Post("/redeemgamecode", s.redeemGameCode) + apiAuth.Get("/exportapplog", s.apiExportAppLogs) apiAuth.Post("/stakestatus", s.apiStakeStatus) apiAuth.Post("/setvsp", s.apiSetVSP) From a920384777af78b5ae0018b981081a4634203173 Mon Sep 17 00:00:00 2001 From: dev-warrior777 <> Date: Wed, 19 Feb 2025 16:01:39 +0800 Subject: [PATCH 2/2] client: Tidy up --- client/app/config.go | 28 ++++++++++++++-------------- client/webserver/webserver.go | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/client/app/config.go b/client/app/config.go index 1529bd2245..a0146c008a 100644 --- a/client/app/config.go +++ b/client/app/config.go @@ -186,20 +186,20 @@ func (cfg *Config) Web(c *core.Core, mm *mm.MarketMaker, log dex.Logger, utc boo } return &webserver.Config{ - DataDir: filepath.Join(cfg.AppData, "srv"), - Core: c, - MarketMaker: mmCore, - Addr: cfg.WebAddr, - CustomSiteDir: cfg.SiteDir, - Logger: log, - UTC: utc, - CertFile: certFile, - KeyFile: keyFile, - NoEmbed: cfg.NoEmbedSite, - HttpProf: cfg.HTTPProfile, - AppVersion: userAppVersion(Version), - Language: cfg.Language, - Tor: cfg.Tor, + DataDir: filepath.Join(cfg.AppData, "srv"), + Core: c, + MarketMaker: mmCore, + Addr: cfg.WebAddr, + CustomSiteDir: cfg.SiteDir, + Logger: log, + UTC: utc, + CertFile: certFile, + KeyFile: keyFile, + NoEmbed: cfg.NoEmbedSite, + HttpProf: cfg.HTTPProfile, + AppVersion: userAppVersion(Version), + Language: cfg.Language, + Tor: cfg.Tor, MainLogFilePath: cfg.LogPath, } } diff --git a/client/webserver/webserver.go b/client/webserver/webserver.go index 745823c250..cb98641ffc 100644 --- a/client/webserver/webserver.go +++ b/client/webserver/webserver.go @@ -280,7 +280,7 @@ type WebServer struct { appVersion string - useDEXBranding bool + useDEXBranding bool mainLogFilePath string }