Selaa lähdekoodia

fixed auth and added a brief homepage

aeth 2 kuukautta sitten
vanhempi
sitoutus
9e8d5036c8

+ 5 - 2
Makefile

@@ -1,8 +1,8 @@
 .PHONY: build format docs
 
 
-WEBSERVER = webserver
-SEED_CMD = seed
+WEBSERVER = keiji
+SEED_CMD = keiji-ctl
 SWAG := $(shell command -v swag 2> /dev/null)
 ## Have to set the WEB_ROOT and DOMAIN_NAME environment variables when building
 build:
@@ -10,6 +10,9 @@ build:
 	-o ./build/linux/$(WEBSERVER)/$(WEBSERVER) ./cmd/$(WEBSERVER)/$(WEBSERVER).go && \
 	go build -o ./build/linux/$(SEED_CMD)/$(SEED_CMD) ./cmd/$(SEED_CMD)/$(SEED_CMD).go
 
+install:
+	sudo cp ./build/linux/$(SEED_CMD)/$(SEED_CMD) /usr/local/bin/
+
 format:
 	go fmt ./...
 

BIN
assets/git.png


BIN
assets/github.png


BIN
assets/linkedin.png


BIN
assets/menu.png


BIN
assets/soundcloud.png


BIN
assets/twitter.png


BIN
build/linux/keiji-ctl/keiji-ctl


BIN
build/linux/keiji/keiji


+ 63 - 11
cmd/seed/seed.go → cmd/keiji-ctl/keiji-ctl.go

@@ -8,18 +8,55 @@ import (
 	"io"
 	"log"
 	"net/http"
+	"net/url"
 	"os"
 	"path"
 
+	"git.aetherial.dev/aeth/keiji/pkg/controller"
 	"git.aetherial.dev/aeth/keiji/pkg/helpers"
 	_ "github.com/mattn/go-sqlite3"
 )
 
-const DEFAULT_URL = "http://horus-ctn01.void:10277"
-
 // authenticate and get the cookie needed to make updates
-func auth() string {
-	return ""
+func auth(url, username, password string) *http.Cookie {
+	client := http.Client{}
+	b, _ := json.Marshal(helpers.Credentials{Username: username, Password: password})
+	req, _ := http.NewRequest(http.MethodPost, url, bytes.NewReader(b))
+	req.Header.Add("Content-Type", "application/json")
+	resp, err := client.Do(req)
+	if err != nil {
+		log.Fatal("auth failed: ", err)
+	}
+	defer resp.Body.Close()
+	if resp.StatusCode > 200 {
+		msg, _ := io.ReadAll(resp.Body)
+		log.Fatal("Invalid credentials or server error: ", string(msg), "\n Status code: ", resp.StatusCode)
+	}
+	cookies := resp.Cookies()
+	for i := range cookies {
+		if cookies[i].Name == controller.AUTH_COOKIE_NAME {
+			return cookies[i]
+		}
+	}
+	log.Fatal("Auth cookie not found.")
+	return nil
+}
+
+// prepare the auth cookie
+func prepareCookie(address string) *http.Cookie {
+
+	parsedAddr, err := url.Parse(address)
+	dn := parsedAddr.Hostname()
+	if err != nil {
+		log.Fatal("unparseable address: ", address, " error: ", err)
+	}
+	var preparedCookie *http.Cookie
+	if cookie == "" {
+		log.Fatal("Cookie cannot be empty.")
+	} else {
+		preparedCookie = &http.Cookie{Value: cookie, Name: controller.AUTH_COOKIE_NAME, Domain: dn}
+	}
+	return preparedCookie
 }
 
 var pngFile string
@@ -27,6 +64,8 @@ var redirect string
 var text string
 var col string
 var cmd string
+var address string
+var cookie string
 
 func main() {
 
@@ -34,11 +73,18 @@ func main() {
 	flag.StringVar(&redirect, "redirect", "", "the website that the navbar will redirect to")
 	flag.StringVar(&text, "text", "", "the text to display on the menu item")
 	flag.StringVar(&col, "col", "", "the column to add/populate the admin table item under")
-	flag.StringVar(&cmd, "cmd", "", "the 'command' for the seed program to use, currently supports options 'admin', 'menu', and 'png")
+	flag.StringVar(&cmd, "cmd", "", "the 'command' for the seed program to use, currently supports options 'admin', 'menu', and 'asset', 'nav'")
+	flag.StringVar(&address, "address", "https://aetherial.dev", "override the url to contact.")
+	flag.StringVar(&cookie, "cookie", "", "pass a cookie to bypass direct authentication")
 	flag.Parse()
 
 	client := http.Client{}
+
 	switch cmd {
+	case "auth":
+		cookie := auth(fmt.Sprintf("%s/login", address), os.Getenv("KEIJI_USERNAME"), os.Getenv("KEIJI_PASSWORD"))
+		fmt.Println(cookie.Value)
+
 	case "asset":
 		b, err := os.ReadFile(pngFile)
 		if err != nil {
@@ -50,7 +96,8 @@ func main() {
 			Data: b,
 		}
 		data, _ := json.Marshal(item)
-		req, _ := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/admin/asset", DEFAULT_URL), bytes.NewReader(data))
+		req, _ := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/admin/asset", address), bytes.NewReader(data))
+		req.AddCookie(prepareCookie(address))
 		req.Header.Add("Content-Type", "application/json")
 		resp, err := client.Do(req)
 		if err != nil {
@@ -66,7 +113,7 @@ func main() {
 		fmt.Println("navigation bar item upload successfully.")
 		os.Exit(0)
 
-	case "png":
+	case "nav":
 		fmt.Println(string(pngFile))
 		b, err := os.ReadFile(pngFile)
 		if err != nil {
@@ -80,7 +127,8 @@ func main() {
 			Png:      b,
 		}
 		data, _ := json.Marshal(item)
-		req, _ := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/admin/navbar", DEFAULT_URL), bytes.NewReader(data))
+		req, _ := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/admin/navbar", address), bytes.NewReader(data))
+		req.AddCookie(prepareCookie(address))
 		req.Header.Add("Content-Type", "application/json")
 		resp, err := client.Do(req)
 		if err != nil {
@@ -93,11 +141,12 @@ func main() {
 			fmt.Println("There was an error performing the desired request: ", string(b))
 			os.Exit(2)
 		}
-		fmt.Println("navigation bar item upload successfully.")
+		fmt.Println("png item upload successfully.")
 		os.Exit(0)
 	case "menu":
 		b, _ := json.Marshal(helpers.MenuLinkPair{LinkText: text, MenuLink: redirect})
-		req, _ := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/admin/menu", DEFAULT_URL), bytes.NewReader(b))
+		req, _ := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/admin/menu", address), bytes.NewReader(b))
+		req.AddCookie(prepareCookie(address))
 		req.Header.Add("Content-Type", "application/json")
 		resp, err := client.Do(req)
 		if err != nil {
@@ -117,7 +166,8 @@ func main() {
 		adminPage := helpers.AdminPage{Tables: tables}
 		adminPage.Tables[col] = append(adminPage.Tables[col], helpers.TableData{Link: redirect, DisplayName: text})
 		b, _ := json.Marshal(adminPage)
-		req, _ := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/admin/panel", DEFAULT_URL), bytes.NewReader(b))
+		req, _ := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/admin/panel", address), bytes.NewReader(b))
+		req.AddCookie(prepareCookie(address))
 		req.Header.Add("Content-Type", "application/json")
 		resp, err := client.Do(req)
 		if err != nil {
@@ -130,6 +180,8 @@ func main() {
 			fmt.Println("There was an error performing the desired request: ", string(b))
 			os.Exit(4)
 		}
+		fmt.Println("admin item added successfully.")
+		os.Exit(0)
 	}
 
 }

+ 1 - 3
cmd/webserver/webserver.go → cmd/keiji/keiji.go

@@ -31,8 +31,6 @@ func main() {
 	if err != nil {
 		log.Fatal("Error when loading env file: ", err)
 	}
-	REDIS_PORT := os.Getenv("REDIS_PORT")
-	REDIS_ADDR := os.Getenv("REDIS_ADDR")
 	var srcOpt webpages.ServiceOption
 	var htmlReader fs.FS
 	if *embedPtr == true {
@@ -85,7 +83,7 @@ func main() {
 	if err != nil {
 		log.Fatal(err)
 	}
-	routes.Register(e, DOMAIN_NAME, REDIS_PORT, REDIS_ADDR, webserverDb, htmlReader)
+	routes.Register(e, DOMAIN_NAME, webserverDb, htmlReader)
 	if os.Getenv("SSL_MODE") == "ON" {
 		e.RunTLS(fmt.Sprintf("%s:%s", os.Getenv("HOST_ADDR"), os.Getenv("HOST_PORT")),
 			os.Getenv(env.CHAIN), os.Getenv(env.KEY))

+ 6 - 10
pkg/controller/controller.go

@@ -7,19 +7,15 @@ import (
 )
 
 type Controller struct {
-	Domain      string
-	database    helpers.DocumentIO
-	RedisConfig helpers.RedisConf
-	Cache       *helpers.AllCache
-	FileIO      fs.FS
+	Domain   string
+	database helpers.DocumentIO
+	Cache    *helpers.AuthCache
+	FileIO   fs.FS
 }
 
-func NewController(domain string, redisPort string, redisAddr string, database helpers.DocumentIO, files fs.FS) *Controller {
+func NewController(domain string, database helpers.DocumentIO, files fs.FS) *Controller {
 	return &Controller{Cache: helpers.NewCache(),
-		Domain: domain, RedisConfig: helpers.RedisConf{
-			Port: redisPort,
-			Addr: redisAddr,
-		},
+		Domain:   domain,
 		database: database,
 		FileIO:   files,
 	}

+ 18 - 14
pkg/controller/html_handlers.go

@@ -3,7 +3,6 @@ package controller
 import (
 	"html/template"
 	"net/http"
-	
 
 	"git.aetherial.dev/aeth/keiji/pkg/helpers"
 	"github.com/gin-gonic/gin"
@@ -50,12 +49,21 @@ func (c *Controller) ServePost(ctx *gin.Context) {
 // @Tags webpages
 // @Router / [get]
 func (c *Controller) ServeHome(ctx *gin.Context) {
+	home := c.database.GetByCategory(helpers.HOMEPAGE)
+	var content helpers.Document
+	if len(home) == 0 {
+		content = helpers.Document{
+			Body: "Under construction. Sry :(",
+		}
+	} else {
+		content = home[0]
+	}
 	ctx.HTML(http.StatusOK, "home", gin.H{
 		"navigation": gin.H{
 			"headers": c.database.GetNavBarLinks(),
 		},
-		"menu":     c.database.GetDropdownElements(),
-		"listings": c.database.GetByCategory(helpers.BLOG),
+		"menu":    c.database.GetDropdownElements(),
+		"default": content,
 	})
 }
 
@@ -67,6 +75,13 @@ func (c *Controller) ServeBlog(ctx *gin.Context) {
 	ctx.HTML(http.StatusOK, "writing", c.database.GetByCategory(helpers.BLOG))
 }
 
+// @Name ServeCreative
+// @Summary serves the HTML for the creative writing listings
+// @Tags webpages
+// @Router /creative [get]
+func (c *Controller) ServeCreative(ctx *gin.Context) {
+	ctx.HTML(http.StatusOK, "writing", c.database.GetByCategory(helpers.CREATIVE))
+}
 
 // @Name ServeDigitalArt
 // @Summary serves the HTML file for the digital art homepage
@@ -74,17 +89,6 @@ func (c *Controller) ServeBlog(ctx *gin.Context) {
 // @Router /digital [get]
 func (c *Controller) ServeDigitalArt(ctx *gin.Context) {
 	images := c.database.GetAllImages()
-	/*
-	if err != nil {
-		ctx.HTML(http.StatusInternalServerError, "unhandled_error",
-			gin.H{
-				"StatusCode": http.StatusInternalServerError,
-				"Reason":     err.Error(),
-			},
-		)
-		return
-	}
-	*/
 	ctx.HTML(http.StatusOK, "digital_art", gin.H{
 		"navigation": gin.H{
 			"headers": c.database.GetNavBarLinks(),

+ 3 - 2
pkg/controller/middleware.go

@@ -1,7 +1,6 @@
 package controller
 
 import (
-
 	"github.com/gin-gonic/gin"
 )
 
@@ -9,11 +8,13 @@ func (c *Controller) IsAuthenticated(ctx *gin.Context) {
 	cookie, err := ctx.Cookie(AUTH_COOKIE_NAME)
 	if err != nil {
 		ctx.Redirect(302, "/login")
+		ctx.AbortWithStatus(401)
 		return
 	}
 	if !c.Cache.Read(cookie) {
 		ctx.Redirect(302, "/login")
+		ctx.AbortWithStatus(401)
 		return
 	}
 	ctx.Next()
-}
+}

+ 21 - 20
pkg/helpers/auth.go

@@ -8,50 +8,51 @@ import (
 	"github.com/patrickmn/go-cache"
 )
 
-
-type InvalidCredentials struct {}
+type InvalidCredentials struct{}
 
 func (i *InvalidCredentials) Error() string {
 	return "Invalid credentials supplied."
 }
 
-
 type Credentials struct {
-	Username	string	`form:"username" json:"username"`
-	Password	string	`form:"password" json:"password"`
+	Username string `form:"username" json:"username"`
+	Password string `form:"password" json:"password"`
 }
 
-type AllCache struct {
-    AuthCookies *cache.Cache
+type AuthCache struct {
+	AuthCookies *cache.Cache
 }
 
 const (
-    defaultExpiration = 20 * time.Minute
-    purgeTime         = 1 * time.Hour
+	defaultExpiration = 20 * time.Minute
+	purgeTime         = 1 * time.Hour
 )
 
-func NewCache() *AllCache {
-    Cache := cache.New(defaultExpiration, purgeTime)
-    return &AllCache{
-        AuthCookies: Cache,
-    }
+func NewCache() *AuthCache {
+	Cache := cache.New(defaultExpiration, purgeTime)
+	return &AuthCache{
+		AuthCookies: Cache,
+	}
 }
 
-func (c *AllCache) update(id string, cookie string) {
-    c.AuthCookies.Set(id, cookie, cache.DefaultExpiration)
+func (c *AuthCache) update(id string, cookie string) {
+	c.AuthCookies.Set(id, cookie, cache.DefaultExpiration)
 }
 
-func (c *AllCache) Read(id string) bool {
-    _, ok := c.AuthCookies.Get(id)
+func (c *AuthCache) Read(id string) bool {
+	_, ok := c.AuthCookies.Get(id)
 	return ok
 }
 
-
 /*
 Recieve the credentials from frontend and validate them
+
 	:param c: pointer to Credential struct
 */
-func Authorize(c *Credentials, cache *AllCache) (string, error) {
+func Authorize(c *Credentials, cache *AuthCache) (string, error) {
+	if c.Username == "" || c.Password == "" {
+		return "", &InvalidCredentials{}
+	}
 	if c.Username == os.Getenv("USERNAME") {
 		if c.Password == os.Getenv("PASSWORD") {
 			id := uuid.New()

+ 7 - 45
pkg/helpers/helpers.go

@@ -1,34 +1,23 @@
 package helpers
 
 import (
-
-	"time"
-
 	"github.com/gomarkdown/markdown"
 	"github.com/gomarkdown/markdown/html"
 	"github.com/gomarkdown/markdown/parser"
 )
 
-const HEADER_KEY = "header-links"
-const MENU_KEY = "menu-config"
-const ADMIN_TABLE_KEY = "admin-tables"
-
 const TECHNICAL = "technical"
 const CONFIGURATION = "configuration"
 const BLOG = "blog"
 const CREATIVE = "creative"
 const DIGITAL_ART = "digital_art"
+const HOMEPAGE = "homepage"
 
 var Topics = []string{
 	TECHNICAL,
 	BLOG,
 	CREATIVE,
-}
-
-var TopicMap = map[string]string{
-	TECHNICAL: TECHNICAL,
-	BLOG:      BLOG,
-	CREATIVE:  CREATIVE,
+	HOMEPAGE,
 }
 
 type HeaderCollection struct {
@@ -52,10 +41,10 @@ type MenuElement struct {
 }
 
 type DocumentOld struct {
-	Ident    Identifier`json:"identifier"`
-	Created  string `json:"created"`
-	Body     string `json:"body"`
-	Category string `json:"category"`
+	Ident    Identifier `json:"identifier"`
+	Created  string     `json:"created"`
+	Body     string     `json:"body"`
+	Category string     `json:"category"`
 	Sample   string
 }
 
@@ -63,38 +52,11 @@ type AdminPage struct {
 	Tables map[string][]TableData `json:"tables"`
 }
 
-
-type TableData struct { // TODO: add this to the database io interface 
+type TableData struct { // TODO: add this to the database io interface
 	DisplayName string `json:"display_name"`
 	Link        string `json:"link"`
 }
 
-func NewDocument(ident string, created *time.Time, body string, category string) Document {
-
-	var ts time.Time
-	if created == nil {
-		rn := time.Now()
-		ts = time.Date(rn.Year(), rn.Month(), rn.Day(), rn.Hour(), rn.Minute(),
-			rn.Second(), rn.Nanosecond(), rn.Location())
-	} else {
-		ts = *created
-	}
-
-	return Document{Ident: Identifier(ident), Created: ts.String(), Body: body, Category: category}
-}
-
-type DocumentUpload struct {
-	Name     string `json:"name"`
-	Category string `json:"category"`
-	Text     string `json:"text"`
-}
-
-type HeaderIo interface {
-	GetHeaders() (*HeaderCollection, error)
-	AddHeaders(HeaderCollection) error
-	GetMenuLinks() (*MenuElement, error)
-}
-
 /*
 	 convert markdown to html
 		:param md: the byte array containing the Markdown to convert

+ 0 - 243
pkg/helpers/redis.go

@@ -1,243 +0,0 @@
-package helpers
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"os"
-	"strings"
-
-	"github.com/redis/go-redis/v9"
-)
-
-
-
-type DocAlreadyExists struct {
-	Key		string
-	Value	string
-}
-
-func (d *DocAlreadyExists) Error() string {
-	return fmt.Sprintf("Key: '%s' already exists with value: '%s'", d.Key, d.Value)
-}
-
-type DocDoesntExist struct {
-	Key		string
-}
-
-func (d *DocDoesntExist) Error() string {
-	return fmt.Sprintf("Document with ID: '%s' does not exist.", d.Key)
-}
-
-type InvalidTopic struct {Topic string}
-
-func (i *InvalidTopic) Error() string {
-	return fmt.Sprintf("Topic: %s is not a valid topic category.", i.Topic)
-	}
-
-type RedisConf struct {
-	Addr	string
-	Port	string
-}
-
-type RedisCaller struct {
-	ctx 	context.Context
-	Client	*redis.Client
-}
-
-
-/*
-Creates a new RedisCaller struct
-	:param redisCfg: a redis configuration struct
-*/
-func NewRedisClient(redisCfg RedisConf) *RedisCaller {
-    return &RedisCaller{Client: redis.NewClient(&redis.Options{
-        Addr:     fmt.Sprintf("%s:%v", redisCfg.Addr, redisCfg.Port),
-        DB:       0,  // use default DB
-    }),
-	ctx: context.Background(),}
-
-}
-
-/*
-retrieves all of the document IDs in the Redis database
-*/
-func (r *RedisCaller) AllDocIds() ([]string, error) {
-	return r.Client.Keys(r.ctx, "*").Result()
-}
-
-/*
-Sets the item (id) to the value supplied in value
-	:param doc: the documents.Document struct to input to the database
-*/
-func (r *RedisCaller) AddDoc(doc Document) error {
-	_, ok := TopicMap[doc.Category]
-	if !ok {
-		return &InvalidTopic{Topic: doc.Category}
-	}
-
-	val, err := r.Client.Get(r.ctx, string(doc.Ident)).Result()
-	if err == redis.Nil {
-		data, err := json.Marshal(&doc)
-		if err != nil {
-			return err
-		}
-
-		err = r.Client.Set(r.ctx, string(doc.Ident), data, 0).Err()
-		if err != nil {
-			return err
-		}
-		return nil
-    } else if err != nil {
-        return err
-    }
-	return &DocAlreadyExists{Key: string(doc.Ident), Value: val}
-}
-
-
-/*
-Add an image to the image store
-	:param img: an ImageStoreItem struct with the appropriate metadata
-*/
-func (r *RedisCaller) AddImage(img *ImageStoreItem) error {
-	val, err := r.Client.Get(r.ctx, img.Identifier).Result()
-	if err == redis.Nil {
-		data, err := json.Marshal(img)
-		if err != nil {
-			return err
-		}
-		err = r.Client.Set(r.ctx, img.Identifier, data, 0).Err()
-		if err != nil {
-			return err
-		}
-		return nil
-    } else if err != nil {
-        return err
-    }
-	return &DocAlreadyExists{Key: img.Identifier, Value: val}
-}
-
-
-/*
-Gets the item stored at the key (id)
-	:param id: the id of the object to get
-*/
-func (r *RedisCaller) GetItem(id string) (*Document, error) {
-
-	var doc Document
-	val, err := r.Client.Get(r.ctx, id).Result()
-	if err == redis.Nil {
-		return nil, err
-    } else if err != nil {
-        return nil, err
-    }
-	data := []byte(val)
-	err = json.Unmarshal(data, &doc)
-	if err != nil {
-		return nil, err
-	}
-	return &doc, nil
-}
-
-/*
-Retrieve all redis items by category. Returns all the IDs of items that belong to that category
-	:param category: the category to filter by
-*/
-func (r *RedisCaller) GetByCategory(category string) ([]string, error) {
-	ids, err := r.AllDocIds()
-	if err != nil {
-		return nil, err
-	}
-	var matches []string
-	for i := range ids {
-		item, err := r.GetItem(ids[i])
-		if err != nil {
-			return nil, err
-		}
-		if item.Category == category {
-			matches = append(matches, ids[i])
-		}
-
-	}
-	return matches, nil
-}
-
-
-/*
-Delete the target document in redis
-	:param id: the id to delete from redis
-*/
-func (r *RedisCaller) DeleteDoc(id string) error {
-	_, err := r.Client.Get(r.ctx, id).Result()
-	if err == redis.Nil {
-		return &DocDoesntExist{id}
-    } else if err != nil {
-        return err
-    }
-
-	err = r.Client.Del(r.ctx, id).Err()
-	if err != nil {
-		return err
-	}
-	return nil
-}
-
-/*
-Update a value in redis
-	:param id: the id of the document to edit
-*/
-func (r *RedisCaller) editVal(id string, in interface{}) error {
-	_, err := r.Client.Get(r.ctx, id).Result()
-	if err != nil {
-		if err == redis.Nil {
-			return &DocDoesntExist{Key: id}
-		}
-		return err
-	}
-
-		data, err := json.Marshal(&in)
-		if err != nil {
-			return err
-		}
-
-		err = r.Client.Set(r.ctx, id, data, 0).Err()
-		if err != nil {
-			return err
-		}
-		return nil
-    }
-
-func (r *RedisCaller) SeedData(seedLoc string) error {
-	dirs, err := os.ReadDir(seedLoc)
-	if err != nil {
-		return err
-	}
-	for i := range dirs {
-		key := strings.Split(dirs[i].Name(), ".")[0]
-		b, err := os.ReadFile(fmt.Sprintf("%s/%s", seedLoc, dirs[i].Name()))
-		if err != nil {
-			return err
-		}
-		err = r.Client.Set(r.ctx, key, b, 0).Err()
-		if err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-
-
-
-
-func (r *RedisCaller) UpdatePost(id string, new Document) error {
-	return r.editVal(id, new)
-}
-
-func (r *RedisCaller) UpdateHeader(id string, new HeaderCollection) error {
-	return r.editVal(id, new)
-}
-
-func (r *RedisCaller) UpdateMenu(id string, new MenuElement) error {
-	return r.editVal(id, new)
-}

+ 0 - 33
pkg/helpers/storage.go

@@ -2,7 +2,6 @@ package helpers
 
 import (
 	"database/sql"
-	"encoding/json"
 	"errors"
 	"fmt"
 	"log"
@@ -14,7 +13,6 @@ import (
 
 	"git.aetherial.dev/aeth/keiji/pkg/env"
 	"github.com/google/uuid"
-	"github.com/redis/go-redis/v9"
 )
 
 type DatabaseSchema struct {
@@ -674,34 +672,3 @@ func GetImageStore() string {
 func newIdentifier() Identifier {
 	return Identifier(uuid.NewString())
 }
-
-/*
-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) {
-	ids, err := rds.GetByCategory(DIGITAL_ART)
-	if err != nil {
-		return nil, err
-	}
-
-	var imageEntries []*ImageStoreItem
-	for i := range ids {
-		val, err := rds.Client.Get(rds.ctx, ids[i]).Result()
-		if err == redis.Nil {
-			return nil, err
-		} else if err != nil {
-			return nil, err
-		}
-		data := []byte(val)
-		var imageEntry ImageStoreItem
-		err = json.Unmarshal(data, &imageEntry)
-		if err != nil {
-			return nil, err
-		}
-		imageEntry.ApiPath = fmt.Sprintf("/api/v1/images/%s", imageEntry.Filename)
-		imageEntries = append(imageEntries, &imageEntry)
-	}
-	return imageEntries, err
-}

+ 3 - 2
pkg/routes/register.go

@@ -8,12 +8,13 @@ import (
 	"github.com/gin-gonic/gin"
 )
 
-func Register(e *gin.Engine, domain string, redisPort string, redisAddr string, database helpers.DocumentIO, files fs.FS) {
-	c := controller.NewController(domain, redisPort, redisAddr, database, files)
+func Register(e *gin.Engine, domain string, database helpers.DocumentIO, files fs.FS) {
+	c := controller.NewController(domain, database, files)
 	web := e.Group("")
 	web.GET("/", c.ServeHome)
 	web.GET("/blog", c.ServeBlog)
 	web.GET("/digital", c.ServeDigitalArt)
+	web.GET("/creative", c.ServeCreative)
 	web.GET("/writing/:id", c.ServePost)
 	web.GET("/login", c.ServeLogin)
 	web.POST("/login", c.Auth)

+ 79 - 0
scripts/seed.py

@@ -0,0 +1,79 @@
+#!/bin/python
+import os
+import sys
+import stat
+import subprocess
+
+KEIJI_CTL = "keiji-ctl"
+URL = os.getenv("SITE_URL")
+
+# the seed loop needs to create all of the:
+#   - admin table links
+#   - navbar png/redirect combos
+#   - the menu link pairs
+
+admin_table = {
+        "new": {
+            "blog post": "/admin/posts",
+            "digital media": "/admin/upload"
+            },
+        "modify": {
+            "blog post": "/admin/posts/all"
+            },
+        }
+menu = {
+        "//Administrator": "/admin/panel",
+        "//Creative Writing": "/creative",
+        "//Black Box": "/blog",
+        "//Digital Art": "/digital"
+        }
+
+
+navbar_items = {
+        "./assets/github.png": "https://github.com/AETH-erial",
+        "./assets/git.png": "https://git.aetherial.dev/aeth",
+        "./assets/twitter.png": "https://x.com/Aetherial___",
+        "./assets/linkedin.png": "https://www.linkedin.com/in/russell-hrubesky-a62237221/",
+        "./assets/soundcloud.png": "https://soundcloud.com/aeth-592553883"
+            }
+
+assets = [
+        "./assets/menu.png",
+        "./assets/github.png",
+        "./assets/git.png",
+        "./assets/twitter.png",
+        "./assets/linkedin.png",
+        "./assets/soundcloud.png"
+        ]
+
+
+# find the keiji-ctl command
+def _find_keiji_ctl() -> str:
+    split_path = os.environ["PATH"].split(":")
+    for path in split_path:
+        cmd_path = f"{path}/{KEIJI_CTL}"
+        try:
+            mode = os.stat(f"{path}/{KEIJI_CTL}").st_mode
+        except FileNotFoundError:
+            continue
+        if stat.S_ISREG(mode):
+            return cmd_path
+    raise FileNotFoundError(f"the {KEIJI_CTL} binary could not be found in the system path.")
+
+
+def main():
+    """ setup script shit """
+    path = _find_keiji_ctl()
+    cookie = subprocess.run([path, "-cmd", "auth", "-address", URL], capture_output=True, text=True).stdout.strip("\n")
+    print(cookie)
+    for asset in assets:
+        subprocess.run([path, "-address", URL, "-cookie", cookie, "-cmd", "asset", "-png", asset])
+    for image, redirect in navbar_items.items():
+        subprocess.run([path, "-address", URL, "-cookie", cookie, "-cmd", "nav", "-png", image, "-redirect", redirect])
+    for text, redirect in menu.items():
+        subprocess.run([path, "-address", URL, "-cookie", cookie, "-cmd", "menu", "-text", text, "-redirect", redirect])
+    for category, pairings in admin_table.items():
+        for text, redirect in pairings.items():
+            subprocess.run([path, "-address", URL, "-cookie", cookie, "-cmd", "admin", "-text", text, "-redirect", redirect, "-col", category])
+
+main()