commento/api/domain_import_commento.go
igolaizola 0d929595cc api: import from commento export format
JSON data can be imported to restore previously exported data
or to migrate data from another self-hosted commento instance.

Closes https://gitlab.com/commento/commento/issues/239
2020-03-31 06:39:52 -04:00

169 lines
4.2 KiB
Go

package main
import (
"bytes"
"compress/gzip"
"encoding/json"
"io/ioutil"
"net/http"
)
type dataImport struct {
Version int `json:"version"`
Comments []comment `json:"comments"`
Commenters []commenter `json:"commenters"`
}
func domainImportCommento(domain string, url string) (int, error) {
if domain == "" || url == "" {
return 0, errorMissingField
}
resp, err := http.Get(url)
if err != nil {
logger.Errorf("cannot get url: %v", err)
return 0, errorCannotDownloadCommento
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
logger.Errorf("cannot read body: %v", err)
return 0, errorCannotDownloadCommento
}
zr, err := gzip.NewReader(bytes.NewBuffer(body))
if err != nil {
logger.Errorf("cannot create gzip reader: %v", err)
return 0, errorInternal
}
contents, err := ioutil.ReadAll(zr)
if err != nil {
logger.Errorf("cannot read gzip contents uncompressed: %v", err)
return 0, errorInternal
}
var data dataImport
if err := json.Unmarshal(contents, &data); err != nil {
logger.Errorf("cannot unmarshal JSON at %s: %v", url, err)
return 0, errorInternal
}
if data.Version != 1 {
logger.Errorf("invalid data version (got %d, want 1): %v", data.Version, err)
return 0, errorUnsupportedCommentoImportVersion
}
// Check if imported commentedHex or email exists, creating a map of
// commenterHex (old hex, new hex)
commenterHex := map[string]string{"anonymous": "anonymous"}
for _, commenter := range data.Commenters {
c, err := commenterGetByEmail("commento", commenter.Email)
if err != nil && err != errorNoSuchCommenter {
logger.Errorf("cannot get commenter by email: %v", err)
return 0, errorInternal
}
if err == nil {
commenterHex[commenter.CommenterHex] = c.CommenterHex
continue
}
randomPassword, err := randomHex(32)
if err != nil {
logger.Errorf("cannot generate random password for new commenter: %v", err)
return 0, errorInternal
}
commenterHex[commenter.CommenterHex], err = commenterNew(commenter.Email,
commenter.Name, commenter.Link, commenter.Photo, "commento", randomPassword)
if err != nil {
return 0, err
}
}
// Create a map of (parent hex, comments)
comments := make(map[string][]comment)
for _, comment := range data.Comments {
parentHex := comment.ParentHex
comments[parentHex] = append(comments[parentHex], comment)
}
// Import comments, creating a map of comment hex (old hex, new hex)
commentHex := map[string]string{"root": "root"}
numImported := 0
keys := []string{"root"}
for i := 0; i < len(keys); i++ {
for _, comment := range comments[keys[i]] {
cHex, ok := commenterHex[comment.CommenterHex]
if !ok {
logger.Errorf("cannot get commenter: %v", err)
return numImported, errorInternal
}
parentHex, ok := commentHex[comment.ParentHex]
if !ok {
logger.Errorf("cannot get parent comment: %v", err)
return numImported, errorInternal
}
hex, err := commentNew(
cHex,
domain,
comment.Path,
parentHex,
comment.Markdown,
comment.State,
comment.CreationDate)
if err != nil {
return numImported, err
}
commentHex[comment.CommentHex] = hex
numImported++
keys = append(keys, comment.CommentHex)
}
}
return numImported, nil
}
func domainImportCommentoHandler(w http.ResponseWriter, r *http.Request) {
type request struct {
OwnerToken *string `json:"ownerToken"`
Domain *string `json:"domain"`
URL *string `json:"url"`
}
var x request
if err := bodyUnmarshal(r, &x); err != nil {
bodyMarshal(w, response{"success": false, "message": err.Error()})
return
}
o, err := ownerGetByOwnerToken(*x.OwnerToken)
if err != nil {
bodyMarshal(w, response{"success": false, "message": err.Error()})
return
}
domain := domainStrip(*x.Domain)
isOwner, err := domainOwnershipVerify(o.OwnerHex, domain)
if err != nil {
bodyMarshal(w, response{"success": false, "message": err.Error()})
return
}
if !isOwner {
bodyMarshal(w, response{"success": false, "message": errorNotAuthorised.Error()})
return
}
numImported, err := domainImportCommento(domain, *x.URL)
if err != nil {
bodyMarshal(w, response{"success": false, "message": err.Error()})
return
}
bodyMarshal(w, response{"success": true, "numImported": numImported})
}