From 6400f1fa950f5dbe33e1fb53730232b08257721d Mon Sep 17 00:00:00 2001 From: ngn Date: Wed, 24 Jul 2024 01:15:37 +0300 Subject: [PATCH] API redesign, better styling for the blog page --- api/Makefile | 9 +- api/database/database.go | 55 ++++ api/global/global.go | 23 ++ api/main.go | 90 ++++-- api/routes/admin.go | 213 ++++++------ api/routes/blog.go | 445 ++++++++++++++------------ api/routes/global.go | 74 ----- api/routes/routes.go | 48 --- api/routes/services.go | 61 ++-- api/util/utils.go | 57 ++-- app/src/lib/header.svelte | 15 +- app/src/routes/+page.svelte | 2 +- app/src/routes/blog/[id]/+page.svelte | 69 ++-- app/src/routes/donate/+page.svelte | 8 +- app/static/global.css | 5 +- app/static/markdown.css | 6 +- 16 files changed, 622 insertions(+), 558 deletions(-) create mode 100644 api/database/database.go create mode 100644 api/global/global.go delete mode 100644 api/routes/global.go delete mode 100644 api/routes/routes.go diff --git a/api/Makefile b/api/Makefile index a469149..ff72694 100644 --- a/api/Makefile +++ b/api/Makefile @@ -1,7 +1,12 @@ -server: *.go routes/* util/* +all: server + +server: *.go routes/* util/* global/* database/* go build -o server . test: FRONTEND_URL=http://localhost:5173/ PASSWORD=test ./server -.PHONY: test +format: + gofmt -s -w . + +.PHONY: test format diff --git a/api/database/database.go b/api/database/database.go new file mode 100644 index 0000000..0388f24 --- /dev/null +++ b/api/database/database.go @@ -0,0 +1,55 @@ +package database + +import ( + "database/sql" + "github.com/ngn13/website/api/global" +) + +type Type struct { + Sql *sql.DB + Votes []global.Vote +} + +func (t *Type) Setup() error { + t.Votes = []global.Vote{} + + _, err := t.Sql.Exec(` + CREATE TABLE IF NOT EXISTS posts( + id TEXT NOT NULL UNIQUE, + title TEXT NOT NULL, + author TEXT NOT NULL, + date TEXT NOT NULL, + content TEXT NOT NULL, + public INTEGER NOT NULL, + vote INTEGER NOT NULL + ); + `) + + if err != nil { + return err + } + + _, err = t.Sql.Exec(` + CREATE TABLE IF NOT EXISTS services( + name TEXT NOT NULL UNIQUE, + desc TEXT NOT NULL, + url TEXT NOT NULL + ); + `) + + return err +} + +func (t *Type) Open(p string) error { + var err error + + if t.Sql, err = sql.Open("sqlite3", p); err != nil { + return err + } + + return t.Setup() +} + +func (t *Type) Close() { + t.Sql.Close() +} diff --git a/api/global/global.go b/api/global/global.go new file mode 100644 index 0000000..6542187 --- /dev/null +++ b/api/global/global.go @@ -0,0 +1,23 @@ +package global + +type Post struct { + ID string `json:"id"` + Title string `json:"title"` + Author string `json:"author"` + Date string `json:"date"` + Content string `json:"content"` + Public int `json:"public"` + Vote int `json:"vote"` +} + +type Service struct { + Name string `json:"name"` + Desc string `json:"desc"` + Url string `json:"url"` +} + +type Vote struct { + Post string + Client string + Status string +} diff --git a/api/main.go b/api/main.go index 59cc9b4..545ef4d 100644 --- a/api/main.go +++ b/api/main.go @@ -1,42 +1,78 @@ package main import ( - "database/sql" "log" + "net/http" "github.com/gofiber/fiber/v2" + "github.com/ngn13/website/api/database" "github.com/ngn13/website/api/routes" ) -func CorsMiddleware(c *fiber.Ctx) error { - c.Set("Access-Control-Allow-Origin", "*") - c.Set("Access-Control-Allow-Headers", - "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With") - c.Set("Access-Control-Allow-Methods", "PUT, DELETE, GET") - return c.Next() -} - func main() { - app := fiber.New(fiber.Config{ - DisableStartupMessage: true, - }) - app.Use(CorsMiddleware) + var ( + app *fiber.App + db database.Type + err error + ) - db, err := sql.Open("sqlite3", "data.db") - if err != nil { - log.Fatal("Cannot connect to the database: "+err.Error()) - } + if err = db.Open("data.db"); err != nil { + log.Fatalf("Cannot access the database: %s", err.Error()) + return + } + defer db.Close() - log.Println("Creating tables") - routes.BlogDb(db) - routes.ServicesDb(db) - routes.Setup(app, db) + app = fiber.New(fiber.Config{ + DisableStartupMessage: true, + }) - log.Println("Starting web server at port 7001") - err = app.Listen("0.0.0.0:7001") - if err != nil { - log.Printf("Error starting the webserver: %s", err.Error()) - } + app.Use("*", func(c *fiber.Ctx) error { + c.Set("Access-Control-Allow-Origin", "*") + c.Set("Access-Control-Allow-Headers", + "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With") + c.Set("Access-Control-Allow-Methods", "PUT, DELETE, GET") + c.Locals("database", &db) + return c.Next() + }) - defer db.Close() + // index route + app.Get("/", func(c *fiber.Ctx) error { + return c.Send([]byte("o/")) + }) + + // blog routes + blog_routes := app.Group("/blog") + blog_routes.Get("/feed.atom", routes.GetAtomFeed) + blog_routes.Get("/feed.rss", routes.GetRSSFeed) + blog_routes.Get("/feed.json", routes.GetJSONFeed) + blog_routes.Get("/sum", routes.SumPost) + blog_routes.Get("/get", routes.GetPost) + blog_routes.Get("/vote/set", routes.VoteSet) + blog_routes.Get("/vote/status", routes.VoteStat) + + // service routes + service_routes := app.Group("services") + service_routes.Get("/all", routes.GetServices) + + // admin routes + admin_routes := app.Group("admin") + admin_routes.Use("*", routes.AuthMiddleware) + admin_routes.Get("/login", routes.Login) + admin_routes.Get("/logout", routes.Logout) + admin_routes.Put("/service/add", routes.AddService) + admin_routes.Delete("/service/remove", routes.RemoveService) + admin_routes.Put("/blog/add", routes.AddPost) + admin_routes.Delete("/blog/remove", routes.RemovePost) + + // 404 routes + app.All("*", func(c *fiber.Ctx) error { + return c.Status(http.StatusNotFound).JSON(fiber.Map{ + "error": "Requested endpoint not found", + }) + }) + + log.Println("Starting web server at port 7001") + if err = app.Listen("0.0.0.0:7001"); err != nil { + log.Printf("Error starting the webserver: %s", err.Error()) + } } diff --git a/api/routes/admin.go b/api/routes/admin.go index c1ac4ec..b628d52 100644 --- a/api/routes/admin.go +++ b/api/routes/admin.go @@ -1,6 +1,7 @@ package routes import ( + "log" "net/http" "os" "strings" @@ -8,132 +9,164 @@ import ( "github.com/gofiber/fiber/v2" "github.com/mattn/go-sqlite3" + "github.com/ngn13/website/api/database" + "github.com/ngn13/website/api/global" "github.com/ngn13/website/api/util" ) -var Token string = util.CreateToken() +var Token string = util.CreateToken() func AuthMiddleware(c *fiber.Ctx) error { - if c.Path() == "/admin/login" { - return c.Next() - } + if c.Path() == "/admin/login" { + return c.Next() + } - if c.Get("Authorization") != Token { - return util.ErrAuth(c) - } + if c.Get("Authorization") != Token { + return util.ErrAuth(c) + } - return c.Next() + return c.Next() } -func Login(c *fiber.Ctx) error{ - if c.Query("pass") != os.Getenv("PASSWORD") { - return c.Status(http.StatusUnauthorized).JSON(fiber.Map{ - "error": "Authentication failed", - }) - } +func Login(c *fiber.Ctx) error { + if c.Query("pass") != os.Getenv("PASSWORD") { + return c.Status(http.StatusUnauthorized).JSON(fiber.Map{ + "error": "Authentication failed", + }) + } - return c.Status(http.StatusOK).JSON(fiber.Map{ - "error": "", - "token": Token, - }) + log.Printf("New login from %s", util.GetIP(c)) + + return c.Status(http.StatusOK).JSON(fiber.Map{ + "error": "", + "token": Token, + }) } -func Logout(c *fiber.Ctx) error{ - Token = util.CreateToken() - return c.Status(http.StatusOK).JSON(fiber.Map{ - "error": "", - }) +func Logout(c *fiber.Ctx) error { + Token = util.CreateToken() + + log.Printf("Logout from %s", util.GetIP(c)) + + return c.Status(http.StatusOK).JSON(fiber.Map{ + "error": "", + }) } func RemoveService(c *fiber.Ctx) error { - name := c.Query("name") - if name == "" { - util.ErrBadData(c) - } + var ( + db *database.Type + name string + ) - _, err := DB.Exec("DELETE FROM services WHERE name = ?", name) - if util.ErrorCheck(err, c){ - return util.ErrServer(c) - } - - return util.NoError(c) + db = c.Locals("database").(*database.Type) + name = c.Query("name") + + if name == "" { + util.ErrBadData(c) + } + + _, err := db.Sql.Exec("DELETE FROM services WHERE name = ?", name) + if util.ErrorCheck(err, c) { + return util.ErrServer(c) + } + + return util.NoError(c) } func AddService(c *fiber.Ctx) error { - var service Service - if c.BodyParser(&service) != nil { - return util.ErrBadJSON(c) - } + var ( + service global.Service + db *database.Type + ) - if service.Name == "" || service.Desc == "" || service.Url == "" { - return util.ErrBadData(c) - } + db = c.Locals("database").(*database.Type) - rows, err := DB.Query("SELECT * FROM services WHERE name = ?", service.Name) - if util.ErrorCheck(err, c){ - return util.ErrServer(c) - } - - if rows.Next() { - rows.Close() - return util.ErrExists(c) - } + if c.BodyParser(&service) != nil { + return util.ErrBadJSON(c) + } - rows.Close() + if service.Name == "" || service.Desc == "" || service.Url == "" { + return util.ErrBadData(c) + } - _, err = DB.Exec( - "INSERT INTO services(name, desc, url) values(?, ?, ?)", - service.Name, service.Desc, service.Url, - ) + rows, err := db.Sql.Query("SELECT * FROM services WHERE name = ?", service.Name) + if util.ErrorCheck(err, c) { + return util.ErrServer(c) + } - if util.ErrorCheck(err, c){ - return util.ErrServer(c) - } + if rows.Next() { + rows.Close() + return util.ErrExists(c) + } - return util.NoError(c) + rows.Close() + + _, err = db.Sql.Exec( + "INSERT INTO services(name, desc, url) values(?, ?, ?)", + service.Name, service.Desc, service.Url, + ) + + if util.ErrorCheck(err, c) { + return util.ErrServer(c) + } + + return util.NoError(c) } -func RemovePost(c *fiber.Ctx) error{ - var id = c.Query("id") - if id == "" { - return util.ErrBadData(c) - } +func RemovePost(c *fiber.Ctx) error { + var ( + db *database.Type + id string + ) - _, err := DB.Exec("DELETE FROM posts WHERE id = ?", id) - if util.ErrorCheck(err, c){ - return util.ErrServer(c) - } + db = c.Locals("database").(*database.Type) + id = c.Query("id") - return util.NoError(c) + if id == "" { + return util.ErrBadData(c) + } + + _, err := db.Sql.Exec("DELETE FROM posts WHERE id = ?", id) + if util.ErrorCheck(err, c) { + return util.ErrServer(c) + } + + return util.NoError(c) } -func AddPost(c *fiber.Ctx) error{ - var post Post - post.Public = 1 +func AddPost(c *fiber.Ctx) error { + var ( + db *database.Type + post global.Post + ) - if c.BodyParser(&post) != nil { - return util.ErrBadJSON(c) - } + db = c.Locals("database").(*database.Type) + post.Public = 1 - if post.Title == "" || post.Author == "" || post.Content == "" { - return util.ErrBadData(c) - } + if c.BodyParser(&post) != nil { + return util.ErrBadJSON(c) + } - post.Date = time.Now().Format("02/01/06") - post.ID = TitleToID(post.Title) + if post.Title == "" || post.Author == "" || post.Content == "" { + return util.ErrBadData(c) + } - _, err := DB.Exec( - "INSERT INTO posts(id, title, author, date, content, public, vote) values(?, ?, ?, ?, ?, ?, ?)", - post.ID, post.Title, post.Author, post.Date, post.Content, post.Public, post.Vote, - ) + post.Date = time.Now().Format("02/01/06") + post.ID = util.TitleToID(post.Title) - if err != nil && strings.Contains(err.Error(), sqlite3.ErrConstraintUnique.Error()) { - return util.ErrExists(c) - } + _, err := db.Sql.Exec( + "INSERT INTO posts(id, title, author, date, content, public, vote) values(?, ?, ?, ?, ?, ?, ?)", + post.ID, post.Title, post.Author, post.Date, post.Content, post.Public, post.Vote, + ) - if util.ErrorCheck(err, c){ - return util.ErrExists(c) - } + if err != nil && strings.Contains(err.Error(), sqlite3.ErrConstraintUnique.Error()) { + return util.ErrExists(c) + } - return util.NoError(c) + if util.ErrorCheck(err, c) { + return util.ErrExists(c) + } + + return util.NoError(c) } diff --git a/api/routes/blog.go b/api/routes/blog.go index f7cc4c4..de214ed 100644 --- a/api/routes/blog.go +++ b/api/routes/blog.go @@ -2,274 +2,317 @@ package routes import ( "database/sql" - "log" + "fmt" "net/http" "net/url" - "fmt" "os" - "strings" "time" "github.com/gofiber/fiber/v2" "github.com/gorilla/feeds" + "github.com/ngn13/website/api/database" + "github.com/ngn13/website/api/global" "github.com/ngn13/website/api/util" ) -func BlogDb(db *sql.DB) { - _, err := db.Exec(` - CREATE TABLE IF NOT EXISTS posts( - id TEXT NOT NULL UNIQUE, - title TEXT NOT NULL, - author TEXT NOT NULL, - date TEXT NOT NULL, - content TEXT NOT NULL, - public INTEGER NOT NULL, - vote INTEGER NOT NULL - ); - `) - DB = db - if err != nil { - log.Fatal("Error creating table: "+err.Error()) - } +func PostFromRow(post *global.Post, rows *sql.Rows) error { + err := rows.Scan(&post.ID, &post.Title, &post.Author, &post.Date, &post.Content, &post.Public, &post.Vote) + if err != nil { + return err + } + + return nil } -func GetIP(c *fiber.Ctx) string { - if c.Get("X-Real-IP") != "" { - return strings.Clone(c.Get("X-Real-IP")) - } +func GetPostByID(db *database.Type, id string) (global.Post, string) { + var post global.Post = global.Post{} + post.Title = "NONE" - return c.IP() + rows, err := db.Sql.Query("SELECT * FROM posts WHERE id = ?", id) + + if err != nil { + return post, "Server error" + } + + success := rows.Next() + if !success { + rows.Close() + return post, "Post not found" + } + + err = PostFromRow(&post, rows) + if err != nil { + rows.Close() + return post, "Server error" + } + rows.Close() + + if post.Title == "NONE" { + return post, "Post not found" + } + + return post, "" } -func VoteStat(c *fiber.Ctx) error{ - var id = c.Query("id") - if id == "" { - return util.ErrBadData(c) - } +func VoteStat(c *fiber.Ctx) error { + var ( + db *database.Type + id string + ) - for i := 0; i < len(votelist); i++ { - if votelist[i].Client == GetIP(c) && votelist[i].Post == id { - return c.JSON(fiber.Map { - "error": "", - "result": votelist[i].Status, - }) - } - } + db = c.Locals("database").(*database.Type) + id = c.Query("id") - return c.Status(http.StatusNotFound).JSON(util.ErrorJSON("Client never voted")) + if id == "" { + return util.ErrBadData(c) + } + + for i := 0; i < len(db.Votes); i++ { + if db.Votes[i].Client == util.GetIP(c) && db.Votes[i].Post == id { + return c.JSON(fiber.Map{ + "error": "", + "result": db.Votes[i].Status, + }) + } + } + + return c.Status(http.StatusNotFound).JSON(util.ErrorJSON("Client never voted")) } -func VoteSet(c *fiber.Ctx) error{ - var id = c.Query("id") - var to = c.Query("to") - voted := false +func VoteSet(c *fiber.Ctx) error { + var ( + id string + to string + voted bool + db *database.Type + ) - if id == "" || (to != "upvote" && to != "downvote") { - return util.ErrBadData(c) - } + db = c.Locals("database").(*database.Type) + id = c.Query("id") + to = c.Query("to") + voted = false - for i := 0; i < len(votelist); i++ { - if votelist[i].Client == GetIP(c) && votelist[i].Post == id && votelist[i].Status == to { - return c.Status(http.StatusForbidden).JSON(util.ErrorJSON("Client already voted")) - } + if id == "" || (to != "upvote" && to != "downvote") { + return util.ErrBadData(c) + } - if votelist[i].Client == GetIP(c) && votelist[i].Post == id && votelist[i].Status != to { - voted = true - } - } + for i := 0; i < len(db.Votes); i++ { + if db.Votes[i].Client == util.GetIP(c) && db.Votes[i].Post == id && db.Votes[i].Status == to { + return c.Status(http.StatusForbidden).JSON(util.ErrorJSON("Client already voted")) + } - post, msg := GetPostByID(id) - if msg != ""{ - return c.Status(http.StatusNotFound).JSON(util.ErrorJSON(msg)) - } + if db.Votes[i].Client == util.GetIP(c) && db.Votes[i].Post == id && db.Votes[i].Status != to { + voted = true + } + } - vote := post.Vote+1 + post, msg := GetPostByID(db, id) + if msg != "" { + return c.Status(http.StatusNotFound).JSON(util.ErrorJSON(msg)) + } - if to == "downvote" { - vote = post.Vote-1 - } + vote := post.Vote + 1 - if to == "downvote" && voted { - vote = vote-1 - } + if to == "downvote" { + vote = post.Vote - 1 + } - if to == "upvote" && voted { - vote = vote+1 - } + if to == "downvote" && voted { + vote = vote - 1 + } - _, err := DB.Exec("UPDATE posts SET vote = ? WHERE title = ?", vote, post.Title) - if util.ErrorCheck(err, c){ - return util.ErrServer(c) - } + if to == "upvote" && voted { + vote = vote + 1 + } - for i := 0; i < len(votelist); i++ { - if votelist[i].Client == GetIP(c) && votelist[i].Post == id && votelist[i].Status != to { - votelist[i].Status = to - return util.NoError(c) - } - } + _, err := db.Sql.Exec("UPDATE posts SET vote = ? WHERE title = ?", vote, post.Title) + if util.ErrorCheck(err, c) { + return util.ErrServer(c) + } - var entry = Vote{} - entry.Client = GetIP(c) - entry.Status = to - entry.Post = id - votelist = append(votelist, entry) - return util.NoError(c) + for i := 0; i < len(db.Votes); i++ { + if db.Votes[i].Client == util.GetIP(c) && db.Votes[i].Post == id && db.Votes[i].Status != to { + db.Votes[i].Status = to + return util.NoError(c) + } + } + + var entry = global.Vote{} + entry.Client = util.GetIP(c) + entry.Status = to + entry.Post = id + db.Votes = append(db.Votes, entry) + return util.NoError(c) } -func GetPost(c *fiber.Ctx) error{ - var id = c.Query("id") - if id == "" { - return util.ErrBadData(c) - } +func GetPost(c *fiber.Ctx) error { + var ( + id string + db *database.Type + ) - post, msg := GetPostByID(id) - if msg != ""{ - return c.Status(http.StatusNotFound).JSON(util.ErrorJSON(msg)) - } + id = c.Query("id") + db = c.Locals("database").(*database.Type) - return c.JSON(fiber.Map { - "error": "", - "result": post, - }) + if id == "" { + return util.ErrBadData(c) + } + + post, msg := GetPostByID(db, id) + if msg != "" { + return c.Status(http.StatusNotFound).JSON(util.ErrorJSON(msg)) + } + + return c.JSON(fiber.Map{ + "error": "", + "result": post, + }) } +func SumPost(c *fiber.Ctx) error { + var ( + posts []global.Post + post global.Post + db *database.Type + err error + ) + db = c.Locals("database").(*database.Type) -func SumPost(c *fiber.Ctx) error{ - var posts []Post = []Post{} - rows, err := DB.Query("SELECT * FROM posts") - if util.ErrorCheck(err, c) { - return util.ErrServer(c) - } + rows, err := db.Sql.Query("SELECT * FROM posts") + if util.ErrorCheck(err, c) { + return util.ErrServer(c) + } - for rows.Next() { - var post Post - err := PostFromRow(&post, rows) + for rows.Next() { + err = PostFromRow(&post, rows) - if util.ErrorCheck(err, c) { - return util.ErrServer(c) - } - - if post.Public == 0 { - continue - } + if util.ErrorCheck(err, c) { + return util.ErrServer(c) + } - if len(post.Content) > 255{ - post.Content = post.Content[0:250] - } + if post.Public == 0 { + continue + } - posts = append(posts, post) - } - rows.Close() + if len(post.Content) > 255 { + post.Content = post.Content[0:250] + } - return c.JSON(fiber.Map { - "error": "", - "result": posts, - }) + posts = append(posts, post) + } + rows.Close() + + return c.JSON(fiber.Map{ + "error": "", + "result": posts, + }) } -func GetFeed() (*feeds.Feed, error){ - var posts []Post = []Post{} - rows, err := DB.Query("SELECT * FROM posts") - if err != nil { - return nil, err - } +func GetFeed(db *database.Type) (*feeds.Feed, error) { + var ( + posts []global.Post + post global.Post + err error + ) - for rows.Next() { - var post Post - err := PostFromRow(&post, rows) + rows, err := db.Sql.Query("SELECT * FROM posts") + if err != nil { + return nil, err + } - if err != nil { - return nil, err - } - - if post.Public == 0 { - continue - } + for rows.Next() { + err := PostFromRow(&post, rows) - posts = append(posts, post) - } - rows.Close() + if err != nil { + return nil, err + } + if post.Public == 0 { + continue + } - blogurl, err := url.JoinPath(os.Getenv("FRONTEND_URL"), "/blog") - if err != nil { - return nil, fmt.Errorf("failed to create the blog URL: %s", err.Error()) - } + posts = append(posts, post) + } + rows.Close() - feed := &feeds.Feed{ - Title: "[ngn.tf] | blog", - Link: &feeds.Link{Href: blogurl}, - Description: "ngn's personal blog", - Author: &feeds.Author{Name: "ngn", Email: "ngn@ngn.tf"}, - Created: time.Now(), - } + blogurl, err := url.JoinPath(os.Getenv("FRONTEND_URL"), "/blog") + if err != nil { + return nil, fmt.Errorf("failed to create the blog URL: %s", err.Error()) + } - feed.Items = []*feeds.Item{} - for _, p := range posts { - purl, err := url.JoinPath(blogurl, p.ID) - if err != nil { - return nil, fmt.Errorf("failed to create URL for '%s': %s\n", p.ID, err.Error()) - } + feed := &feeds.Feed{ + Title: "[ngn.tf] | blog", + Link: &feeds.Link{Href: blogurl}, + Description: "ngn's personal blog", + Author: &feeds.Author{Name: "ngn", Email: "ngn@ngn.tf"}, + Created: time.Now(), + } - parsed, err := time.Parse("02/01/06", p.Date) - if err != nil { - return nil, fmt.Errorf("failed to parse time for '%s': %s\n", p.ID, err.Error()) - } + feed.Items = []*feeds.Item{} + for _, p := range posts { + purl, err := url.JoinPath(blogurl, p.ID) + if err != nil { + return nil, fmt.Errorf("failed to create URL for '%s': %s\n", p.ID, err.Error()) + } - feed.Items = append(feed.Items, &feeds.Item{ - Id: p.ID, - Title: p.Title, - Link: &feeds.Link{Href: purl}, - Author: &feeds.Author{Name: p.Author}, - Created: parsed, - }) - } + parsed, err := time.Parse("02/01/06", p.Date) + if err != nil { + return nil, fmt.Errorf("failed to parse time for '%s': %s\n", p.ID, err.Error()) + } - return feed, nil + feed.Items = append(feed.Items, &feeds.Item{ + Id: p.ID, + Title: p.Title, + Link: &feeds.Link{Href: purl}, + Author: &feeds.Author{Name: p.Author}, + Created: parsed, + }) + } + + return feed, nil } func GetAtomFeed(c *fiber.Ctx) error { - feed, err := GetFeed() - if util.ErrorCheck(err, c){ - return util.ErrServer(c) - } + feed, err := GetFeed(c.Locals("database").(*database.Type)) + if util.ErrorCheck(err, c) { + return util.ErrServer(c) + } - atom, err := feed.ToAtom() - if util.ErrorCheck(err, c){ - return util.ErrServer(c) - } + atom, err := feed.ToAtom() + if util.ErrorCheck(err, c) { + return util.ErrServer(c) + } - c.Set("Content-Type", "application/atom+xml") - return c.Send([]byte(atom)) + c.Set("Content-Type", "application/atom+xml") + return c.Send([]byte(atom)) } func GetRSSFeed(c *fiber.Ctx) error { - feed, err := GetFeed() - if util.ErrorCheck(err, c){ - return util.ErrServer(c) - } + feed, err := GetFeed(c.Locals("database").(*database.Type)) + if util.ErrorCheck(err, c) { + return util.ErrServer(c) + } - rss, err := feed.ToRss() - if util.ErrorCheck(err, c){ - return util.ErrServer(c) - } + rss, err := feed.ToRss() + if util.ErrorCheck(err, c) { + return util.ErrServer(c) + } - c.Set("Content-Type", "application/rss+xml") - return c.Send([]byte(rss)) + c.Set("Content-Type", "application/rss+xml") + return c.Send([]byte(rss)) } func GetJSONFeed(c *fiber.Ctx) error { - feed, err := GetFeed() - if util.ErrorCheck(err, c){ - return util.ErrServer(c) - } + feed, err := GetFeed(c.Locals("database").(*database.Type)) + if util.ErrorCheck(err, c) { + return util.ErrServer(c) + } - json, err := feed.ToJSON() - if util.ErrorCheck(err, c){ - return util.ErrServer(c) - } - c.Set("Content-Type", "application/feed+json") - return c.Send([]byte(json)) + json, err := feed.ToJSON() + if util.ErrorCheck(err, c) { + return util.ErrServer(c) + } + c.Set("Content-Type", "application/feed+json") + return c.Send([]byte(json)) } diff --git a/api/routes/global.go b/api/routes/global.go deleted file mode 100644 index f33264e..0000000 --- a/api/routes/global.go +++ /dev/null @@ -1,74 +0,0 @@ -package routes - -import ( - "database/sql" - "strings" -) - -// ############### BLOG ############### -type Post struct { - ID string `json:"id"` - Title string `json:"title"` - Author string `json:"author"` - Date string `json:"date"` - Content string `json:"content"` - Public int `json:"public"` - Vote int `json:"vote"` -} - -var votelist = []Vote{} -type Vote struct { - Post string - Client string - Status string -} - -func PostFromRow(post *Post, rows *sql.Rows) error{ - err := rows.Scan(&post.ID, &post.Title, &post.Author, &post.Date, &post.Content, &post.Public, &post.Vote) - if err != nil { - return err - } - - return nil -} - -func GetPostByID(id string) (Post, string) { - var post Post = Post{} - post.Title = "NONE" - - rows, err := DB.Query("SELECT * FROM posts WHERE id = ?", id) - - if err != nil{ - return post, "Server error" - } - - success := rows.Next() - if !success { - rows.Close() - return post, "Post not found" - } - - err = PostFromRow(&post, rows) - if err != nil { - rows.Close() - return post, "Server error" - } - rows.Close() - - if post.Title == "NONE" { - return post, "Post not found" - } - - return post, "" -} - -func TitleToID(name string) string{ - return strings.ToLower(strings.ReplaceAll(name, " ", "")) -} - -// ############### SERVICES ############### -type Service struct { - Name string `json:"name"` - Desc string `json:"desc"` - Url string `json:"url"` -} diff --git a/api/routes/routes.go b/api/routes/routes.go deleted file mode 100644 index 9a2ee9b..0000000 --- a/api/routes/routes.go +++ /dev/null @@ -1,48 +0,0 @@ -package routes - -import ( - "database/sql" - "net/http" - - "github.com/gofiber/fiber/v2" -) - -var DB *sql.DB - -func Setup(app *fiber.App, db *sql.DB){ - // database init - DB = db - - // index route - app.Get("/", func(c *fiber.Ctx) error { - return c.Send([]byte("o/")) - }) - - // blog routes - app.Get("/blog/feed.atom", GetAtomFeed) - app.Get("/blog/feed.rss", GetRSSFeed) - app.Get("/blog/feed.json", GetJSONFeed) - app.Get("/blog/sum", SumPost) - app.Get("/blog/get", GetPost) - app.Get("/blog/vote/set", VoteSet) - app.Get("/blog/vote/status", VoteStat) - - // service routes - app.Get("/services/all", GetServices) - - // admin routes - app.Use("/admin*", AuthMiddleware) - app.Get("/admin/login", Login) - app.Get("/admin/logout", Logout) - app.Put("/admin/service/add", AddService) - app.Delete("/admin/service/remove", RemoveService) - app.Put("/admin/blog/add", AddPost) - app.Delete("/admin/blog/remove", RemovePost) - - // 404 page - app.All("*", func(c *fiber.Ctx) error { - return c.Status(http.StatusNotFound).JSON(fiber.Map { - "error": "Requested endpoint not found", - }) - }) -} diff --git a/api/routes/services.go b/api/routes/services.go index eb5cc25..3ab7469 100644 --- a/api/routes/services.go +++ b/api/routes/services.go @@ -1,48 +1,41 @@ package routes import ( - "database/sql" "log" "github.com/gofiber/fiber/v2" + "github.com/ngn13/website/api/database" + "github.com/ngn13/website/api/global" "github.com/ngn13/website/api/util" ) -func ServicesDb(db *sql.DB) { - _, err := db.Exec(` - CREATE TABLE IF NOT EXISTS services( - name TEXT NOT NULL UNIQUE, - desc TEXT NOT NULL, - url TEXT NOT NULL - ); - `) - - if err != nil { - log.Fatal("Error creating table: "+err.Error()) - } -} - func GetServices(c *fiber.Ctx) error { - var services []Service = []Service{} + var ( + services []global.Service = []global.Service{} + service global.Service + db *database.Type + err error + ) - rows, err := DB.Query("SELECT * FROM services") - if util.ErrorCheck(err, c) { - return util.ErrServer(c) - } + db = c.Locals("database").(*database.Type) - for rows.Next() { - var service Service - err := rows.Scan(&service.Name, &service.Desc, &service.Url) - if err != nil { - log.Println("Error scaning services row: "+err.Error()) - continue - } - services = append(services, service) - } - rows.Close() + rows, err := db.Sql.Query("SELECT * FROM services") + if util.ErrorCheck(err, c) { + return util.ErrServer(c) + } - return c.JSON(fiber.Map { - "error": "", - "result": services, - }) + for rows.Next() { + if err = rows.Scan(&service.Name, &service.Desc, &service.Url); err != nil { + log.Println("Error scaning services row: " + err.Error()) + continue + } + services = append(services, service) + } + + rows.Close() + + return c.JSON(fiber.Map{ + "error": "", + "result": services, + }) } diff --git a/api/util/utils.go b/api/util/utils.go index 334f5e1..b4c30d8 100644 --- a/api/util/utils.go +++ b/api/util/utils.go @@ -4,57 +4,66 @@ import ( "log" "math/rand" "net/http" + "strings" "github.com/gofiber/fiber/v2" ) -var charlist []rune = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") +func TitleToID(name string) string { + return strings.ToLower(strings.ReplaceAll(name, " ", "")) +} func CreateToken() string { - b := make([]rune, 20) - for i := range b { - b[i] = charlist[rand.Intn(len(charlist))] - } - - return string(b) + s := make([]byte, 32) + for i := 0; i < 32; i++ { + s[i] = byte(65 + rand.Intn(25)) + } + return string(s) } -func ErrorCheck(err error, c *fiber.Ctx) bool{ - if err != nil { - log.Printf("Server error: '%s' on %s\n", err, c.Path()) - return true - } +func ErrorCheck(err error, c *fiber.Ctx) bool { + if err != nil { + log.Printf("Server error: '%s' on %s\n", err, c.Path()) + return true + } - return false + return false } -func ErrorJSON(error string) fiber.Map{ - return fiber.Map { - "error": error, - } +func ErrorJSON(error string) fiber.Map { + return fiber.Map{ + "error": error, + } +} + +func GetIP(c *fiber.Ctx) string { + if c.Get("X-Real-IP") != "" { + return strings.Clone(c.Get("X-Real-IP")) + } + + return c.IP() } func ErrServer(c *fiber.Ctx) error { - return c.Status(http.StatusInternalServerError).JSON(ErrorJSON("Server error")) + return c.Status(http.StatusInternalServerError).JSON(ErrorJSON("Server error")) } func ErrExists(c *fiber.Ctx) error { - return c.Status(http.StatusConflict).JSON(ErrorJSON("Entry already exists")) + return c.Status(http.StatusConflict).JSON(ErrorJSON("Entry already exists")) } func ErrBadData(c *fiber.Ctx) error { - return c.Status(http.StatusBadRequest).JSON(ErrorJSON("Provided data is invalid")) + return c.Status(http.StatusBadRequest).JSON(ErrorJSON("Provided data is invalid")) } func ErrBadJSON(c *fiber.Ctx) error { - return c.Status(http.StatusBadRequest).JSON(ErrorJSON("Bad JSON data")) + return c.Status(http.StatusBadRequest).JSON(ErrorJSON("Bad JSON data")) } func ErrAuth(c *fiber.Ctx) error { - return c.Status(http.StatusUnauthorized).JSON(ErrorJSON("Authentication failed")) + return c.Status(http.StatusUnauthorized).JSON(ErrorJSON("Authentication failed")) } func NoError(c *fiber.Ctx) error { - return c.Status(http.StatusOK).JSON(ErrorJSON("")) + return c.Status(http.StatusOK).JSON(ErrorJSON("")) } - diff --git a/app/src/lib/header.svelte b/app/src/lib/header.svelte index 0337a2e..c76bbf1 100644 --- a/app/src/lib/header.svelte +++ b/app/src/lib/header.svelte @@ -1,9 +1,12 @@ - +

+

{subtitle}

diff --git a/app/src/routes/+page.svelte b/app/src/routes/+page.svelte index f2a82b2..fdc024c 100644 --- a/app/src/routes/+page.svelte +++ b/app/src/routes/+page.svelte @@ -80,7 +80,7 @@
-

v4.8

+

v4.9