Browse Source

trying to work some structure into this chaos

aeth 1 month ago
parent
commit
1962a98d76
4 changed files with 221 additions and 42 deletions
  1. 1 0
      go.mod
  2. 2 0
      go.sum
  3. 1 24
      pkg/helpers/helpers.go
  4. 217 18
      pkg/helpers/storage.go

+ 1 - 0
go.mod

@@ -32,6 +32,7 @@ require (
 	github.com/leodido/go-urn v1.2.4 // indirect
 	github.com/mailru/easyjson v0.7.6 // indirect
 	github.com/mattn/go-isatty v0.0.20 // indirect
+	github.com/mattn/go-sqlite3 v1.14.22 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/patrickmn/go-cache v2.1.0+incompatible // indirect

+ 2 - 0
go.sum

@@ -87,6 +87,8 @@ github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ
 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
+github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=

+ 1 - 24
pkg/helpers/helpers.go

@@ -3,7 +3,6 @@ package helpers
 import (
 	"encoding/json"
 
-	"strings"
 	"time"
 
 	"github.com/gomarkdown/markdown"
@@ -54,12 +53,7 @@ type MenuElement struct {
 	MenuLinks []MenuLinkPair `json:"menu_links"`
 }
 
-type MenuLinkPair struct {
-	MenuLink string `json:"menu_link"`
-	LinkText string `json:"link_text"`
-}
-
-type Document struct {
+type DocumentOld struct {
 	Ident    string `json:"identifier"`
 	Created  string `json:"created"`
 	Body     string `json:"body"`
@@ -183,23 +177,6 @@ func AddHeaders(h HeaderCollection, redisCfg RedisConf) error {
 	return nil
 }
 
-/*
-Truncates a text post into a 256 character long 'sample' for displaying posts
-*/
-func (d *Document) MakeSample() string {
-	t := strings.Split(d.Body, "")
-	var sample []string
-	if len(d.Body) < 256 {
-		return d.Body
-	}
-	for i := 0; i < 256; i++ {
-		sample = append(sample, t[i])
-	}
-	sample = append(sample, " ...")
-	return strings.Join(sample, "")
-
-}
-
 /*
 Retrieve all documents from the category specified in the argument category
 

+ 217 - 18
pkg/helpers/storage.go

@@ -1,36 +1,236 @@
 package helpers
 
 import (
+	"database/sql"
 	"encoding/json"
+	"errors"
 	"fmt"
 	"os"
+	"strings"
 	"time"
 
 	"git.aetherial.dev/aeth/keiji/pkg/env"
 	"github.com/google/uuid"
+	"github.com/mattn/go-sqlite3"
 	"github.com/redis/go-redis/v9"
 )
 
-type InvalidSkipArg struct {Skip int}
+type DatabaseSchema struct {
+	// Gotta figure out what this looks like
+	// so that the ExtractAll() function gets
+	// all of the data from the database
+
+}
+
+type MenuLinkPair struct {
+	MenuLink string `json:"menu_link"`
+	LinkText string `json:"link_text"`
+}
+
+type NavBarItem struct {
+	Png  string `json:"png"`
+	Link string `json:"link"`
+}
+
+type Document struct {
+	PostId   string
+	Title    string `json:"title"`
+	Created  string `json:"created"`
+	Body     string `json:"body"`
+	Category string `json:"category"`
+	Sample   string
+}
+
+/*
+Truncates a text post into a 256 character long 'sample' for displaying posts
+*/
+func (d *Document) MakeSample() string {
+	t := strings.Split(d.Body, "")
+	var sample []string
+	if len(d.Body) < 256 {
+		return d.Body
+	}
+	for i := 0; i < 256; i++ {
+		sample = append(sample, t[i])
+	}
+	sample = append(sample, " ...")
+	return strings.Join(sample, "")
+
+}
+
+type Image struct {
+	Location string
+	Title    string
+	Desc     string
+}
+
+type DocumentIO interface {
+	GetDocument(id string) (Document, error)
+	GetImage(id string) (Image, error)
+	UpdateDocument(doc Document) error
+	DeleteDocument(id string) error
+	AddDocument(doc Document) error
+	AddImage(img ImageStoreItem) error
+	GetByCategory(category string) []string
+	AllDocuments() []Document
+	GetDropdownElements() []MenuLinkPair
+	GetNavBarLinks() []NavBarItem
+	ExportAll()
+}
+
+var (
+	ErrDuplicate    = errors.New("record already exists")
+	ErrNotExists    = errors.New("row not exists")
+	ErrUpdateFailed = errors.New("update failed")
+	ErrDeleteFailed = errors.New("delete failed")
+)
+
+type SQLiteRepo struct {
+	db *sql.DB
+}
+
+// Instantiate a new SQLiteRepo struct
+func NewSQLiteRepo(db *sql.DB) *SQLiteRepo {
+	return &SQLiteRepo{
+		db: db,
+	}
+
+}
+
+// Creates a new SQL table for text posts
+func (r *SQLiteRepo) Migrate() error {
+	query := `
+    CREATE TABLE IF NOT EXISTS posts(
+        id INTEGER PRIMARY KEY AUTOINCREMENT,
+		postid TEXT NOT NULL,
+		title TEXT NOT NULL,
+        created TEXT NOT NULL,
+        body TEXT NOT NULL UNIQUE,
+        category TEXT NOT NULL,
+		sample TEXT NOT NULL
+    );
+    `
+
+	_, err := r.db.Exec(query)
+	return err
+}
+
+/*
+Create an entry in the hosts table
+
+	:param host: a Host entry from a port scan
+*/
+func (r *SQLiteRepo) Create(post Document) error {
+	_, err := r.db.Exec("INSERT INTO posts(postid, title, created, body, category, sample) values(?,?,?,?,?)", uuid.New().String(), post.Title, post.Created, post.Body, post.Category, post.MakeSample())
+	if err != nil {
+		var sqliteErr sqlite3.Error
+		if errors.As(err, &sqliteErr) {
+			if errors.Is(sqliteErr.ExtendedCode, sqlite3.ErrConstraintUnique) {
+				return ErrDuplicate
+			}
+		}
+		return err
+	}
+
+	return nil
+}
+
+// Get all Hosts from the host table
+func (r *SQLiteRepo) AllDocuments() []Document {
+	rows, err := r.db.Query("SELECT * FROM posts")
+	if err != nil {
+		fmt.Printf("There was an issue getting all posts. %s", err.Error())
+		return nil
+	}
+	defer rows.Close()
+
+	var all []Document
+	for rows.Next() {
+		var post Document
+		if err := rows.Scan(&post.PostId, &post.Title, &post.Created, &post.Body, &post.Sample); err != nil {
+			fmt.Printf("There was an error getting all documents. %s", err.Error())
+			return nil
+		}
+		all = append(all, post)
+	}
+	return all
+}
+
+// Get a blogpost by its postid
+func (r *SQLiteRepo) GetByIP(postId string) (Document, error) {
+	row := r.db.QueryRow("SELECT * FROM posts WHERE postid = ?", postId)
+
+	var post Document
+	if err := row.Scan(&post); err != nil {
+		if errors.Is(err, sql.ErrNoRows) {
+			return post, ErrNotExists
+		}
+		return post, err
+	}
+	return post, nil
+}
+
+// Update a record by its ID
+func (r *SQLiteRepo) Update(id int64, updated Document) error {
+	if id == 0 {
+		return errors.New("invalid updated ID")
+	}
+	res, err := r.db.Exec("UPDATE posts SET title = ?, body = ?, desc = ? WHERE id = ?", updated.Title, updated.Body, updated.MakeSample(), id)
+	if err != nil {
+		return err
+	}
+
+	rowsAffected, err := res.RowsAffected()
+	if err != nil {
+		return err
+	}
+
+	if rowsAffected == 0 {
+		return ErrUpdateFailed
+	}
+
+	return nil
+}
+
+// Delete a record by its ID
+func (r *SQLiteRepo) Delete(id int64) error {
+	res, err := r.db.Exec("DELETE FROM posts WHERE id = ?", id)
+	if err != nil {
+		return err
+	}
+
+	rowsAffected, err := res.RowsAffected()
+	if err != nil {
+		return err
+	}
+
+	if rowsAffected == 0 {
+		return ErrDeleteFailed
+	}
+
+	return err
+}
+
+type InvalidSkipArg struct{ Skip int }
 
 func (i *InvalidSkipArg) Error() string {
 	return fmt.Sprintf("Invalid skip amount was passed: %v", i.Skip)
 }
 
-
 type ImageStoreItem struct {
-	Identifier		string	`json:"identifier"`
-	Filename		string	`json:"filename"`
-	AbsolutePath	string	`json:"absolute_path"`
-	Title			string	`json:"title" form:"title"`
-	Created			string	`json:"created"`
-	Desc			string	`json:"description" form:"description"`
-	Category		string	`json:"category"`
-	ApiPath			string
+	Identifier   string `json:"identifier"`
+	Filename     string `json:"filename"`
+	AbsolutePath string `json:"absolute_path"`
+	Title        string `json:"title" form:"title"`
+	Created      string `json:"created"`
+	Desc         string `json:"description" form:"description"`
+	Category     string `json:"category"`
+	ApiPath      string
 }
 
 /*
 Create a new ImageStoreItem
+
 	:param fname: the name of the file to be saved
 	:param title: the canonical title to give the image
 	:param desc: the description to associate to the image
@@ -38,18 +238,17 @@ Create a new ImageStoreItem
 func NewImageStoreItem(fname string, title string, desc string) *ImageStoreItem {
 	id := uuid.New()
 	img := ImageStoreItem{
-		Identifier: id.String(),
-		Filename: fname,
-		Title: title,
-		Category: DIGITAL_ART,
+		Identifier:   id.String(),
+		Filename:     fname,
+		Title:        title,
+		Category:     DIGITAL_ART,
 		AbsolutePath: fmt.Sprintf("%s/%s", GetImageStore(), fname),
-		Created: time.Now().UTC().String(),
-		Desc: desc,
+		Created:      time.Now().UTC().String(),
+		Desc:         desc,
 	}
 	return &img
 }
 
-
 /*
 Function to return the location of the image store. Wrapping the env call in
 a function so that refactoring is easier
@@ -60,6 +259,7 @@ func GetImageStore() string {
 
 /*
 Return database entries of the images that exist in the imagestore
+
 	:param rds: pointer to a RedisCaller to perform the lookups with
 */
 func GetImageData(rds *RedisCaller) ([]*ImageStoreItem, error) {
@@ -87,4 +287,3 @@ func GetImageData(rds *RedisCaller) ([]*ImageStoreItem, error) {
 	}
 	return imageEntries, err
 }
-