admin_handlers.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. package controller
  2. import (
  3. "bytes"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "log"
  8. "net/http"
  9. "time"
  10. "git.aetherial.dev/aeth/keiji/pkg/auth"
  11. "git.aetherial.dev/aeth/keiji/pkg/storage"
  12. "github.com/gin-gonic/gin"
  13. )
  14. const AUTH_COOKIE_NAME = "X-Server-Auth"
  15. // @Name ServeLogin
  16. // @Summary serves the HTML login page
  17. // @Tags admin
  18. // @Router /login [get]
  19. func (c *Controller) ServeLogin(ctx *gin.Context) {
  20. cookie, _ := ctx.Cookie(AUTH_COOKIE_NAME)
  21. if c.Cache.Read(cookie) {
  22. ctx.Redirect(302, "/home")
  23. }
  24. ctx.HTML(http.StatusOK, "login", gin.H{
  25. "heading": "aetherial.dev login",
  26. })
  27. }
  28. // @Name Auth
  29. // @Summary serves recieves admin user and pass, sets a cookie
  30. // @Tags admin
  31. // @Param cred body storage.Credentials true "Admin Credentials"
  32. // @Router /login [post]
  33. func (c *Controller) Auth(ctx *gin.Context) {
  34. var cred auth.Credentials
  35. err := ctx.ShouldBind(&cred)
  36. if err != nil {
  37. ctx.JSON(400, map[string]string{
  38. "Error": err.Error(),
  39. })
  40. return
  41. }
  42. cookie, err := auth.Authorize(&cred, c.Cache, c.AuthSource)
  43. if err != nil {
  44. ctx.JSON(400, map[string]string{
  45. "Error": err.Error(),
  46. })
  47. return
  48. }
  49. ctx.SetCookie(AUTH_COOKIE_NAME, cookie, 3600, "/", c.Domain, false, false)
  50. ctx.HTML(http.StatusOK, "admin", gin.H{
  51. "navigation": gin.H{
  52. "headers": c.database.GetNavBarLinks(),
  53. "menu": c.database.GetDropdownElements(),
  54. },
  55. "Tables": c.database.GetAdminTables().Tables,
  56. })
  57. }
  58. /*
  59. @Name AddAdminTableEntry
  60. @Summary add an entry to the admin table
  61. @Tags admin
  62. @Router /admin/panel
  63. */
  64. func (c *Controller) AddAdminTableEntry(ctx *gin.Context) {
  65. tables := make(map[string][]storage.TableData)
  66. adminPage := storage.AdminPage{Tables: tables}
  67. err := ctx.ShouldBind(&adminPage)
  68. if err != nil {
  69. ctx.JSON(400, map[string]string{
  70. "Error": err.Error(),
  71. })
  72. return
  73. }
  74. for category := range adminPage.Tables {
  75. for entry := range adminPage.Tables[category] {
  76. err := c.database.AddAdminTableEntry(adminPage.Tables[category][entry], category)
  77. if err != nil {
  78. ctx.JSON(400, map[string]string{
  79. "Error": err.Error(),
  80. })
  81. return
  82. }
  83. }
  84. }
  85. ctx.Data(200, "text", []byte("Categories populated."))
  86. }
  87. /*
  88. @Name AddMenuItem
  89. @Summary add an entry to the sidebar menu
  90. @Tags admin
  91. @Router /admin/menu
  92. */
  93. func (c *Controller) AddMenuItem(ctx *gin.Context) {
  94. var item storage.LinkPair
  95. err := ctx.ShouldBind(&item)
  96. if err != nil {
  97. ctx.JSON(400, map[string]string{
  98. "Error": err.Error(),
  99. })
  100. return
  101. }
  102. err = c.database.AddMenuItem(item)
  103. if err != nil {
  104. ctx.JSON(400, map[string]string{
  105. "Error": err.Error(),
  106. })
  107. return
  108. }
  109. ctx.Data(200, "text", []byte("menu item added."))
  110. }
  111. /*
  112. @Name AddNavbarItem
  113. @Summary add an entry to the navbar
  114. @Tags admin
  115. @Router /admin/navbar
  116. */
  117. func (c *Controller) AddNavbarItem(ctx *gin.Context) {
  118. file, err := ctx.FormFile("file")
  119. if err != nil {
  120. ctx.HTML(400, "upload_status", gin.H{"UpdateMessage": err, "Color": "red"})
  121. return
  122. }
  123. fh, err := file.Open()
  124. if err != nil {
  125. ctx.HTML(500, "upload_status", gin.H{"UpdateMessage": err, "Color": "red"})
  126. return
  127. }
  128. fb := make([]byte, file.Size)
  129. var output bytes.Buffer
  130. for {
  131. n, err := fh.Read(fb)
  132. if err == io.EOF {
  133. break
  134. }
  135. if err != nil {
  136. ctx.HTML(500, "upload_status", gin.H{"UpdateMessage": err, "Color": "red"})
  137. return
  138. }
  139. output.Write(fb[:n])
  140. }
  141. var item storage.NavBarItem
  142. item = storage.NavBarItem{
  143. Link: file.Filename,
  144. Png: fb,
  145. }
  146. err = ctx.ShouldBind(&item)
  147. if err != nil {
  148. ctx.HTML(400, "upload_status", gin.H{"UpdateMessage": err, "Color": "red"})
  149. return
  150. }
  151. err = c.database.AddNavbarItem(item)
  152. if err != nil {
  153. ctx.HTML(500, "upload_status", gin.H{"UpdateMessage": err, "Color": "red"})
  154. return
  155. }
  156. err = c.database.AddAsset(item.File.Filename, fb)
  157. if err != nil {
  158. ctx.HTML(500, "upload_status", gin.H{"UpdateMessage": err, "Color": "red"})
  159. return
  160. }
  161. ctx.HTML(200, "upload_status", gin.H{"UpdateMessage": "Addition Successful!", "Color": "green"})
  162. }
  163. /*
  164. @Name RemoveNavbarItem
  165. @Summary remove an entry from the navbar
  166. @Tags admin
  167. @Router /admin/navbar
  168. */
  169. func (c *Controller) RemoveNavbarItem(ctx *gin.Context) {
  170. itemName, ok := ctx.Params.Get("item_name")
  171. if !ok {
  172. ctx.HTML(500, "upload_status", gin.H{"UpdateMessage": errors.New("No navbar name passed."), "Color": "red"})
  173. return
  174. }
  175. err := c.database.DeleteNavbarItem(storage.Identifier(itemName))
  176. if err != nil {
  177. ctx.HTML(500, "upload_status", gin.H{"UpdateMessage": err, "Color": "red"})
  178. return
  179. }
  180. ctx.HTML(200, "upload_status", gin.H{"UpdateMessage": "Deleted '" + itemName + "' Successfully!", "Color": "green"})
  181. }
  182. func (c *Controller) ServeNavbarModify(ctx *gin.Context) {
  183. navbar := c.database.GetNavBarLinks()
  184. tableData := storage.AdminPage{Tables: map[string][]storage.TableData{}}
  185. for i := range navbar {
  186. tableData.Tables["items"] = append(tableData.Tables["items"],
  187. storage.TableData{
  188. DisplayName: navbar[i].Link,
  189. Link: fmt.Sprintf("/admin/navbar/options/%s", navbar[i].Link),
  190. },
  191. )
  192. }
  193. ctx.HTML(200, "admin", gin.H{
  194. "navigation": gin.H{
  195. "menu": c.database.GetDropdownElements(),
  196. "headers": c.database.GetNavBarLinks(),
  197. },
  198. "Tables": tableData.Tables,
  199. })
  200. }
  201. /*
  202. @Name AddAsset
  203. @Summary add an asset to the db
  204. @Tags admin
  205. @Router /admin/assets
  206. */
  207. func (c *Controller) AddAsset(ctx *gin.Context) {
  208. var item storage.Asset
  209. err := ctx.ShouldBind(&item)
  210. if err != nil {
  211. ctx.JSON(400, map[string]string{
  212. "Error": err.Error(),
  213. })
  214. return
  215. }
  216. err = c.database.AddAsset(item.Name, item.Data)
  217. if err != nil {
  218. ctx.JSON(400, map[string]string{
  219. "Error": err.Error(),
  220. })
  221. return
  222. }
  223. }
  224. // @Name AdminPanel
  225. // @Summary serve the admin panel page
  226. // @Tags admin
  227. // @Router /admin/panel [get]
  228. func (c *Controller) AdminPanel(ctx *gin.Context) {
  229. ctx.HTML(http.StatusOK, "admin", gin.H{
  230. "navigation": gin.H{
  231. "headers": c.database.GetNavBarLinks(),
  232. "menu": c.database.GetDropdownElements(),
  233. },
  234. "Tables": c.database.GetAdminTables().Tables,
  235. })
  236. }
  237. /*
  238. Serves the admin panel with all of the documents in each blog category for editing
  239. */
  240. func (c *Controller) ServeBlogDirectory(ctx *gin.Context) {
  241. tableData := storage.AdminPage{Tables: map[string][]storage.TableData{}}
  242. for i := range storage.Topics {
  243. docs := c.database.GetByCategory(storage.Topics[i])
  244. for z := range docs {
  245. tableData.Tables[storage.Topics[i]] = append(tableData.Tables[storage.Topics[i]],
  246. storage.TableData{
  247. DisplayName: docs[z].Title,
  248. Link: fmt.Sprintf("/admin/options/%s", docs[z].Ident),
  249. },
  250. )
  251. }
  252. }
  253. ctx.HTML(200, "admin", gin.H{
  254. "navigation": gin.H{
  255. "menu": c.database.GetDropdownElements(),
  256. "headers": c.database.GetNavBarLinks(),
  257. },
  258. "Tables": tableData.Tables,
  259. })
  260. }
  261. /*
  262. Serves the blogpost editor with the submit button set to PATCH a document
  263. */
  264. func (c *Controller) GetBlogPostEditor(ctx *gin.Context) {
  265. post, exist := ctx.Params.Get("id")
  266. if !exist {
  267. ctx.JSON(404, map[string]string{
  268. "Error": "the requested file could not be found",
  269. })
  270. return
  271. }
  272. doc, err := c.database.GetDocument(storage.Identifier(post))
  273. if err != nil {
  274. ctx.JSON(500, map[string]string{
  275. "Error": err.Error(),
  276. })
  277. return
  278. }
  279. images := c.database.GetAllImages()
  280. imgIds := []storage.Identifier{}
  281. for i := range images {
  282. imgIds = append(imgIds, images[i].Ident)
  283. }
  284. ctx.HTML(200, "blogpost_editor", gin.H{
  285. "navigation": gin.H{
  286. "menu": c.database.GetDropdownElements(),
  287. "headers": c.database.GetNavBarLinks(),
  288. },
  289. "Ident": doc.Ident,
  290. "Topics": storage.Topics,
  291. "Title": doc.Title,
  292. "DefaultTopic": doc.Category,
  293. "Created": doc.Created,
  294. "Body": doc.Body,
  295. "ImageIds": imgIds,
  296. })
  297. }
  298. /*
  299. Update an existing blog post
  300. */
  301. func (c *Controller) UpdateBlogPost(ctx *gin.Context) {
  302. var doc storage.Document
  303. err := ctx.ShouldBind(&doc)
  304. if err != nil {
  305. ctx.HTML(500, "upload_status", gin.H{"UpdateMessage": "Update Failed!", "Color": "red"})
  306. return
  307. }
  308. err = c.database.UpdateDocument(doc)
  309. if err != nil {
  310. ctx.HTML(400, "upload_status", gin.H{"UpdateMessage": "Update Failed!", "Color": "red"})
  311. return
  312. }
  313. ctx.HTML(200, "upload_status", gin.H{"UpdateMessage": "Update Successful!", "Color": "green"})
  314. }
  315. /*
  316. Serving the new blogpost page. Serves the editor with the method to POST a new document
  317. */
  318. func (c *Controller) ServeNewBlogPage(ctx *gin.Context) {
  319. ctx.HTML(200, "blogpost_editor", gin.H{
  320. "navigation": gin.H{
  321. "menu": c.database.GetDropdownElements(),
  322. "headers": c.database.GetNavBarLinks(),
  323. },
  324. "Post": true,
  325. "Topics": storage.Topics,
  326. "Created": time.Now().UTC().String(),
  327. })
  328. }
  329. /*
  330. Serving the new blogpost page. Serves the editor with the method to POST a new document
  331. */
  332. func (c *Controller) ServeNewNavbarItem(ctx *gin.Context) {
  333. ctx.HTML(200, "navbar_editor", gin.H{
  334. "navigation": gin.H{
  335. "menu": c.database.GetDropdownElements(),
  336. "headers": c.database.GetNavBarLinks(),
  337. },
  338. "Post": true,
  339. })
  340. }
  341. /*
  342. Reciever for the ServeNewBlogPage UI screen. Adds a new document to the database
  343. */
  344. func (c *Controller) MakeBlogPost(ctx *gin.Context) {
  345. var doc storage.Document
  346. err := ctx.ShouldBind(&doc)
  347. if err != nil {
  348. ctx.HTML(500, "upload_status", gin.H{"UpdateMessage": "Update Failed!", "Color": "red"})
  349. return
  350. }
  351. _, err = c.database.AddDocument(doc)
  352. if err != nil {
  353. ctx.HTML(400, "upload_status", gin.H{"UpdateMessage": "Update Failed!", "Color": "red"})
  354. return
  355. }
  356. ctx.HTML(200, "upload_status", gin.H{"UpdateMessage": "Update Successful!", "Color": "green"})
  357. }
  358. /*
  359. Serves the HTML page for a new visual media post
  360. */
  361. func (c *Controller) ServeFileUpload(ctx *gin.Context) {
  362. ctx.HTML(200, "upload", gin.H{
  363. "navigation": gin.H{
  364. "menu": c.database.GetDropdownElements(),
  365. "headers": c.database.GetNavBarLinks(),
  366. },
  367. "Topics": storage.ImageCategories,
  368. "DefaultTopic": storage.DIGITAL_ART,
  369. })
  370. }
  371. /*
  372. Reciever for the page served to created a new visual media post
  373. */
  374. func (c *Controller) SaveFile(ctx *gin.Context) {
  375. var img storage.Image
  376. err := ctx.ShouldBind(&img)
  377. if err != nil {
  378. ctx.HTML(500, "upload_status", gin.H{"UpdateMessage": err, "Color": "red"})
  379. return
  380. }
  381. file, err := ctx.FormFile("file")
  382. if err != nil {
  383. ctx.HTML(500, "upload_status", gin.H{"UpdateMessage": err, "Color": "red"})
  384. return
  385. }
  386. fh, err := file.Open()
  387. if err != nil {
  388. ctx.HTML(500, "upload_status", gin.H{"UpdateMessage": err, "Color": "red"})
  389. return
  390. }
  391. fb := make([]byte, file.Size)
  392. var output bytes.Buffer
  393. for {
  394. n, err := fh.Read(fb)
  395. if err == io.EOF {
  396. break
  397. }
  398. if err != nil {
  399. log.Fatal(err)
  400. }
  401. output.Write(fb[:n])
  402. }
  403. _, err = c.database.AddImage(fb, img.Title, img.Desc, img.Category)
  404. if err != nil {
  405. ctx.HTML(500, "upload_status", gin.H{"UpdateMessage": err, "Color": "red"})
  406. return
  407. }
  408. ctx.HTML(200, "upload_status", gin.H{"UpdateMessage": "Update Successful!", "Color": "green"})
  409. }
  410. // Serve the document deletion template
  411. func (c *Controller) NavbarOptions(ctx *gin.Context) {
  412. id, found := ctx.Params.Get("id")
  413. if !found {
  414. ctx.HTML(400, "upload_status", gin.H{"UpdateMessage": "No ID selected!", "Color": "red"})
  415. return
  416. }
  417. ctx.HTML(200, "post_options", gin.H{
  418. "Link": fmt.Sprintf("/admin/navbar/%s", id),
  419. })
  420. }
  421. // Serve the document deletion template
  422. func (c *Controller) PostOptions(ctx *gin.Context) {
  423. id, found := ctx.Params.Get("id")
  424. if !found {
  425. ctx.HTML(400, "upload_status", gin.H{"UpdateMessage": "No ID selected!", "Color": "red"})
  426. return
  427. }
  428. ctx.HTML(200, "post_options", gin.H{
  429. "Link": fmt.Sprintf("/admin/posts/%s", id),
  430. })
  431. }
  432. /*
  433. Delete a document from the database
  434. */
  435. func (c *Controller) DeleteDocument(ctx *gin.Context) {
  436. id, found := ctx.Params.Get("id")
  437. if !found {
  438. ctx.HTML(500, "upload_status", gin.H{"UpdateMessage": "No ID passed!", "Color": "red"})
  439. return
  440. }
  441. err := c.database.DeleteDocument(storage.Identifier(id))
  442. if err != nil {
  443. ctx.HTML(500, "upload_status", gin.H{"UpdateMessage": "Delete Failed!", "Color": "red"})
  444. return
  445. }
  446. ctx.HTML(200, "upload_status", gin.H{"UpdateMessage": "Delete Successful!", "Color": "green"})
  447. }