diff --git a/arn/ClientErrorReport.go b/arn/ClientErrorReport.go
index 4c9d6d8a..192242ba 100644
--- a/arn/ClientErrorReport.go
+++ b/arn/ClientErrorReport.go
@@ -14,7 +14,7 @@ type ClientErrorReport struct {
hasCreator
}
-// StreamClientErrorReports returns a stream of all characters.
+// StreamClientErrorReports returns a stream of all client error reports.
func StreamClientErrorReports() <-chan *ClientErrorReport {
channel := make(chan *ClientErrorReport, nano.ChannelBufferSize)
@@ -29,10 +29,9 @@ func StreamClientErrorReports() <-chan *ClientErrorReport {
return channel
}
-// AllClientErrorReports returns a slice of all characters.
+// AllClientErrorReports returns a slice of all client error reports.
func AllClientErrorReports() []*ClientErrorReport {
all := make([]*ClientErrorReport, 0, DB.Collection("ClientErrorReport").Count())
-
stream := StreamClientErrorReports()
for obj := range stream {
diff --git a/arn/ClientErrorReportAPI.go b/arn/ClientErrorReportAPI.go
index 146b93c3..73985636 100644
--- a/arn/ClientErrorReportAPI.go
+++ b/arn/ClientErrorReportAPI.go
@@ -7,6 +7,7 @@ import (
"github.com/aerogo/api"
)
+// Force interface implementations
var (
_ api.Newable = (*ClientErrorReport)(nil)
)
diff --git a/arn/Crash.go b/arn/Crash.go
new file mode 100644
index 00000000..83f1f6c2
--- /dev/null
+++ b/arn/Crash.go
@@ -0,0 +1,39 @@
+package arn
+
+import "github.com/aerogo/nano"
+
+// Crash contains data about server crashes.
+type Crash struct {
+ Error string `json:"error"`
+ Stack string `json:"stack"`
+
+ hasID
+ hasCreator
+}
+
+// StreamCrashes returns a stream of all crashes.
+func StreamCrashes() <-chan *Crash {
+ channel := make(chan *Crash, nano.ChannelBufferSize)
+
+ go func() {
+ for obj := range DB.All("Crash") {
+ channel <- obj.(*Crash)
+ }
+
+ close(channel)
+ }()
+
+ return channel
+}
+
+// AllCrashes returns a slice of all crashes.
+func AllCrashes() []*Crash {
+ all := make([]*Crash, 0, DB.Collection("Crash").Count())
+ stream := StreamCrashes()
+
+ for obj := range stream {
+ all = append(all, obj)
+ }
+
+ return all
+}
diff --git a/arn/CrashAPI.go b/arn/CrashAPI.go
new file mode 100644
index 00000000..faa40e74
--- /dev/null
+++ b/arn/CrashAPI.go
@@ -0,0 +1,13 @@
+package arn
+
+import "github.com/aerogo/api"
+
+// Force interface implementations
+var (
+ _ api.Savable = (*Crash)(nil)
+)
+
+// Save saves the crash in the database.
+func (crash *Crash) Save() {
+ DB.Set("Crash", crash.ID, crash)
+}
diff --git a/arn/Database.go b/arn/Database.go
index ea1647ef..f01c8b32 100644
--- a/arn/Database.go
+++ b/arn/Database.go
@@ -25,6 +25,7 @@ var DB = Node.Namespace("arn").RegisterTypes(
(*AnimeCharacters)(nil),
(*AnimeRelations)(nil),
(*AnimeList)(nil),
+ (*Crash)(nil),
(*Character)(nil),
(*ClientErrorReport)(nil),
(*Company)(nil),
diff --git a/middleware/Recover.go b/middleware/Recover.go
index 12a25d3b..66c1a726 100644
--- a/middleware/Recover.go
+++ b/middleware/Recover.go
@@ -8,6 +8,7 @@ import (
"strings"
"github.com/aerogo/aero"
+ "github.com/animenotifier/notify.moe/arn"
)
// Recover recovers from panics and shows them as the response body.
@@ -27,11 +28,28 @@ func Recover(next aero.Handler) aero.Handler {
}
stack := make([]byte, 4096)
- length := runtime.Stack(stack, true)
+ length := runtime.Stack(stack, false)
stackString := string(stack[:length])
fmt.Fprint(os.Stderr, stackString)
- message := err.Error() + "
" + strings.ReplaceAll(stackString, "\n", "
")
+ // Save crash in database
+ crash := &arn.Crash{
+ Error: err.Error(),
+ Stack: stackString,
+ }
+
+ crash.ID = arn.GenerateID("Crash")
+ crash.Created = arn.DateTimeUTC()
+ user := arn.GetUserFromContext(ctx)
+
+ if user != nil {
+ crash.CreatedBy = user.ID
+ }
+
+ crash.Save()
+
+ // Send HTML
+ message := "