admin_handlers.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  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. ctx.HTML(200, "blogpost_editor", gin.H{
  280. "navigation": gin.H{
  281. "menu": c.database.GetDropdownElements(),
  282. "headers": c.database.GetNavBarLinks(),
  283. },
  284. "Ident": doc.Ident,
  285. "Topics": storage.Topics,
  286. "Title": doc.Title,
  287. "DefaultTopic": doc.Category,
  288. "Created": doc.Created,
  289. "Body": doc.Body,
  290. })
  291. }
  292. /*
  293. Update an existing blog post
  294. */
  295. func (c *Controller) UpdateBlogPost(ctx *gin.Context) {
  296. var doc storage.Document
  297. err := ctx.ShouldBind(&doc)
  298. if err != nil {
  299. ctx.HTML(500, "upload_status", gin.H{"UpdateMessage": "Update Failed!", "Color": "red"})
  300. return
  301. }
  302. err = c.database.UpdateDocument(doc)
  303. if err != nil {
  304. ctx.HTML(400, "upload_status", gin.H{"UpdateMessage": "Update Failed!", "Color": "red"})
  305. return
  306. }
  307. ctx.HTML(200, "upload_status", gin.H{"UpdateMessage": "Update Successful!", "Color": "green"})
  308. }
  309. /*
  310. Serving the new blogpost page. Serves the editor with the method to POST a new document
  311. */
  312. func (c *Controller) ServeNewBlogPage(ctx *gin.Context) {
  313. ctx.HTML(200, "blogpost_editor", gin.H{
  314. "navigation": gin.H{
  315. "menu": c.database.GetDropdownElements(),
  316. "headers": c.database.GetNavBarLinks(),
  317. },
  318. "Post": true,
  319. "Topics": storage.Topics,
  320. "Created": time.Now().UTC().String(),
  321. })
  322. }
  323. /*
  324. Serving the new blogpost page. Serves the editor with the method to POST a new document
  325. */
  326. func (c *Controller) ServeNewNavbarItem(ctx *gin.Context) {
  327. ctx.HTML(200, "navbar_editor", gin.H{
  328. "navigation": gin.H{
  329. "menu": c.database.GetDropdownElements(),
  330. "headers": c.database.GetNavBarLinks(),
  331. },
  332. "Post": true,
  333. })
  334. }
  335. /*
  336. Reciever for the ServeNewBlogPage UI screen. Adds a new document to the database
  337. */
  338. func (c *Controller) MakeBlogPost(ctx *gin.Context) {
  339. var doc storage.Document
  340. err := ctx.ShouldBind(&doc)
  341. if err != nil {
  342. ctx.HTML(500, "upload_status", gin.H{"UpdateMessage": "Update Failed!", "Color": "red"})
  343. return
  344. }
  345. _, err = c.database.AddDocument(doc)
  346. if err != nil {
  347. ctx.HTML(400, "upload_status", gin.H{"UpdateMessage": "Update Failed!", "Color": "red"})
  348. return
  349. }
  350. ctx.HTML(200, "upload_status", gin.H{"UpdateMessage": "Update Successful!", "Color": "green"})
  351. }
  352. /*
  353. Serves the HTML page for a new visual media post
  354. */
  355. func (c *Controller) ServeFileUpload(ctx *gin.Context) {
  356. ctx.HTML(200, "upload", gin.H{
  357. "navigation": gin.H{
  358. "menu": c.database.GetDropdownElements(),
  359. "headers": c.database.GetNavBarLinks(),
  360. },
  361. })
  362. }
  363. /*
  364. Reciever for the page served to created a new visual media post
  365. */
  366. func (c *Controller) SaveFile(ctx *gin.Context) {
  367. var img storage.Image
  368. err := ctx.ShouldBind(&img)
  369. if err != nil {
  370. ctx.HTML(500, "upload_status", gin.H{"UpdateMessage": err, "Color": "red"})
  371. return
  372. }
  373. file, err := ctx.FormFile("file")
  374. if err != nil {
  375. ctx.HTML(500, "upload_status", gin.H{"UpdateMessage": err, "Color": "red"})
  376. return
  377. }
  378. fh, err := file.Open()
  379. if err != nil {
  380. ctx.HTML(500, "upload_status", gin.H{"UpdateMessage": err, "Color": "red"})
  381. return
  382. }
  383. fb := make([]byte, file.Size)
  384. var output bytes.Buffer
  385. for {
  386. n, err := fh.Read(fb)
  387. if err == io.EOF {
  388. break
  389. }
  390. if err != nil {
  391. log.Fatal(err)
  392. }
  393. output.Write(fb[:n])
  394. }
  395. _, err = c.database.AddImage(fb, img.Title, img.Desc)
  396. if err != nil {
  397. ctx.HTML(500, "upload_status", gin.H{"UpdateMessage": err, "Color": "red"})
  398. return
  399. }
  400. ctx.HTML(200, "upload_status", gin.H{"UpdateMessage": "Update Successful!", "Color": "green"})
  401. }
  402. // Serve the document deletion template
  403. func (c *Controller) NavbarOptions(ctx *gin.Context) {
  404. id, found := ctx.Params.Get("id")
  405. if !found {
  406. ctx.HTML(400, "upload_status", gin.H{"UpdateMessage": "No ID selected!", "Color": "red"})
  407. return
  408. }
  409. ctx.HTML(200, "post_options", gin.H{
  410. "Link": fmt.Sprintf("/admin/navbar/%s", id),
  411. })
  412. }
  413. // Serve the document deletion template
  414. func (c *Controller) PostOptions(ctx *gin.Context) {
  415. id, found := ctx.Params.Get("id")
  416. if !found {
  417. ctx.HTML(400, "upload_status", gin.H{"UpdateMessage": "No ID selected!", "Color": "red"})
  418. return
  419. }
  420. ctx.HTML(200, "post_options", gin.H{
  421. "Link": fmt.Sprintf("/admin/posts/%s", id),
  422. })
  423. }
  424. /*
  425. Delete a document from the database
  426. */
  427. func (c *Controller) DeleteDocument(ctx *gin.Context) {
  428. id, found := ctx.Params.Get("id")
  429. if !found {
  430. ctx.HTML(500, "upload_status", gin.H{"UpdateMessage": "No ID passed!", "Color": "red"})
  431. return
  432. }
  433. err := c.database.DeleteDocument(storage.Identifier(id))
  434. if err != nil {
  435. ctx.HTML(500, "upload_status", gin.H{"UpdateMessage": "Delete Failed!", "Color": "red"})
  436. return
  437. }
  438. ctx.HTML(200, "upload_status", gin.H{"UpdateMessage": "Delete Successful!", "Color": "green"})
  439. }