node build fixed
This commit is contained in:
197
seanime-2.9.10/internal/report/report.go
Normal file
197
seanime-2.9.10/internal/report/report.go
Normal file
@@ -0,0 +1,197 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ClickLog struct {
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Element string `json:"element"`
|
||||
PageURL string `json:"pageUrl"`
|
||||
Text *string `json:"text"`
|
||||
ClassName *string `json:"className"`
|
||||
}
|
||||
|
||||
type NetworkLog struct {
|
||||
Type string `json:"type"`
|
||||
Method string `json:"method"`
|
||||
URL string `json:"url"`
|
||||
PageURL string `json:"pageUrl"`
|
||||
Status int `json:"status"`
|
||||
Duration int `json:"duration"`
|
||||
DataPreview string `json:"dataPreview"`
|
||||
Body string `json:"body"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
}
|
||||
|
||||
type ReactQueryLog struct {
|
||||
Type string `json:"type"`
|
||||
PageURL string `json:"pageUrl"`
|
||||
Status string `json:"status"`
|
||||
Hash string `json:"hash"`
|
||||
Error interface{} `json:"error"`
|
||||
DataPreview string `json:"dataPreview"`
|
||||
DataType string `json:"dataType"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
}
|
||||
|
||||
type ConsoleLog struct {
|
||||
Type string `json:"type"`
|
||||
Content string `json:"content"`
|
||||
PageURL string `json:"pageUrl"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
}
|
||||
|
||||
type UnlockedLocalFile struct {
|
||||
Path string `json:"path"`
|
||||
MediaId int `json:"mediaId"`
|
||||
}
|
||||
|
||||
type IssueReport struct {
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UserAgent string `json:"userAgent"`
|
||||
AppVersion string `json:"appVersion"`
|
||||
OS string `json:"os"`
|
||||
Arch string `json:"arch"`
|
||||
ClickLogs []*ClickLog `json:"clickLogs,omitempty"`
|
||||
NetworkLogs []*NetworkLog `json:"networkLogs,omitempty"`
|
||||
ReactQueryLogs []*ReactQueryLog `json:"reactQueryLogs,omitempty"`
|
||||
ConsoleLogs []*ConsoleLog `json:"consoleLogs,omitempty"`
|
||||
UnlockedLocalFiles []*UnlockedLocalFile `json:"unlockedLocalFiles,omitempty"`
|
||||
ScanLogs []string `json:"scanLogs,omitempty"`
|
||||
ServerLogs string `json:"serverLogs,omitempty"`
|
||||
ServerStatus string `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
func NewIssueReport(userAgent, appVersion, _os, arch string, logsDir string, isAnimeLibraryIssue bool, serverStatus interface{}, toRedact []string) (ret *IssueReport, err error) {
|
||||
ret = &IssueReport{
|
||||
CreatedAt: time.Now(),
|
||||
UserAgent: userAgent,
|
||||
AppVersion: appVersion,
|
||||
OS: _os,
|
||||
Arch: arch,
|
||||
ClickLogs: make([]*ClickLog, 0),
|
||||
NetworkLogs: make([]*NetworkLog, 0),
|
||||
ReactQueryLogs: make([]*ReactQueryLog, 0),
|
||||
ConsoleLogs: make([]*ConsoleLog, 0),
|
||||
UnlockedLocalFiles: make([]*UnlockedLocalFile, 0),
|
||||
ScanLogs: make([]string, 0),
|
||||
ServerLogs: "",
|
||||
ServerStatus: "",
|
||||
}
|
||||
|
||||
// Get all log files in the directory
|
||||
entries, err := os.ReadDir(logsDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read log directory: %w", err)
|
||||
}
|
||||
var serverLogFiles []os.FileInfo
|
||||
var scanLogFiles []os.FileInfo
|
||||
|
||||
for _, file := range entries {
|
||||
if strings.HasPrefix(file.Name(), "seanime-") {
|
||||
info, err := file.Info()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
serverLogFiles = append(serverLogFiles, info)
|
||||
}
|
||||
if strings.Contains(file.Name(), "-scan") {
|
||||
info, err := file.Info()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
// Check if file is newer than 1 day
|
||||
if time.Since(info.ModTime()).Hours() < 24 {
|
||||
scanLogFiles = append(scanLogFiles, info)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
userPathPattern := regexp.MustCompile(`(?i)(/home/|/Users/|C:\\Users\\)([^/\\]+)`)
|
||||
|
||||
if serverStatus != nil {
|
||||
serverStatusMarshaled, err := json.Marshal(serverStatus)
|
||||
if err == nil {
|
||||
// pretty print the json
|
||||
var prettyJSON bytes.Buffer
|
||||
err = json.Indent(&prettyJSON, serverStatusMarshaled, "", " ")
|
||||
if err == nil {
|
||||
ret.ServerStatus = prettyJSON.String()
|
||||
|
||||
for _, redact := range toRedact {
|
||||
ret.ServerStatus = strings.ReplaceAll(ret.ServerStatus, redact, "[REDACTED]")
|
||||
}
|
||||
|
||||
ret.ServerStatus = userPathPattern.ReplaceAllString(ret.ServerStatus, "${1}[REDACTED]")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(serverLogFiles) > 0 {
|
||||
sort.Slice(serverLogFiles, func(i, j int) bool {
|
||||
return serverLogFiles[i].ModTime().After(serverLogFiles[j].ModTime())
|
||||
})
|
||||
// Get the most recent log file
|
||||
latestLog := serverLogFiles[0]
|
||||
latestLogPath := filepath.Join(logsDir, latestLog.Name())
|
||||
latestLogContent, err := os.ReadFile(latestLogPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read log file: %w", err)
|
||||
}
|
||||
|
||||
latestLogContent = userPathPattern.ReplaceAll(latestLogContent, []byte("${1}[REDACTED]"))
|
||||
|
||||
for _, redact := range toRedact {
|
||||
latestLogContent = bytes.ReplaceAll(latestLogContent, []byte(redact), []byte("[REDACTED]"))
|
||||
}
|
||||
ret.ServerLogs = string(latestLogContent)
|
||||
}
|
||||
|
||||
if isAnimeLibraryIssue {
|
||||
if len(scanLogFiles) > 0 {
|
||||
for _, file := range scanLogFiles {
|
||||
scanLogPath := filepath.Join(logsDir, file.Name())
|
||||
scanLogContent, err := os.ReadFile(scanLogPath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
scanLogContent = userPathPattern.ReplaceAll(scanLogContent, []byte("${1}[REDACTED]"))
|
||||
|
||||
if len(scanLogContent) == 0 {
|
||||
ret.ScanLogs = append(ret.ScanLogs, "EMPTY")
|
||||
} else {
|
||||
ret.ScanLogs = append(ret.ScanLogs, string(scanLogContent))
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (ir *IssueReport) AddClickLogs(clickLogs []*ClickLog) {
|
||||
ir.ClickLogs = append(ir.ClickLogs, clickLogs...)
|
||||
}
|
||||
|
||||
func (ir *IssueReport) AddNetworkLogs(networkLogs []*NetworkLog) {
|
||||
ir.NetworkLogs = append(ir.NetworkLogs, networkLogs...)
|
||||
}
|
||||
|
||||
func (ir *IssueReport) AddReactQueryLogs(reactQueryLogs []*ReactQueryLog) {
|
||||
ir.ReactQueryLogs = append(ir.ReactQueryLogs, reactQueryLogs...)
|
||||
}
|
||||
|
||||
func (ir *IssueReport) AddConsoleLogs(consoleLogs []*ConsoleLog) {
|
||||
ir.ConsoleLogs = append(ir.ConsoleLogs, consoleLogs...)
|
||||
}
|
||||
95
seanime-2.9.10/internal/report/repository.go
Normal file
95
seanime-2.9.10/internal/report/repository.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/samber/lo"
|
||||
"github.com/samber/mo"
|
||||
"runtime"
|
||||
"seanime/internal/constants"
|
||||
"seanime/internal/database/models"
|
||||
"seanime/internal/library/anime"
|
||||
)
|
||||
|
||||
type Repository struct {
|
||||
logger *zerolog.Logger
|
||||
|
||||
savedIssueReport mo.Option[*IssueReport]
|
||||
}
|
||||
|
||||
func NewRepository(logger *zerolog.Logger) *Repository {
|
||||
return &Repository{
|
||||
logger: logger,
|
||||
savedIssueReport: mo.None[*IssueReport](),
|
||||
}
|
||||
}
|
||||
|
||||
type SaveIssueReportOptions struct {
|
||||
LogsDir string `json:"logsDir"`
|
||||
UserAgent string `json:"userAgent"`
|
||||
ClickLogs []*ClickLog `json:"clickLogs"`
|
||||
NetworkLogs []*NetworkLog `json:"networkLogs"`
|
||||
ReactQueryLogs []*ReactQueryLog `json:"reactQueryLogs"`
|
||||
ConsoleLogs []*ConsoleLog `json:"consoleLogs"`
|
||||
LocalFiles []*anime.LocalFile `json:"localFiles"`
|
||||
Settings *models.Settings `json:"settings"`
|
||||
DebridSettings *models.DebridSettings `json:"debridSettings"`
|
||||
IsAnimeLibraryIssue bool `json:"isAnimeLibraryIssue"`
|
||||
ServerStatus interface{} `json:"serverStatus"`
|
||||
}
|
||||
|
||||
func (r *Repository) SaveIssueReport(opts SaveIssueReportOptions) error {
|
||||
|
||||
var toRedact []string
|
||||
if opts.Settings != nil {
|
||||
toRedact = opts.Settings.GetSensitiveValues()
|
||||
}
|
||||
if opts.DebridSettings != nil {
|
||||
toRedact = append(toRedact, opts.DebridSettings.GetSensitiveValues()...)
|
||||
}
|
||||
toRedact = lo.Filter(toRedact, func(s string, _ int) bool {
|
||||
return s != ""
|
||||
})
|
||||
|
||||
issueReport, err := NewIssueReport(
|
||||
opts.UserAgent,
|
||||
constants.Version,
|
||||
runtime.GOOS,
|
||||
runtime.GOARCH,
|
||||
opts.LogsDir,
|
||||
opts.IsAnimeLibraryIssue,
|
||||
opts.ServerStatus,
|
||||
toRedact,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create issue report: %w", err)
|
||||
}
|
||||
|
||||
issueReport.ClickLogs = opts.ClickLogs
|
||||
issueReport.NetworkLogs = opts.NetworkLogs
|
||||
issueReport.ReactQueryLogs = opts.ReactQueryLogs
|
||||
issueReport.ConsoleLogs = opts.ConsoleLogs
|
||||
if opts.IsAnimeLibraryIssue {
|
||||
for _, localFile := range opts.LocalFiles {
|
||||
if localFile.Locked {
|
||||
continue
|
||||
}
|
||||
issueReport.UnlockedLocalFiles = append(issueReport.UnlockedLocalFiles, &UnlockedLocalFile{
|
||||
Path: localFile.Path,
|
||||
MediaId: localFile.MediaId,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
r.savedIssueReport = mo.Some(issueReport)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Repository) GetSavedIssueReport() (*IssueReport, bool) {
|
||||
if r.savedIssueReport.IsAbsent() {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return r.savedIssueReport.MustGet(), true
|
||||
}
|
||||
Reference in New Issue
Block a user