A web blog and personal homepage engine written in Go.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

251 lines
6.0 KiB

package main
import (
"flag"
"fmt"
"os"
"path/filepath"
"sort"
"git.kirsle.net/apps/gophertype/pkg"
"git.kirsle.net/apps/gophertype/pkg/console"
_ "git.kirsle.net/apps/gophertype/pkg/controllers"
"git.kirsle.net/apps/gophertype/pkg/models"
_ "github.com/jinzhu/gorm/dialects/mysql"
_ "github.com/jinzhu/gorm/dialects/postgres"
_ "github.com/jinzhu/gorm/dialects/sqlite"
jsondb "github.com/kirsle/blog/jsondb"
mComments "github.com/kirsle/blog/models/comments"
mPosts "github.com/kirsle/blog/models/posts"
)
// Command-line flags.
var (
optSourceRoot string
optRoot string
// Database option flags.
optSQLite string
optPostgres string
optMySQL string
// Chosen DB options.
dbDriver string
dbPath string
)
// Other global variables
var (
JsonDB *jsondb.DB
)
func init() {
flag.StringVar(&optSourceRoot, "srcroot", "", "User root from old kirsle/blog website")
flag.StringVar(&optRoot, "root", "", "User root for GopherType")
// Database driver. Choose one.
flag.StringVar(&optSQLite, "sqlite3", "", "Use SQLite database, default 'database.sqlite'")
flag.StringVar(&optPostgres, "postgres", "",
"Use Postgres database, format: "+
"host=myhost port=myport user=gorm dbname=gorm password=mypassword")
flag.StringVar(&optMySQL, "mysql", "",
"Use MySQL database, format: "+
"user:password@/dbname?charset=utf8&parseTime=True&loc=Local")
}
func main() {
console.SetDebug(false)
flag.Parse()
// Validate the choice of database.
if optSQLite != "" {
dbDriver = "sqlite3"
dbPath = optSQLite
} else if optPostgres != "" {
dbDriver = "postgres"
dbPath = optPostgres
} else if optMySQL != "" {
dbDriver = "mysql"
dbPath = optMySQL
} else {
fmt.Print(
"Specify a DB driver for Gophertype, similar to the gophertype command.",
)
os.Exit(1)
}
// Validate the roots are given.
if optSourceRoot == "" {
fmt.Print(
"Missing -srcroot parameter: this should be the User Root from the legacy kirsle/blog site.",
)
os.Exit(1)
}
if optRoot == "" {
fmt.Print(
"Missing required -root parameter: this is the User Root for Gophertype",
)
}
// Initialize the old JsonDB.
app := gophertype.NewSite(optRoot)
app.UseDB(dbDriver, dbPath)
initJsonDB()
doMigrate()
}
// Initialize the old kirsle/blog JsonDB.
func initJsonDB() {
JsonDB = jsondb.New(filepath.Join(optSourceRoot, ".private"))
mPosts.DB = JsonDB
mComments.DB = JsonDB
}
// doMigrate is the head of the migrate functions.
func doMigrate() {
migratePosts()
migrateComments()
resetSerial("posts")
}
// migratePosts migrates blog posts over.
func migratePosts() {
console.Warn("BEGIN: Migrating blog posts")
idx, err := mPosts.GetIndex()
if err != nil {
panic("migratePosts: GetIndex: " + err.Error())
}
// Sort the IDs to make it pretty.
var sortedIds = []int{}
for id := range idx.Posts {
sortedIds = append(sortedIds, id)
}
sort.Ints(sortedIds)
for _, id := range sortedIds {
post, err := mPosts.Load(id)
if err != nil {
panic(fmt.Sprintf("migratePosts: error loading legacy post ID %d: %s", id, err))
}
console.Info("Post ID %d: %s", id, post.Title)
// Create the post in Gophertype.
p := models.Post{
Title: post.Title,
Fragment: post.Fragment,
ContentType: post.ContentType,
AuthorID: post.AuthorID,
Body: post.Body,
Privacy: post.Privacy,
Sticky: post.Sticky,
EnableComments: post.EnableComments,
}
p.ID = post.ID
p.CreatedAt = post.Created
p.UpdatedAt = post.Updated
// Convert tags.
for _, tag := range post.Tags {
p.Tags = append(p.Tags, models.TaggedPost{
Tag: tag,
})
}
err = p.Save()
if err != nil {
console.Error("Error saving post %d: %s", p.ID, err)
}
// Set the correct updated_at time.
models.DB.Table("posts").Where("id = ?", p.ID).Updates(map[string]interface{}{
"updated_at": post.Updated,
})
}
console.Warn("FINISH: Migrating blog posts")
}
// migrateComments migrates comments over.
func migrateComments() {
console.Warn("BEGIN: Migrating comments")
// Only migrate comments one time.
var count int
models.DB.Model(&models.Comment{}).Count(&count)
if count > 0 {
console.Info("Comments already seem imported (count > 0, count == %d); skipping.", count)
return
}
// Find all the comment threads.
files, err := JsonDB.List("comments/threads")
if err != nil {
panic("Error listing comments: " + err.Error())
}
for _, file := range files {
document := filepath.Base(file)
thread, err := mComments.Load(document)
if err != nil {
panic(fmt.Sprintf("Error loading comment thread %s: %s", file, err))
}
for _, comment := range thread.Comments {
// Migrate to the new model.
com := models.Comment{
Thread: thread.ID,
UserID: comment.UserID,
Name: comment.Name,
Email: comment.Email,
Avatar: comment.Avatar,
Body: comment.Body,
EditToken: comment.EditToken,
DeleteToken: comment.DeleteToken,
}
com.CreatedAt = comment.Created
com.UpdatedAt = comment.Updated
// Special case for guestbook page.
if thread.ID == "guestbook" {
com.OriginURL = "/guestbook"
}
if err := com.Save(); err != nil {
console.Error("Error saving comment: %s", err)
}
}
}
console.Warn("FINISH: Migrating comments")
}
// resetSerial fixes the auto-incrementing ID for the next row added.
func resetSerial(table string) {
// Get the max ID from the table.
result := struct {
Max int
}{}
models.DB.Table(table).Select("max(id) AS max").Scan(&result)
// Run the query based on dialect.
var query string
switch dbDriver {
case "postgres":
query = fmt.Sprintf("ALTER SEQUENCE %s RESTART WITH %d", table+"_id_seq", result.Max+1)
case "sqlite3":
query = fmt.Sprintf("UPDATE SQLITE_SEQUENCE SET SEQ=%d WHERE NAME=%s", result.Max+1, table)
case "mysql":
query = fmt.Sprintf("ALTER TABLE %s AUTO_INCREMENT = %d", table, result.Max+1)
}
if len(query) > 0 {
var noop []string
r := models.DB.Raw(query).Scan(&noop)
if r.Error != nil {
console.Error("Error with query `%s`: %s", query, r.Error)
}
}
}