storage.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780
  1. package storage
  2. import (
  3. "database/sql"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "log"
  8. "mime/multipart"
  9. "os"
  10. "path"
  11. "strings"
  12. "time"
  13. "git.aetherial.dev/aeth/keiji/pkg/env"
  14. "github.com/google/uuid"
  15. )
  16. const TECHNICAL = "technical"
  17. const CONFIGURATION = "configuration"
  18. const BLOG = "blog"
  19. const CREATIVE = "creative"
  20. const DIGITAL_ART = "digital_art"
  21. const MISC_ASSET = "misc_asset"
  22. const HOMEPAGE = "homepage"
  23. var Topics = []string{
  24. TECHNICAL,
  25. BLOG,
  26. CREATIVE,
  27. HOMEPAGE,
  28. }
  29. var ImageCategories = []string{
  30. MISC_ASSET,
  31. DIGITAL_ART,
  32. }
  33. type DatabaseSchema struct {
  34. // Gotta figure out what this looks like
  35. // so that the ExtractAll() function gets
  36. // all of the data from the database
  37. }
  38. type MenuElement struct {
  39. Png string `json:"png"`
  40. Category string `json:"category"`
  41. MenuLinks []LinkPair `json:"menu_links"`
  42. }
  43. type AdminPage struct {
  44. Tables map[string][]TableData `json:"tables"`
  45. }
  46. type TableData struct { // TODO: add this to the database io interface
  47. DisplayName string `json:"display_name"`
  48. Link string `json:"link"`
  49. }
  50. type LinkPair struct {
  51. Link string `json:"link"`
  52. Text string `json:"text"`
  53. }
  54. type NavBarItem struct {
  55. Png []byte `json:"png"`
  56. File *multipart.FileHeader `form:"file"`
  57. Link string `json:"link" form:"link"`
  58. Redirect string `json:"redirect" form:"redirect"`
  59. }
  60. type Asset struct {
  61. Name string
  62. Data []byte
  63. }
  64. type Identifier string
  65. type Document struct {
  66. Row int
  67. Ident Identifier `json:"id"`
  68. Title string `json:"title"`
  69. Created string `json:"created"`
  70. Body string `json:"body"`
  71. Category string `json:"category"`
  72. Sample string `json:"sample"`
  73. }
  74. /*
  75. Truncates a text post into a 256 character long 'sample' for displaying posts
  76. */
  77. func (d *Document) MakeSample() string {
  78. t := strings.Split(d.Body, "")
  79. var sample []string
  80. if len(d.Body) < 256 {
  81. return d.Body
  82. }
  83. for i := 0; i < 256; i++ {
  84. sample = append(sample, t[i])
  85. }
  86. sample = append(sample, " ...")
  87. return strings.Join(sample, "")
  88. }
  89. type Image struct {
  90. Ident Identifier `json:"identifier"`
  91. Location string
  92. Title string `json:"title" form:"title"`
  93. File *multipart.FileHeader `form:"file"`
  94. Desc string `json:"description" form:"description"`
  95. Created string
  96. Category string `json:"category" form:"category"`
  97. Data []byte
  98. }
  99. type DocumentIO interface {
  100. GetDocument(id Identifier) (Document, error)
  101. GetImage(id Identifier) (Image, error)
  102. GetAllImages() []Image
  103. GetImagesByCategory(category string) []Image
  104. UpdateDocument(doc Document) error
  105. DeleteDocument(id Identifier) error
  106. DeleteNavbarItem(id Identifier) error
  107. AddDocument(doc Document) (Identifier, error)
  108. AddImage(data []byte, title, desc, category string) (Identifier, error)
  109. AddAsset(name string, data []byte) error
  110. AddAdminTableEntry(TableData, string) error
  111. AddNavbarItem(NavBarItem) error
  112. AddMenuItem(LinkPair) error
  113. GetByCategory(category string) []Document
  114. AllDocuments() []Document
  115. GetDropdownElements() []LinkPair
  116. GetNavBarLinks() []NavBarItem
  117. GetAssets() []Asset
  118. GetAdminTables() AdminPage
  119. }
  120. var (
  121. ErrDuplicate = errors.New("record already exists")
  122. ErrNotExists = errors.New("row not exists")
  123. ErrUpdateFailed = errors.New("update failed")
  124. ErrDeleteFailed = errors.New("delete failed")
  125. )
  126. type SQLiteRepo struct {
  127. db *sql.DB
  128. imageIO ImageIO
  129. }
  130. type ImageIO interface {
  131. Put([]byte, Identifier) error
  132. Get(Identifier) ([]byte, error)
  133. }
  134. type FilesystemImageIO struct {
  135. RootDir string
  136. }
  137. /*
  138. create a new FilesystemImageIO struct
  139. */
  140. func MustNewFilesystemImageIO(path string) FilesystemImageIO {
  141. var filemode os.FileMode
  142. filemode = 0775
  143. err := os.MkdirAll(path, filemode)
  144. if err != nil {
  145. log.Fatalf("Error creating path: '%s' with permissions: '%d'. Error: %s\n", path, filemode, err.Error())
  146. }
  147. return FilesystemImageIO{RootDir: path}
  148. }
  149. /*
  150. Put a data blob on the filesystem
  151. :param b: the
  152. */
  153. func (f FilesystemImageIO) Put(b []byte, id Identifier) error {
  154. fh, err := os.OpenFile(path.Join(f.RootDir, string(id)), os.O_CREATE|os.O_RDWR, os.ModePerm)
  155. if err != nil {
  156. return err
  157. }
  158. defer fh.Close()
  159. _, err = fh.Write(b)
  160. if err != nil {
  161. return err
  162. }
  163. return nil
  164. }
  165. /*
  166. Get a data blob from the filesystem
  167. :param id: the identifier of the image to retrieve
  168. */
  169. func (f FilesystemImageIO) Get(id Identifier) ([]byte, error) {
  170. fh, err := os.Open(path.Join(f.RootDir, string(id)))
  171. if err != nil {
  172. return nil, err
  173. }
  174. b, err := io.ReadAll(fh)
  175. if err != nil {
  176. return nil, err
  177. }
  178. return b, nil
  179. }
  180. // Instantiate a new SQLiteRepo struct
  181. func NewSQLiteRepo(db *sql.DB, imgIo ImageIO) *SQLiteRepo {
  182. return &SQLiteRepo{
  183. db: db,
  184. imageIO: imgIo,
  185. }
  186. }
  187. // Creates a new SQL table for text posts
  188. func (r *SQLiteRepo) Migrate(seedQueries []string) error {
  189. for i := range seedQueries {
  190. _, err := r.db.Exec(seedQueries[i])
  191. if err != nil {
  192. return err
  193. }
  194. }
  195. return nil
  196. }
  197. /*
  198. Get all dropdown menu elements. Returns a list of LinkPair structs with the text and redirect location
  199. */
  200. func (s *SQLiteRepo) GetDropdownElements() []LinkPair {
  201. rows, err := s.db.Query("SELECT * FROM menu")
  202. var menuItems []LinkPair
  203. defer rows.Close()
  204. for rows.Next() {
  205. var id int
  206. var item LinkPair
  207. err = rows.Scan(&id, &item.Link, &item.Text)
  208. if err != nil {
  209. log.Fatal(err)
  210. }
  211. menuItems = append(menuItems, item)
  212. }
  213. return menuItems
  214. }
  215. /*
  216. Retrieve a dropdown element by its text name on the UI
  217. */
  218. func (s *SQLiteRepo) GetMenuItemByName(link, text string) (LinkPair, bool) {
  219. rows := s.db.QueryRow("SELECT * FROM menu WHERE link = ? AND text = ?", link, text)
  220. var item LinkPair
  221. var id int
  222. if err := rows.Scan(&id, &item.Link, &item.Text); err != nil {
  223. if errors.Is(err, sql.ErrNoRows) {
  224. return item, false
  225. }
  226. }
  227. log.Printf("%+v\n", item)
  228. return item, true
  229. }
  230. // get Admin table entry by its display name, link, and category.
  231. func (s *SQLiteRepo) GetAdminTableEntry(displayName, link, category string) (TableData, bool) {
  232. rows := s.db.QueryRow("SELECT * FROM admin WHERE display_name = ? AND link = ? AND category = ?", displayName, link, category)
  233. var item TableData
  234. var id int
  235. if err := rows.Scan(&id, &item.DisplayName, &item.Link, &category); err != nil {
  236. if errors.Is(err, sql.ErrNoRows) {
  237. return item, false
  238. }
  239. log.Fatal(err)
  240. }
  241. return item, true
  242. }
  243. // get navbar entry.
  244. func (s *SQLiteRepo) GetNavbarLink(link, redirect string) (NavBarItem, bool) {
  245. rows := s.db.QueryRow("SELECT * FROM navbar WHERE link = ? AND redirect = ?", link, redirect)
  246. var item NavBarItem
  247. var id int
  248. if err := rows.Scan(&id, &item.Png, &item.Link, &item.Redirect); err != nil {
  249. if errors.Is(err, sql.ErrNoRows) {
  250. return item, false
  251. }
  252. log.Fatal(err)
  253. }
  254. return item, true
  255. }
  256. // get an asset from the store
  257. func (s *SQLiteRepo) GetAsset(name string) (Asset, bool) {
  258. rows := s.db.QueryRow("SELECT * FROM assets WHERE name = ?", name)
  259. var item Asset
  260. var id int
  261. if err := rows.Scan(&id, &item.Name, &item.Data); err != nil {
  262. if errors.Is(err, sql.ErrNoRows) {
  263. return item, false
  264. }
  265. log.Fatal(err)
  266. }
  267. return item, true
  268. }
  269. /*
  270. Get all nav bar items. Returns a list of NavBarItem structs with the png data, the file name, and the redirect location of the icon
  271. /*
  272. Get all nav bar items. Returns a list of NavBarItem structs with the png data, the file name, and the redirect location of the icon
  273. */
  274. func (s *SQLiteRepo) GetNavBarLinks() []NavBarItem {
  275. rows, err := s.db.Query("SELECT * FROM navbar")
  276. var navbarItems []NavBarItem
  277. defer rows.Close()
  278. for rows.Next() {
  279. var item NavBarItem
  280. var id int
  281. err = rows.Scan(&id, &item.Png, &item.Link, &item.Redirect)
  282. if err != nil {
  283. log.Fatal(err)
  284. }
  285. navbarItems = append(navbarItems, item)
  286. }
  287. return navbarItems
  288. }
  289. /*
  290. get all assets from the asset table
  291. */
  292. func (s *SQLiteRepo) GetAssets() []Asset {
  293. rows, err := s.db.Query("SELECT * FROM assets")
  294. var assets []Asset
  295. defer rows.Close()
  296. for rows.Next() {
  297. var item Asset
  298. var id int
  299. err = rows.Scan(&id, &item.Name, &item.Data)
  300. if err != nil {
  301. log.Fatal(err)
  302. }
  303. assets = append(assets, item)
  304. }
  305. return assets
  306. }
  307. /*
  308. get all assets from the asset table
  309. */
  310. func (s *SQLiteRepo) GetAdminTables() AdminPage {
  311. rows, err := s.db.Query("SELECT * FROM admin")
  312. adminPage := AdminPage{Tables: map[string][]TableData{}}
  313. defer rows.Close()
  314. for rows.Next() {
  315. var item TableData
  316. var id int
  317. var category string
  318. err = rows.Scan(&id, &item.DisplayName, &item.Link, &category)
  319. if err != nil {
  320. log.Fatal(err)
  321. }
  322. adminPage.Tables[category] = append(adminPage.Tables[category], item)
  323. }
  324. return adminPage
  325. }
  326. /*
  327. Retrieve a document from the sqlite db
  328. :param id: the Identifier of the post
  329. */
  330. func (s *SQLiteRepo) GetDocument(id Identifier) (Document, error) {
  331. row := s.db.QueryRow("SELECT * FROM posts WHERE id = ?", id)
  332. var post Document
  333. var rowNum int
  334. if err := row.Scan(&rowNum, &post.Ident, &post.Title, &post.Created, &post.Body, &post.Category, &post.Sample); err != nil {
  335. if errors.Is(err, sql.ErrNoRows) {
  336. return post, ErrNotExists
  337. }
  338. return post, err
  339. }
  340. return post, nil
  341. }
  342. /*
  343. Get all documents by category
  344. :param category: the category to retrieve all docs from
  345. */
  346. func (s *SQLiteRepo) GetByCategory(category string) []Document {
  347. rows, err := s.db.Query("SELECT * FROM posts WHERE category = ?", category)
  348. if err != nil {
  349. log.Fatal(err)
  350. }
  351. var docs []Document
  352. defer rows.Close()
  353. for rows.Next() {
  354. var doc Document
  355. err := rows.Scan(&doc.Row, &doc.Ident, &doc.Title, &doc.Created, &doc.Body, &doc.Category, &doc.Sample)
  356. if err != nil {
  357. log.Fatal(err)
  358. }
  359. docs = append(docs, doc)
  360. }
  361. err = rows.Err()
  362. if err != nil {
  363. log.Fatal(err)
  364. }
  365. return docs
  366. }
  367. /*
  368. get image data from the images table
  369. :param id: the serial identifier of the post
  370. */
  371. func (s *SQLiteRepo) GetImage(id Identifier) (Image, error) {
  372. row := s.db.QueryRow("SELECT * FROM images WHERE id = ?", id)
  373. var rowNum int
  374. var title, desc, created, category string
  375. if err := row.Scan(&rowNum, &id, &title, &desc, &created, &category); err != nil {
  376. if errors.Is(err, sql.ErrNoRows) {
  377. return Image{}, ErrNotExists
  378. }
  379. return Image{}, err
  380. }
  381. data, err := s.imageIO.Get(id)
  382. if err != nil {
  383. return Image{}, err
  384. }
  385. return Image{Ident: id, Title: title, Desc: desc, Data: data, Created: created, Category: category}, nil
  386. }
  387. /*
  388. Get all of the images from the datastore
  389. */
  390. func (s *SQLiteRepo) GetAllImages() []Image {
  391. rows, err := s.db.Query("SELECT * FROM images")
  392. if err != nil {
  393. log.Fatal(err)
  394. }
  395. imgs := []Image{}
  396. for rows.Next() {
  397. var img Image
  398. var rowNum int
  399. err := rows.Scan(&rowNum, &img.Ident, &img.Title, &img.Desc, &img.Created, &img.Category)
  400. if err != nil {
  401. log.Fatal(err)
  402. }
  403. b, err := s.imageIO.Get(img.Ident)
  404. if err != nil {
  405. log.Fatal(err)
  406. }
  407. imgs = append(imgs, Image{Ident: img.Ident, Title: img.Title, Desc: img.Desc, Data: b, Created: img.Created, Category: img.Category})
  408. }
  409. err = rows.Err()
  410. if err != nil {
  411. log.Fatal(err)
  412. }
  413. return imgs
  414. }
  415. func (s *SQLiteRepo) GetImagesByCategory(category string) []Image {
  416. rows, err := s.db.Query("SELECT * FROM images WHERE category = ?", category)
  417. if err != nil {
  418. log.Fatal(err)
  419. }
  420. imgs := []Image{}
  421. for rows.Next() {
  422. var img Image
  423. var rowNum int
  424. err := rows.Scan(&rowNum, &img.Ident, &img.Title, &img.Desc, &img.Created, &img.Category)
  425. if err != nil {
  426. log.Fatal(err)
  427. }
  428. b, err := s.imageIO.Get(img.Ident)
  429. if err != nil {
  430. log.Fatal(err)
  431. }
  432. imgs = append(imgs, Image{Ident: img.Ident, Title: img.Title, Desc: img.Desc, Data: b, Created: img.Created, Category: img.Category})
  433. }
  434. err = rows.Err()
  435. if err != nil {
  436. log.Fatal(err)
  437. }
  438. return imgs
  439. }
  440. /*
  441. Add an image to the database
  442. :param title: the title of the image
  443. :param location: the location to save the image to
  444. :param desc: the description of the image, if any
  445. :param data: the binary data for the image
  446. */
  447. func (s *SQLiteRepo) AddImage(data []byte, title string, desc string, category string) (Identifier, error) {
  448. id := newIdentifier()
  449. err := s.imageIO.Put(data, id)
  450. if err != nil {
  451. return Identifier(""), err
  452. }
  453. _, err = s.db.Exec("INSERT INTO images (id, title, desc, created, category) VALUES (?,?,?,?,?)", string(id), title, desc, time.Now().String(), category)
  454. if err != nil {
  455. return Identifier(""), err
  456. }
  457. return id, nil
  458. }
  459. /*
  460. Updates a document in the database with the supplied. Only changes the title, the body, category. Keys off of the documents Identifier
  461. :param doc: the Document to upload into the database
  462. */
  463. func (s *SQLiteRepo) UpdateDocument(doc Document) error {
  464. tx, err := s.db.Begin()
  465. if err != nil {
  466. return err
  467. }
  468. stmt, err := tx.Prepare("UPDATE posts SET title = ?, body = ?, category = ?, sample = ? WHERE id = ?;")
  469. if err != nil {
  470. tx.Rollback()
  471. return err
  472. }
  473. res, err := stmt.Exec(doc.Title, doc.Body, doc.Category, doc.MakeSample(), doc.Ident)
  474. if err != nil {
  475. tx.Rollback()
  476. return err
  477. }
  478. affected, _ := res.RowsAffected()
  479. if affected != 1 {
  480. return ErrNotExists
  481. }
  482. tx.Commit()
  483. return nil
  484. }
  485. /*
  486. Adds a LinkPair to the menu database table
  487. :param item: the LinkPair to upload
  488. */
  489. func (s *SQLiteRepo) AddMenuItem(item LinkPair) error {
  490. tx, err := s.db.Begin()
  491. if err != nil {
  492. return err
  493. }
  494. _, found := s.GetMenuItemByName(item.Link, item.Text)
  495. if found {
  496. tx.Rollback()
  497. return ErrDuplicate
  498. }
  499. stmt, _ := tx.Prepare("INSERT INTO menu(link, text) VALUES (?,?)")
  500. _, err = stmt.Exec(item.Link, item.Text)
  501. if err != nil {
  502. tx.Rollback()
  503. return err
  504. }
  505. tx.Commit()
  506. return nil
  507. }
  508. /*
  509. Adds an item to the navbar database table
  510. :param item: the NavBarItem to upload
  511. */
  512. func (s *SQLiteRepo) AddNavbarItem(item NavBarItem) error {
  513. tx, err := s.db.Begin()
  514. if err != nil {
  515. return err
  516. }
  517. _, found := s.GetNavbarLink(item.Link, item.Redirect)
  518. if found {
  519. tx.Rollback()
  520. return ErrDuplicate
  521. }
  522. stmt, err := tx.Prepare("INSERT INTO navbar(png, link, redirect) VALUES (?,?,?)")
  523. if err != nil {
  524. tx.Rollback()
  525. return err
  526. }
  527. _, err = stmt.Exec(item.Png, item.Link, item.Redirect)
  528. if err != nil {
  529. tx.Rollback()
  530. return err
  531. }
  532. tx.Commit()
  533. return nil
  534. }
  535. /*
  536. Adds an asset to the asset database table asset
  537. :param name: the name of the asset (filename)
  538. :param data: the byte array of the PNG to upload TODO: limit this to 256kb
  539. */
  540. func (s *SQLiteRepo) AddAsset(name string, data []byte) error {
  541. tx, err := s.db.Begin()
  542. if err != nil {
  543. return err
  544. }
  545. _, found := s.GetAsset(name)
  546. if found {
  547. tx.Rollback()
  548. return ErrDuplicate
  549. }
  550. stmt, _ := tx.Prepare("INSERT INTO assets(name, data) VALUES (?,?)")
  551. _, err = stmt.Exec(name, data)
  552. if err != nil {
  553. tx.Rollback()
  554. return err
  555. }
  556. tx.Commit()
  557. return nil
  558. }
  559. /*
  560. Adds a document to the database (for text posts)
  561. :param doc: the Document to add
  562. */
  563. func (s *SQLiteRepo) AddDocument(doc Document) (Identifier, error) {
  564. id := newIdentifier()
  565. tx, err := s.db.Begin()
  566. if err != nil {
  567. return Identifier(""), err
  568. }
  569. stmt, _ := tx.Prepare("INSERT INTO posts(id, title, created, body, category, sample) VALUES (?,?,?,?,?,?)")
  570. _, err = stmt.Exec(id, doc.Title, doc.Created, doc.Body, doc.Category, doc.MakeSample())
  571. if err != nil {
  572. tx.Rollback()
  573. return Identifier(""), err
  574. }
  575. tx.Commit()
  576. return id, nil
  577. }
  578. /*
  579. Add an entry to the 'admin' table in the database
  580. :param item: an admin table k/v text to redirect pair
  581. :param tableName: the name of the table to populate the link in on the UI
  582. */
  583. func (s *SQLiteRepo) AddAdminTableEntry(item TableData, category string) error {
  584. tx, err := s.db.Begin()
  585. if err != nil {
  586. return err
  587. }
  588. _, found := s.GetAdminTableEntry(item.DisplayName, item.Link, category)
  589. if found {
  590. tx.Rollback()
  591. return ErrDuplicate
  592. }
  593. stmt, _ := tx.Prepare("INSERT INTO admin (display_name, link, category) VALUES (?,?,?)")
  594. _, err = stmt.Exec(item.DisplayName, item.Link, category)
  595. if err != nil {
  596. tx.Rollback()
  597. return err
  598. }
  599. tx.Commit()
  600. return nil
  601. }
  602. /*
  603. Delete a document from the db
  604. :param id: the identifier of the document to remove
  605. */
  606. func (s *SQLiteRepo) DeleteDocument(id Identifier) error {
  607. tx, err := s.db.Begin()
  608. if err != nil {
  609. return err
  610. }
  611. stmt, _ := tx.Prepare("DELETE FROM posts WHERE id=?")
  612. _, err = stmt.Exec(id)
  613. if err != nil {
  614. tx.Rollback()
  615. return err
  616. }
  617. tx.Commit()
  618. return nil
  619. }
  620. /*
  621. Delete navigation bar item
  622. :param id: the name or 'id' of the navbar item to remove
  623. */
  624. func (s *SQLiteRepo) DeleteNavbarItem(id Identifier) error {
  625. tx, err := s.db.Begin()
  626. if err != nil {
  627. return err
  628. }
  629. stmt, _ := tx.Prepare("DELETE FROM navbar WHERE link=?")
  630. result, err := stmt.Exec(id)
  631. if err != nil {
  632. tx.Rollback()
  633. return err
  634. }
  635. rowsAffected, _ := result.RowsAffected()
  636. if rowsAffected < 1 {
  637. return ErrNotExists
  638. }
  639. tx.Commit()
  640. return nil
  641. }
  642. // Get all Hosts from the host table
  643. func (s *SQLiteRepo) AllDocuments() []Document {
  644. rows, err := s.db.Query("SELECT * FROM posts")
  645. if err != nil {
  646. fmt.Printf("There was an issue getting all posts. %s", err.Error())
  647. return nil
  648. }
  649. defer rows.Close()
  650. all := []Document{}
  651. for rows.Next() {
  652. var post Document
  653. if err := rows.Scan(&post.Row, &post.Ident, &post.Title, &post.Created, &post.Body, &post.Category, &post.Sample); err != nil {
  654. fmt.Printf("There was an error getting all documents. %s", err.Error())
  655. return nil
  656. }
  657. all = append(all, post)
  658. }
  659. return all
  660. }
  661. type InvalidSkipArg struct{ Skip int }
  662. func (i *InvalidSkipArg) Error() string {
  663. return fmt.Sprintf("Invalid skip amount was passed: %v", i.Skip)
  664. }
  665. type ImageStoreItem struct {
  666. Identifier string `json:"identifier"`
  667. Filename string `json:"filename"`
  668. AbsolutePath string `json:"absolute_path"`
  669. Title string `json:"title" form:"title"`
  670. Created string `json:"created"`
  671. Desc string `json:"description" form:"description"`
  672. Category string `json:"category"`
  673. ApiPath string
  674. }
  675. /*
  676. Function to return the location of the image store. Wrapping the env call in
  677. a function so that refactoring is easier
  678. */
  679. func GetImageStore() string {
  680. return os.Getenv(env.IMAGE_STORE)
  681. }
  682. // Wrapping the new id call in a function to make refactoring easier
  683. func newIdentifier() Identifier {
  684. return Identifier(uuid.NewString())
  685. }