Apply go fix ./... analysers (Go 1.26) across backend and examples:
- interface{} → any (type alias, no behaviour change)
- for i := 0; i < N; i++ → for range N / for i := range N
- slices.Contains / slices.ContainsFunc replacing manual loops
- strings.SplitSeq replacing strings.Split in range (avoids allocation)
- strings.CutPrefix replacing HasPrefix+TrimPrefix
- min() replacing manual if/else
- fmt.Appendf replacing []byte(fmt.Sprintf(...))
- strings.Builder replacing string += concatenation
- wg.Go(func(){}) replacing wg.Add(1)/go/wg.Done() pattern
- removed redundant ii := i loop variable copies (unnecessary since Go 1.22)
omitempty on struct-typed JSON fields: go fix removed omitempty from
struct-typed fields (time.Time, PostInfo, UserDetailEntry) because
encoding/json's omitempty never applied to struct types — it was always
a no-op. Kept as bare tags (no omitzero replacement) to preserve the
existing serialisation behaviour.
152 lines
3.8 KiB
Go
152 lines
3.8 KiB
Go
package migrator
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/url"
|
|
"time"
|
|
|
|
"github.com/umputun/remark42/backend/app/store"
|
|
|
|
log "github.com/go-pkgz/lgr"
|
|
)
|
|
|
|
// Commento implements Importer from commento export json
|
|
type Commento struct {
|
|
DataStore Store
|
|
}
|
|
|
|
// Credit: https://gitlab.com/commento/commento/-/blob/master/api/domain_import_commento.go#L11-L15
|
|
type commentoExport struct {
|
|
Version int `json:"version"`
|
|
Comments []commentoComment `json:"comments"`
|
|
Commenters []commentoCommenter `json:"commenters"`
|
|
}
|
|
|
|
// Credit: https://gitlab.com/commento/commento/-/blob/master/api/comment.go#L7-L20
|
|
type commentoComment struct {
|
|
CommentHex string `json:"commentHex"`
|
|
Domain string `json:"domain,omitempty"`
|
|
Path string `json:"url,omitempty"`
|
|
CommenterHex string `json:"commenterHex"`
|
|
Markdown string `json:"markdown"`
|
|
HTML string `json:"html"`
|
|
ParentHex string `json:"parentHex"`
|
|
Score int `json:"score"`
|
|
State string `json:"state,omitempty"`
|
|
CreationDate time.Time `json:"creationDate"`
|
|
Direction int `json:"direction"`
|
|
Deleted bool `json:"deleted"`
|
|
}
|
|
|
|
// Credit: https://gitlab.com/commento/commento/-/blob/master/api/commenter.go#L7-L16
|
|
type commentoCommenter struct {
|
|
CommenterHex string `json:"commenterHex,omitempty"`
|
|
Email string `json:"email,omitempty"`
|
|
Name string `json:"name"`
|
|
Link string `json:"link"`
|
|
Photo string `json:"photo"`
|
|
Provider string `json:"provider,omitempty"`
|
|
JoinDate time.Time `json:"joinDate"`
|
|
IsModerator bool `json:"isModerator"`
|
|
}
|
|
|
|
// Import comments from Commento and save to store
|
|
func (d *Commento) Import(r io.Reader, siteID string) (size int, err error) {
|
|
if e := d.DataStore.DeleteAll(siteID); e != nil {
|
|
return 0, e
|
|
}
|
|
|
|
commentsCh := d.convert(r, siteID)
|
|
failed, passed := 0, 0
|
|
for c := range commentsCh {
|
|
if _, err = d.DataStore.Create(c); err != nil {
|
|
failed++
|
|
continue
|
|
}
|
|
passed++
|
|
}
|
|
|
|
if failed > 0 {
|
|
err = fmt.Errorf("failed to save %d comments", failed)
|
|
if passed == 0 {
|
|
err = fmt.Errorf("import failed")
|
|
}
|
|
}
|
|
|
|
log.Printf("[DEBUG] imported %d comments to site %s", passed, siteID)
|
|
|
|
return passed, err
|
|
}
|
|
|
|
func (d *Commento) convert(r io.Reader, siteID string) (ch chan store.Comment) {
|
|
commentsCh := make(chan store.Comment)
|
|
|
|
decoder := json.NewDecoder(r)
|
|
|
|
go func() {
|
|
var exportedData commentoExport
|
|
err := decoder.Decode(&exportedData)
|
|
if err != nil {
|
|
log.Printf("[WARN] can't decode commento export json, %s", err.Error())
|
|
}
|
|
|
|
usersMap := map[string]store.User{}
|
|
for _, commenter := range exportedData.Commenters {
|
|
usersMap[commenter.CommenterHex] = store.User{
|
|
Name: commenter.Name,
|
|
ID: "commento_" + store.EncodeID(commenter.CommenterHex),
|
|
Picture: commenter.Photo,
|
|
}
|
|
}
|
|
|
|
usersMap["anonymous"] = store.User{
|
|
Name: "Anonymous",
|
|
ID: "commento_" + store.EncodeID("anonymous"),
|
|
}
|
|
|
|
for _, comment := range exportedData.Comments {
|
|
u, ok := usersMap[comment.CommenterHex]
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
if comment.Deleted {
|
|
continue
|
|
}
|
|
|
|
parentID := comment.ParentHex
|
|
// comments with ParentHex == "root" are top-level comments
|
|
if parentID == "root" {
|
|
parentID = ""
|
|
}
|
|
|
|
commentURL, e := url.JoinPath("https://", comment.Domain, comment.Path)
|
|
if e != nil {
|
|
log.Printf("[WARN] can't construct comment URL in commento import, %s", err.Error())
|
|
}
|
|
log.Printf("[ERROR] commentoURL: %s", commentURL)
|
|
|
|
c := store.Comment{
|
|
ID: comment.CommentHex,
|
|
Locator: store.Locator{
|
|
URL: commentURL,
|
|
SiteID: siteID,
|
|
},
|
|
User: u,
|
|
Text: comment.Markdown,
|
|
Timestamp: comment.CreationDate,
|
|
ParentID: parentID,
|
|
Imported: true,
|
|
}
|
|
|
|
commentsCh <- c
|
|
}
|
|
|
|
close(commentsCh)
|
|
}()
|
|
|
|
return commentsCh
|
|
}
|