2019-09-01 23:19:10 +00:00
|
|
|
package database
|
|
|
|
|
|
|
|
import (
|
2019-09-07 10:56:13 +00:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"net/http"
|
|
|
|
|
2019-09-01 23:19:10 +00:00
|
|
|
"github.com/aerogo/aero"
|
2019-09-07 10:56:13 +00:00
|
|
|
"github.com/aerogo/api"
|
|
|
|
"github.com/akyoto/stringutils/unsafe"
|
|
|
|
"github.com/animenotifier/notify.moe/arn"
|
|
|
|
"github.com/mohae/deepcopy"
|
2019-09-01 23:19:10 +00:00
|
|
|
)
|
|
|
|
|
2019-09-07 10:56:13 +00:00
|
|
|
// privateTypes are types that are not available for download.
|
|
|
|
var privateTypes = []string{
|
2019-09-07 11:09:55 +00:00
|
|
|
"Analytics",
|
2019-11-04 07:34:00 +00:00
|
|
|
"Crash",
|
|
|
|
"ClientErrorReport",
|
2019-09-07 10:56:13 +00:00
|
|
|
"EditLogEntry",
|
|
|
|
"EmailToUser",
|
2019-09-07 11:09:55 +00:00
|
|
|
"FacebookToUser",
|
2019-09-07 10:56:13 +00:00
|
|
|
"PayPalPayment",
|
|
|
|
"Purchase",
|
|
|
|
"Session",
|
2019-09-07 11:09:55 +00:00
|
|
|
"TwitterToUser",
|
2019-09-07 10:56:13 +00:00
|
|
|
}
|
|
|
|
|
2019-09-01 23:19:10 +00:00
|
|
|
// Download downloads a snapshot of a database collection.
|
|
|
|
func Download(ctx aero.Context) error {
|
2019-09-07 10:56:13 +00:00
|
|
|
typ := ctx.Get("type")
|
|
|
|
|
|
|
|
if !arn.DB.HasType(typ) {
|
|
|
|
return ctx.Error(http.StatusNotFound, "Type doesn't exist")
|
|
|
|
}
|
|
|
|
|
|
|
|
if arn.Contains(privateTypes, typ) {
|
|
|
|
return ctx.Error(http.StatusUnauthorized, "Type is private and can not be downloaded")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send headers necessary for file downloads
|
|
|
|
ctx.Response().SetHeader("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.dat"`, typ))
|
|
|
|
|
|
|
|
// Stream data
|
|
|
|
reader, writer := io.Pipe()
|
|
|
|
encoder := json.NewEncoder(writer)
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
for object := range arn.DB.All(typ) {
|
|
|
|
idObject, hasID := object.(arn.Identifiable)
|
|
|
|
|
|
|
|
if !hasID {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// Filter out private data
|
|
|
|
filter, isFilter := object.(api.Filter)
|
|
|
|
|
|
|
|
if isFilter && filter.ShouldFilter(ctx) {
|
|
|
|
object = deepcopy.Copy(object)
|
|
|
|
filter = object.(api.Filter)
|
|
|
|
filter.Filter()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write ID
|
|
|
|
_, err := writer.Write(unsafe.StringToBytes(idObject.GetID()))
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
_ = writer.CloseWithError(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write newline
|
|
|
|
_, err = writer.Write([]byte("\n"))
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
_ = writer.CloseWithError(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write JSON (newline included)
|
|
|
|
err = encoder.Encode(object)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
_ = writer.CloseWithError(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
writer.Close()
|
|
|
|
}()
|
|
|
|
|
|
|
|
return ctx.Reader(reader)
|
2019-09-01 23:19:10 +00:00
|
|
|
}
|