11package logging
22
33import (
4+ "bufio"
45 "fmt"
56 "github.com/forceu/gokapi/internal/environment"
67 "github.com/forceu/gokapi/internal/helper"
@@ -16,6 +17,13 @@ import (
1617var logPath = "config/log.txt"
1718var mutex sync.Mutex
1819
20+ const categoryInfo = "info"
21+ const categoryDownload = "download"
22+ const categoryUpload = "upload"
23+ const categoryEdit = "edit"
24+ const categoryAuth = "auth"
25+ const categoryWarning = "warning"
26+
1927var outputToStdout = false
2028
2129// Init sets the path where to write the log file to
@@ -25,27 +33,176 @@ func Init(filePath string) {
2533 outputToStdout = env .LogToStdout
2634}
2735
28- // AddString adds a line to the logfile including the current date. Non-Blocking
29- func AddString (text string ) {
30- output := formatDate (text )
36+ // GetAll returns all log entries as a single string and if the log file exists
37+ func GetAll () (string , bool ) {
38+ if helper .FileExists (logPath ) {
39+ content , err := os .ReadFile (logPath )
40+ helper .Check (err )
41+ return string (content ), true
42+ } else {
43+ return fmt .Sprintf ("[%s] No log file found!" , categoryWarning ), false
44+ }
45+ }
46+
47+ // createLogEntry adds a line to the logfile including the current date. Also outputs to Stdout if set.
48+ func createLogEntry (category , text string , blocking bool ) {
49+ output := createLogFormat (category , text )
3150 if outputToStdout {
3251 fmt .Println (output )
3352 }
34- go writeToFile (output )
53+ if blocking {
54+ writeToFile (output )
55+ } else {
56+ go writeToFile (output )
57+ }
3558}
3659
37- // GetLogPath returns the relative path to the log file
38- func GetLogPath () string {
39- return logPath
60+ func createLogFormat (category , text string ) string {
61+ return createLogFormatCustomTimestamp (category , text , time .Now ())
62+ }
63+ func createLogFormatCustomTimestamp (category , text string , timestamp time.Time ) string {
64+ return fmt .Sprintf ("%s [%s] %s" , getDate (timestamp ), category , text )
4065}
4166
42- // AddDownload adds a line to the logfile when a download was requested. Non-Blocking
43- func AddDownload (file * models.File , r * http.Request , saveIp bool ) {
67+ // LogStartup adds a log entry to indicate that Gokapi has started. Non-blocking
68+ func LogStartup () {
69+ createLogEntry (categoryInfo , "Gokapi started" , false )
70+ }
71+
72+ // LogShutdown adds a log entry to indicate that Gokapi is shutting down. Blocking call
73+ func LogShutdown () {
74+ createLogEntry (categoryInfo , "Gokapi shutting down" , true )
75+ }
76+
77+ // LogSetup adds a log entry to indicate that the setup was run. Non-blocking
78+ func LogSetup () {
79+ createLogEntry (categoryAuth , "Re-running Gokapi setup" , false )
80+ }
81+
82+ // LogDeploymentPassword adds a log entry to indicate that a deployment password was set. Non-blocking
83+ func LogDeploymentPassword () {
84+ createLogEntry (categoryAuth , "Setting new admin password" , false )
85+ }
86+
87+ // LogUserDeletion adds a log entry to indicate that a user was deleted. Non-blocking
88+ func LogUserDeletion (modifiedUser , userEditor models.User ) {
89+ createLogEntry (categoryAuth , fmt .Sprintf ("%s (#%d) was deleted by %s (user #%d)" ,
90+ modifiedUser .Name , modifiedUser .Id , userEditor .Name , userEditor .Id ), false )
91+ }
92+
93+ // LogUserEdit adds a log entry to indicate that a user was modified. Non-blocking
94+ func LogUserEdit (modifiedUser , userEditor models.User ) {
95+ createLogEntry (categoryAuth , fmt .Sprintf ("%s (#%d) was modified by %s (user #%d)" ,
96+ modifiedUser .Name , modifiedUser .Id , userEditor .Name , userEditor .Id ), false )
97+ }
98+
99+ // LogUserCreation adds a log entry to indicate that a user was created. Non-blocking
100+ func LogUserCreation (modifiedUser , userEditor models.User ) {
101+ createLogEntry (categoryAuth , fmt .Sprintf ("%s (#%d) was created by %s (user #%d)" ,
102+ modifiedUser .Name , modifiedUser .Id , userEditor .Name , userEditor .Id ), false )
103+ }
104+
105+ // LogDownload adds a log entry when a download was requested. Non-Blocking
106+ func LogDownload (file models.File , r * http.Request , saveIp bool ) {
44107 if saveIp {
45- AddString ( fmt .Sprintf ("Download: Filename %s, IP %s, ID %s, Useragent %s" , file .Name , getIpAddress (r ), file .Id , r .UserAgent ()))
108+ createLogEntry ( categoryDownload , fmt .Sprintf ("%s, IP %s, ID %s, Useragent %s" , file .Name , getIpAddress (r ), file .Id , r .UserAgent ()), false )
46109 } else {
47- AddString (fmt .Sprintf ("Download: Filename %s, ID %s, Useragent %s" , file .Name , file .Id , r .UserAgent ()))
110+ createLogEntry (categoryDownload , fmt .Sprintf ("%s, ID %s, Useragent %s" , file .Name , file .Id , r .UserAgent ()), false )
111+ }
112+ }
113+
114+ // LogUpload adds a log entry when an upload was created. Non-Blocking
115+ func LogUpload (file models.File , user models.User ) {
116+ createLogEntry (categoryUpload , fmt .Sprintf ("%s, ID %s, uploaded by %s (user #%d)" , file .Name , file .Id , user .Name , user .Id ), false )
117+ }
118+
119+ // LogEdit adds a log entry when an upload was edited. Non-Blocking
120+ func LogEdit (file models.File , user models.User ) {
121+ createLogEntry (categoryEdit , fmt .Sprintf ("%s, ID %s, edited by %s (user #%d)" , file .Name , file .Id , user .Name , user .Id ), false )
122+ }
123+
124+ // LogReplace adds a log entry when an upload was replaced. Non-Blocking
125+ func LogReplace (originalFile , newContent models.File , user models.User ) {
126+ createLogEntry (categoryEdit , fmt .Sprintf ("%s, ID %s had content replaced with %s (ID %s) by %s (user #%d)" ,
127+ originalFile .Name , originalFile .Id , newContent .Name , newContent .Id , user .Name , user .Id ), false )
128+ }
129+
130+ // LogDelete adds a log entry when an upload was deleted. Non-Blocking
131+ func LogDelete (file models.File , user models.User ) {
132+ createLogEntry (categoryEdit , fmt .Sprintf ("%s, ID %s, deleted by %s (user #%d)" , file .Name , file .Id , user .Name , user .Id ), false )
133+ }
134+
135+ // UpgradeToV2 adds tags to existing logs
136+ // deprecated
137+ func UpgradeToV2 () {
138+ content , exists := GetAll ()
139+ mutex .Lock ()
140+ if ! exists {
141+ return
142+ }
143+ var newLogs strings.Builder
144+ scanner := bufio .NewScanner (strings .NewReader (content ))
145+ for scanner .Scan () {
146+ line := scanner .Text ()
147+ if strings .Contains (line , "Gokapi started" ) {
148+ line = strings .Replace (line , "Gokapi started" , "[" + categoryInfo + "] Gokapi started" , 1 )
149+ }
150+ if strings .Contains (line , "Download: Filename" ) {
151+ line = strings .Replace (line , "Download: Filename" , "[" + categoryDownload + "] Filename" , 1 )
152+ }
153+ newLogs .WriteString (line )
154+ newLogs .WriteString ("\n " )
48155 }
156+ helper .Check (scanner .Err ())
157+ err := os .WriteFile (logPath , []byte (newLogs .String ()), 0600 )
158+ helper .Check (err )
159+ defer mutex .Unlock ()
160+ }
161+
162+ func DeleteLogs (userName string , userId int , cutoff int64 , r * http.Request ) {
163+ if cutoff == 0 {
164+ deleteAllLogs (userName , userId , r )
165+ return
166+ }
167+ mutex .Lock ()
168+ logFile , err := os .ReadFile (logPath )
169+ helper .Check (err )
170+ var newFile strings.Builder
171+ scanner := bufio .NewScanner (strings .NewReader (string (logFile )))
172+ newFile .WriteString (getLogDeletionMessage (userName , userId , r , time .Unix (cutoff , 0 )))
173+ for scanner .Scan () {
174+ line := scanner .Text ()
175+ timeEntry , err := parseTimeLogEntry (line )
176+ if err != nil {
177+ fmt .Println (err )
178+ continue
179+ }
180+ if timeEntry .Unix () > cutoff {
181+ newFile .WriteString (line + "\n " )
182+ }
183+ }
184+ err = os .WriteFile (logPath , []byte (newFile .String ()), 0600 )
185+ helper .Check (err )
186+ defer mutex .Unlock ()
187+ }
188+
189+ func parseTimeLogEntry (input string ) (time.Time , error ) {
190+ const layout = "Mon, 02 Jan 2006 15:04:05 MST"
191+ lineContent := strings .Split (input , " [" )
192+ return time .Parse (layout , lineContent [0 ])
193+ }
194+
195+ func getLogDeletionMessage (userName string , userId int , r * http.Request , timestamp time.Time ) string {
196+ return createLogFormatCustomTimestamp (categoryWarning , fmt .Sprintf ("Previous logs deleted by %s (user #%d) on %s. IP: %s\n " ,
197+ userName , userId , getDate (time .Now ()), getIpAddress (r )), timestamp )
198+ }
199+
200+ func deleteAllLogs (userName string , userId int , r * http.Request ) {
201+ mutex .Lock ()
202+ defer mutex .Unlock ()
203+ message := getLogDeletionMessage (userName , userId , r , time .Now ())
204+ err := os .WriteFile (logPath , []byte (message ), 0600 )
205+ helper .Check (err )
49206}
50207
51208func writeToFile (text string ) {
@@ -58,8 +215,8 @@ func writeToFile(text string) {
58215 helper .Check (err )
59216}
60217
61- func formatDate ( input string ) string {
62- return time . Now (). UTC ().Format (time .RFC1123 ) + " " + input
218+ func getDate ( timestamp time. Time ) string {
219+ return timestamp . UTC ().Format (time .RFC1123 )
63220}
64221
65222func getIpAddress (r * http.Request ) string {
0 commit comments