GraphQL API and utilities for the rpdata project
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.

318 lines
7.5 KiB

  1. package main
  2. import (
  3. "archive/zip"
  4. "context"
  5. "encoding/json"
  6. "flag"
  7. "fmt"
  8. "git.aiterp.net/rpdata/api/database"
  9. "git.aiterp.net/rpdata/api/database/mongodb"
  10. "git.aiterp.net/rpdata/api/internal/config"
  11. "git.aiterp.net/rpdata/api/models"
  12. "log"
  13. "strings"
  14. "time"
  15. )
  16. var flagDriver = flag.String("driver", "postgres", "The database driver to use.")
  17. var flagHost = flag.String("host", "127.0.0.1", "The host to connect to.")
  18. var flagPort = flag.Int("port", 5432, "The port to connect on.")
  19. var flagDb = flag.String("db", "rpdata", "The database name")
  20. var flagUsername = flag.String("username", "", "")
  21. var flagPassword = flag.String("password", "", "")
  22. var flagMechanism = flag.String("mechanism", "", "")
  23. var flagInputFile = flag.String("infile", "dump.zip", "The file to read from.")
  24. var flagIncludeKeys = flag.Bool("include-keys", false, "Whether to include the keys.")
  25. var flagReplace = flag.Bool("replace", false, "Replace existing content")
  26. var flagOnly = flag.String("only", "", "Only restore these models (e.g. \"log,post\")")
  27. func main() {
  28. flag.Parse()
  29. cfg := config.Database{
  30. Driver: *flagDriver,
  31. Host: *flagHost,
  32. Port: *flagPort,
  33. Db: *flagDb,
  34. Username: *flagUsername,
  35. Password: *flagPassword,
  36. Mechanism: *flagMechanism,
  37. RestoreIDs: true,
  38. }
  39. var only map[string]bool
  40. if *flagOnly != "" {
  41. only = make(map[string]bool)
  42. for _, part := range strings.Split(*flagOnly, ",") {
  43. only[strings.Trim(part, "  \t\n\r")] = true
  44. }
  45. }
  46. mongodb.DisableFixes = true
  47. db, err := database.Init(cfg)
  48. if err != nil {
  49. log.Fatalln("Failed to open database:", err)
  50. }
  51. cfg2 := cfg
  52. cfg2.RestoreIDs = false
  53. zipReader, err := zip.OpenReader(*flagInputFile)
  54. if err != nil {
  55. log.Fatalln("Failed to open input file:", err)
  56. }
  57. defer func() {
  58. err = zipReader.Close()
  59. if err != nil {
  60. log.Fatalln("Failed to close input file:", err)
  61. }
  62. }()
  63. ctx, cancel := context.WithTimeout(context.Background(), time.Minute*30)
  64. defer cancel()
  65. postMap := make(map[string][]*models.Post)
  66. for _, file := range zipReader.File {
  67. if strings.HasSuffix(file.Name, "/") {
  68. continue
  69. }
  70. parts := strings.Split(file.Name, "/")
  71. if len(parts) < 3 || parts[0] != "rpdata_dump_v1" {
  72. log.Fatalln("Unrecognized file path:", file.Name)
  73. }
  74. if only != nil && !only[parts[1]] {
  75. continue
  76. }
  77. reader, err := file.Open()
  78. if err != nil {
  79. log.Fatalln("Unrecognized file:", file.Name, err)
  80. }
  81. hideList := make(map[string]bool)
  82. log.Println("Loaded", parts[1], parts[2], "from archive.")
  83. switch parts[1] {
  84. case "character":
  85. {
  86. character := models.Character{}
  87. err := json.NewDecoder(reader).Decode(&character)
  88. if err != nil {
  89. log.Fatalln("Could not parse character:", parts[2], err)
  90. }
  91. if *flagReplace {
  92. _ = db.Characters().Delete(ctx, character)
  93. }
  94. _, err = db.Characters().Insert(ctx, character)
  95. if err != nil {
  96. log.Fatalln("Could not insert character:", parts[2], err)
  97. }
  98. log.Println("Character", character.Name, "inserted.")
  99. }
  100. case "channel":
  101. {
  102. channel := models.Channel{}
  103. err := json.NewDecoder(reader).Decode(&channel)
  104. if err != nil {
  105. log.Fatalln("Could not parse channel:", parts[2], err)
  106. }
  107. if *flagReplace {
  108. _ = db.Channels().Remove(ctx, channel)
  109. }
  110. _, err = db.Channels().Insert(ctx, channel)
  111. if err != nil {
  112. log.Fatalln("Could not insert channel:", parts[2], err)
  113. }
  114. log.Println("Channel", channel.Name, "inserted.")
  115. }
  116. case "change":
  117. {
  118. change := models.Change{}
  119. err := json.NewDecoder(reader).Decode(&change)
  120. if err != nil {
  121. log.Fatalln("Could not parse character:", parts[2], err)
  122. }
  123. if *flagReplace {
  124. _ = db.Changes().Remove(ctx, change)
  125. }
  126. _, err = db.Changes().Insert(ctx, change)
  127. if err != nil {
  128. log.Fatalln("Could not insert character:", parts[2], err)
  129. }
  130. if change.Listed {
  131. log.Println("Change", change.ID, "inserted.")
  132. } else {
  133. log.Println("Unlisted change inserted.")
  134. }
  135. }
  136. case "story":
  137. {
  138. story := models.Story{}
  139. err := json.NewDecoder(reader).Decode(&story)
  140. if err != nil {
  141. log.Fatalln("Could not parse story:", parts[2], err)
  142. }
  143. if *flagReplace {
  144. _ = db.Stories().Delete(ctx, story)
  145. }
  146. _, err = db.Stories().Insert(ctx, story)
  147. if err != nil {
  148. log.Fatalln("Could not insert story:", parts[2], err)
  149. }
  150. if story.Listed {
  151. log.Println("Story", story.Name, "inserted.")
  152. } else {
  153. log.Println("Unlisted story inserted.")
  154. hideList[story.ID] = true
  155. }
  156. }
  157. case "chapter":
  158. {
  159. chapter := models.Chapter{}
  160. err := json.NewDecoder(reader).Decode(&chapter)
  161. if err != nil {
  162. log.Fatalln("Could not parse story:", parts[2], err)
  163. }
  164. if *flagReplace {
  165. _ = db.Chapters().Delete(ctx, chapter)
  166. }
  167. _, err = db.Chapters().Insert(ctx, chapter)
  168. if err != nil {
  169. log.Fatalln("Could not insert story:", parts[2], err)
  170. }
  171. if !hideList[chapter.StoryID] {
  172. log.Println("Chapter", fmt.Sprintf("%s (id: %s)", chapter.Title, chapter.ID), "inserted.")
  173. } else {
  174. log.Println("Unlisted chapter inserted.")
  175. hideList[chapter.ID] = true
  176. }
  177. }
  178. case "comment":
  179. {
  180. comment := models.Comment{}
  181. err := json.NewDecoder(reader).Decode(&comment)
  182. if err != nil {
  183. log.Fatalln("Could not parse story:", parts[2], err)
  184. }
  185. if *flagReplace {
  186. _ = db.Comments().Delete(ctx, comment)
  187. }
  188. _, err = db.Comments().Insert(ctx, comment)
  189. if err != nil {
  190. log.Fatalln("Could not insert story:", parts[2], err)
  191. }
  192. if !hideList[comment.ChapterID] {
  193. log.Println("Comment", comment.Subject, "inserted.")
  194. } else {
  195. log.Println("Unlisted comment inserted.")
  196. }
  197. }
  198. case "log":
  199. {
  200. logg := models.Log{}
  201. err := json.NewDecoder(reader).Decode(&logg)
  202. if err != nil {
  203. log.Fatalln("Could not parse log:", parts[2], err)
  204. }
  205. if *flagReplace {
  206. _ = db.Logs().Delete(ctx, logg)
  207. }
  208. _, err = db.Logs().Insert(ctx, logg)
  209. if err != nil {
  210. log.Fatalln("Could not insert log:", parts[2], err)
  211. }
  212. log.Println("Log", logg.Date.Format(time.RFC3339)[:16], logg.ChannelName, "inserted.")
  213. }
  214. case "post":
  215. {
  216. post := models.Post{}
  217. err := json.NewDecoder(reader).Decode(&post)
  218. if err != nil {
  219. log.Fatalln("Could not parse post:", parts[2], err)
  220. }
  221. postMap[post.LogID] = append(postMap[post.LogID], &post)
  222. }
  223. case "user":
  224. {
  225. user := models.User{}
  226. err := json.NewDecoder(reader).Decode(&user)
  227. if err != nil {
  228. log.Fatalln("Could not parse post:", parts[2], err)
  229. }
  230. _, err = db.Users().Insert(ctx, user)
  231. if err != nil {
  232. log.Fatalln("Could not insert user:", parts[2], err)
  233. }
  234. log.Println("User", user.ID, "inserted.")
  235. }
  236. case "key":
  237. {
  238. if !*flagIncludeKeys {
  239. break
  240. }
  241. key := models.Key{}
  242. err := json.NewDecoder(reader).Decode(&key)
  243. if err != nil {
  244. log.Fatalln("Could not parse key:", parts[2], err)
  245. }
  246. _, err = db.Keys().Insert(ctx, key)
  247. if err != nil {
  248. log.Fatalln("Could not insert key:", parts[2], err)
  249. }
  250. log.Println("Key", key.ID, "inserted.")
  251. }
  252. }
  253. _ = reader.Close()
  254. }
  255. if only == nil || only["post"] {
  256. for _, posts := range postMap {
  257. log.Printf("Inserting %d posts for log %s...", len(posts), posts[0].LogID)
  258. _, err = db.Posts().InsertMany(ctx, posts...)
  259. if err != nil {
  260. log.Fatalln("Could not insert post for logId:", posts[0].LogID, err)
  261. }
  262. log.Println("\tDone!")
  263. }
  264. }
  265. }