API redesign, better styling for the blog page

This commit is contained in:
ngn 2024-07-24 01:15:37 +03:00
parent 51b9214a5c
commit 6400f1fa95
16 changed files with 622 additions and 558 deletions

View File

@ -1,7 +1,12 @@
server: *.go routes/* util/* all: server
server: *.go routes/* util/* global/* database/*
go build -o server . go build -o server .
test: test:
FRONTEND_URL=http://localhost:5173/ PASSWORD=test ./server FRONTEND_URL=http://localhost:5173/ PASSWORD=test ./server
.PHONY: test format:
gofmt -s -w .
.PHONY: test format

55
api/database/database.go Normal file
View File

@ -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()
}

23
api/global/global.go Normal file
View File

@ -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
}

View File

@ -1,42 +1,78 @@
package main package main
import ( import (
"database/sql"
"log" "log"
"net/http"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/ngn13/website/api/database"
"github.com/ngn13/website/api/routes" "github.com/ngn13/website/api/routes"
) )
func CorsMiddleware(c *fiber.Ctx) error { func main() {
var (
app *fiber.App
db database.Type
err error
)
if err = db.Open("data.db"); err != nil {
log.Fatalf("Cannot access the database: %s", err.Error())
return
}
defer db.Close()
app = fiber.New(fiber.Config{
DisableStartupMessage: true,
})
app.Use("*", func(c *fiber.Ctx) error {
c.Set("Access-Control-Allow-Origin", "*") c.Set("Access-Control-Allow-Origin", "*")
c.Set("Access-Control-Allow-Headers", c.Set("Access-Control-Allow-Headers",
"Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With") "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.Set("Access-Control-Allow-Methods", "PUT, DELETE, GET")
c.Locals("database", &db)
return c.Next() return c.Next()
}
func main() {
app := fiber.New(fiber.Config{
DisableStartupMessage: true,
}) })
app.Use(CorsMiddleware)
db, err := sql.Open("sqlite3", "data.db") // index route
if err != nil { app.Get("/", func(c *fiber.Ctx) error {
log.Fatal("Cannot connect to the database: "+err.Error()) return c.Send([]byte("o/"))
} })
log.Println("Creating tables") // blog routes
routes.BlogDb(db) blog_routes := app.Group("/blog")
routes.ServicesDb(db) blog_routes.Get("/feed.atom", routes.GetAtomFeed)
routes.Setup(app, db) 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") log.Println("Starting web server at port 7001")
err = app.Listen("0.0.0.0:7001") if err = app.Listen("0.0.0.0:7001"); err != nil {
if err != nil {
log.Printf("Error starting the webserver: %s", err.Error()) log.Printf("Error starting the webserver: %s", err.Error())
} }
defer db.Close()
} }

View File

@ -1,6 +1,7 @@
package routes package routes
import ( import (
"log"
"net/http" "net/http"
"os" "os"
"strings" "strings"
@ -8,6 +9,8 @@ import (
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/mattn/go-sqlite3" "github.com/mattn/go-sqlite3"
"github.com/ngn13/website/api/database"
"github.com/ngn13/website/api/global"
"github.com/ngn13/website/api/util" "github.com/ngn13/website/api/util"
) )
@ -32,6 +35,8 @@ func Login(c *fiber.Ctx) error{
}) })
} }
log.Printf("New login from %s", util.GetIP(c))
return c.Status(http.StatusOK).JSON(fiber.Map{ return c.Status(http.StatusOK).JSON(fiber.Map{
"error": "", "error": "",
"token": Token, "token": Token,
@ -40,18 +45,28 @@ func Login(c *fiber.Ctx) error{
func Logout(c *fiber.Ctx) error { func Logout(c *fiber.Ctx) error {
Token = util.CreateToken() Token = util.CreateToken()
log.Printf("Logout from %s", util.GetIP(c))
return c.Status(http.StatusOK).JSON(fiber.Map{ return c.Status(http.StatusOK).JSON(fiber.Map{
"error": "", "error": "",
}) })
} }
func RemoveService(c *fiber.Ctx) error { func RemoveService(c *fiber.Ctx) error {
name := c.Query("name") var (
db *database.Type
name string
)
db = c.Locals("database").(*database.Type)
name = c.Query("name")
if name == "" { if name == "" {
util.ErrBadData(c) util.ErrBadData(c)
} }
_, err := DB.Exec("DELETE FROM services WHERE name = ?", name) _, err := db.Sql.Exec("DELETE FROM services WHERE name = ?", name)
if util.ErrorCheck(err, c) { if util.ErrorCheck(err, c) {
return util.ErrServer(c) return util.ErrServer(c)
} }
@ -60,7 +75,13 @@ func RemoveService(c *fiber.Ctx) error {
} }
func AddService(c *fiber.Ctx) error { func AddService(c *fiber.Ctx) error {
var service Service var (
service global.Service
db *database.Type
)
db = c.Locals("database").(*database.Type)
if c.BodyParser(&service) != nil { if c.BodyParser(&service) != nil {
return util.ErrBadJSON(c) return util.ErrBadJSON(c)
} }
@ -69,7 +90,7 @@ func AddService(c *fiber.Ctx) error {
return util.ErrBadData(c) return util.ErrBadData(c)
} }
rows, err := DB.Query("SELECT * FROM services WHERE name = ?", service.Name) rows, err := db.Sql.Query("SELECT * FROM services WHERE name = ?", service.Name)
if util.ErrorCheck(err, c) { if util.ErrorCheck(err, c) {
return util.ErrServer(c) return util.ErrServer(c)
} }
@ -81,7 +102,7 @@ func AddService(c *fiber.Ctx) error {
rows.Close() rows.Close()
_, err = DB.Exec( _, err = db.Sql.Exec(
"INSERT INTO services(name, desc, url) values(?, ?, ?)", "INSERT INTO services(name, desc, url) values(?, ?, ?)",
service.Name, service.Desc, service.Url, service.Name, service.Desc, service.Url,
) )
@ -94,12 +115,19 @@ func AddService(c *fiber.Ctx) error {
} }
func RemovePost(c *fiber.Ctx) error { func RemovePost(c *fiber.Ctx) error {
var id = c.Query("id") var (
db *database.Type
id string
)
db = c.Locals("database").(*database.Type)
id = c.Query("id")
if id == "" { if id == "" {
return util.ErrBadData(c) return util.ErrBadData(c)
} }
_, err := DB.Exec("DELETE FROM posts WHERE id = ?", id) _, err := db.Sql.Exec("DELETE FROM posts WHERE id = ?", id)
if util.ErrorCheck(err, c) { if util.ErrorCheck(err, c) {
return util.ErrServer(c) return util.ErrServer(c)
} }
@ -108,7 +136,12 @@ func RemovePost(c *fiber.Ctx) error{
} }
func AddPost(c *fiber.Ctx) error { func AddPost(c *fiber.Ctx) error {
var post Post var (
db *database.Type
post global.Post
)
db = c.Locals("database").(*database.Type)
post.Public = 1 post.Public = 1
if c.BodyParser(&post) != nil { if c.BodyParser(&post) != nil {
@ -120,9 +153,9 @@ func AddPost(c *fiber.Ctx) error{
} }
post.Date = time.Now().Format("02/01/06") post.Date = time.Now().Format("02/01/06")
post.ID = TitleToID(post.Title) post.ID = util.TitleToID(post.Title)
_, err := DB.Exec( _, err := db.Sql.Exec(
"INSERT INTO posts(id, title, author, date, content, public, vote) values(?, ?, ?, ?, ?, ?, ?)", "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.ID, post.Title, post.Author, post.Date, post.Content, post.Public, post.Vote,
) )

View File

@ -2,56 +2,76 @@ package routes
import ( import (
"database/sql" "database/sql"
"log" "fmt"
"net/http" "net/http"
"net/url" "net/url"
"fmt"
"os" "os"
"strings"
"time" "time"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/gorilla/feeds" "github.com/gorilla/feeds"
"github.com/ngn13/website/api/database"
"github.com/ngn13/website/api/global"
"github.com/ngn13/website/api/util" "github.com/ngn13/website/api/util"
) )
func BlogDb(db *sql.DB) { func PostFromRow(post *global.Post, rows *sql.Rows) error {
_, err := db.Exec(` err := rows.Scan(&post.ID, &post.Title, &post.Author, &post.Date, &post.Content, &post.Public, &post.Vote)
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 { if err != nil {
log.Fatal("Error creating table: "+err.Error()) return err
}
} }
func GetIP(c *fiber.Ctx) string { return nil
if c.Get("X-Real-IP") != "" {
return strings.Clone(c.Get("X-Real-IP"))
} }
return c.IP() func GetPostByID(db *database.Type, id string) (global.Post, string) {
var post global.Post = global.Post{}
post.Title = "NONE"
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 { func VoteStat(c *fiber.Ctx) error {
var id = c.Query("id") var (
db *database.Type
id string
)
db = c.Locals("database").(*database.Type)
id = c.Query("id")
if id == "" { if id == "" {
return util.ErrBadData(c) return util.ErrBadData(c)
} }
for i := 0; i < len(votelist); i++ { for i := 0; i < len(db.Votes); i++ {
if votelist[i].Client == GetIP(c) && votelist[i].Post == id { if db.Votes[i].Client == util.GetIP(c) && db.Votes[i].Post == id {
return c.JSON(fiber.Map{ return c.JSON(fiber.Map{
"error": "", "error": "",
"result": votelist[i].Status, "result": db.Votes[i].Status,
}) })
} }
} }
@ -60,25 +80,33 @@ func VoteStat(c *fiber.Ctx) error{
} }
func VoteSet(c *fiber.Ctx) error { func VoteSet(c *fiber.Ctx) error {
var id = c.Query("id") var (
var to = c.Query("to") id string
voted := false to string
voted bool
db *database.Type
)
db = c.Locals("database").(*database.Type)
id = c.Query("id")
to = c.Query("to")
voted = false
if id == "" || (to != "upvote" && to != "downvote") { if id == "" || (to != "upvote" && to != "downvote") {
return util.ErrBadData(c) return util.ErrBadData(c)
} }
for i := 0; i < len(votelist); i++ { for i := 0; i < len(db.Votes); i++ {
if votelist[i].Client == GetIP(c) && votelist[i].Post == id && votelist[i].Status == to { 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")) return c.Status(http.StatusForbidden).JSON(util.ErrorJSON("Client already voted"))
} }
if votelist[i].Client == GetIP(c) && votelist[i].Post == id && votelist[i].Status != to { if db.Votes[i].Client == util.GetIP(c) && db.Votes[i].Post == id && db.Votes[i].Status != to {
voted = true voted = true
} }
} }
post, msg := GetPostByID(id) post, msg := GetPostByID(db, id)
if msg != "" { if msg != "" {
return c.Status(http.StatusNotFound).JSON(util.ErrorJSON(msg)) return c.Status(http.StatusNotFound).JSON(util.ErrorJSON(msg))
} }
@ -97,33 +125,40 @@ func VoteSet(c *fiber.Ctx) error{
vote = vote + 1 vote = vote + 1
} }
_, err := DB.Exec("UPDATE posts SET vote = ? WHERE title = ?", vote, post.Title) _, err := db.Sql.Exec("UPDATE posts SET vote = ? WHERE title = ?", vote, post.Title)
if util.ErrorCheck(err, c) { if util.ErrorCheck(err, c) {
return util.ErrServer(c) return util.ErrServer(c)
} }
for i := 0; i < len(votelist); i++ { for i := 0; i < len(db.Votes); i++ {
if votelist[i].Client == GetIP(c) && votelist[i].Post == id && votelist[i].Status != to { if db.Votes[i].Client == util.GetIP(c) && db.Votes[i].Post == id && db.Votes[i].Status != to {
votelist[i].Status = to db.Votes[i].Status = to
return util.NoError(c) return util.NoError(c)
} }
} }
var entry = Vote{} var entry = global.Vote{}
entry.Client = GetIP(c) entry.Client = util.GetIP(c)
entry.Status = to entry.Status = to
entry.Post = id entry.Post = id
votelist = append(votelist, entry) db.Votes = append(db.Votes, entry)
return util.NoError(c) return util.NoError(c)
} }
func GetPost(c *fiber.Ctx) error { func GetPost(c *fiber.Ctx) error {
var id = c.Query("id") var (
id string
db *database.Type
)
id = c.Query("id")
db = c.Locals("database").(*database.Type)
if id == "" { if id == "" {
return util.ErrBadData(c) return util.ErrBadData(c)
} }
post, msg := GetPostByID(id) post, msg := GetPostByID(db, id)
if msg != "" { if msg != "" {
return c.Status(http.StatusNotFound).JSON(util.ErrorJSON(msg)) return c.Status(http.StatusNotFound).JSON(util.ErrorJSON(msg))
} }
@ -134,18 +169,23 @@ func GetPost(c *fiber.Ctx) error{
}) })
} }
func SumPost(c *fiber.Ctx) error { func SumPost(c *fiber.Ctx) error {
var posts []Post = []Post{} var (
rows, err := DB.Query("SELECT * FROM posts") posts []global.Post
post global.Post
db *database.Type
err error
)
db = c.Locals("database").(*database.Type)
rows, err := db.Sql.Query("SELECT * FROM posts")
if util.ErrorCheck(err, c) { if util.ErrorCheck(err, c) {
return util.ErrServer(c) return util.ErrServer(c)
} }
for rows.Next() { for rows.Next() {
var post Post err = PostFromRow(&post, rows)
err := PostFromRow(&post, rows)
if util.ErrorCheck(err, c) { if util.ErrorCheck(err, c) {
return util.ErrServer(c) return util.ErrServer(c)
@ -169,15 +209,19 @@ func SumPost(c *fiber.Ctx) error{
}) })
} }
func GetFeed() (*feeds.Feed, error){ func GetFeed(db *database.Type) (*feeds.Feed, error) {
var posts []Post = []Post{} var (
rows, err := DB.Query("SELECT * FROM posts") posts []global.Post
post global.Post
err error
)
rows, err := db.Sql.Query("SELECT * FROM posts")
if err != nil { if err != nil {
return nil, err return nil, err
} }
for rows.Next() { for rows.Next() {
var post Post
err := PostFromRow(&post, rows) err := PostFromRow(&post, rows)
if err != nil { if err != nil {
@ -192,7 +236,6 @@ func GetFeed() (*feeds.Feed, error){
} }
rows.Close() rows.Close()
blogurl, err := url.JoinPath(os.Getenv("FRONTEND_URL"), "/blog") blogurl, err := url.JoinPath(os.Getenv("FRONTEND_URL"), "/blog")
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create the blog URL: %s", err.Error()) return nil, fmt.Errorf("failed to create the blog URL: %s", err.Error())
@ -231,7 +274,7 @@ func GetFeed() (*feeds.Feed, error){
} }
func GetAtomFeed(c *fiber.Ctx) error { func GetAtomFeed(c *fiber.Ctx) error {
feed, err := GetFeed() feed, err := GetFeed(c.Locals("database").(*database.Type))
if util.ErrorCheck(err, c) { if util.ErrorCheck(err, c) {
return util.ErrServer(c) return util.ErrServer(c)
} }
@ -246,7 +289,7 @@ func GetAtomFeed(c *fiber.Ctx) error {
} }
func GetRSSFeed(c *fiber.Ctx) error { func GetRSSFeed(c *fiber.Ctx) error {
feed, err := GetFeed() feed, err := GetFeed(c.Locals("database").(*database.Type))
if util.ErrorCheck(err, c) { if util.ErrorCheck(err, c) {
return util.ErrServer(c) return util.ErrServer(c)
} }
@ -261,7 +304,7 @@ func GetRSSFeed(c *fiber.Ctx) error {
} }
func GetJSONFeed(c *fiber.Ctx) error { func GetJSONFeed(c *fiber.Ctx) error {
feed, err := GetFeed() feed, err := GetFeed(c.Locals("database").(*database.Type))
if util.ErrorCheck(err, c) { if util.ErrorCheck(err, c) {
return util.ErrServer(c) return util.ErrServer(c)
} }

View File

@ -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"`
}

View File

@ -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",
})
})
}

View File

@ -1,44 +1,37 @@
package routes package routes
import ( import (
"database/sql"
"log" "log"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/ngn13/website/api/database"
"github.com/ngn13/website/api/global"
"github.com/ngn13/website/api/util" "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 { 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") db = c.Locals("database").(*database.Type)
rows, err := db.Sql.Query("SELECT * FROM services")
if util.ErrorCheck(err, c) { if util.ErrorCheck(err, c) {
return util.ErrServer(c) return util.ErrServer(c)
} }
for rows.Next() { for rows.Next() {
var service Service if err = rows.Scan(&service.Name, &service.Desc, &service.Url); err != nil {
err := rows.Scan(&service.Name, &service.Desc, &service.Url)
if err != nil {
log.Println("Error scaning services row: " + err.Error()) log.Println("Error scaning services row: " + err.Error())
continue continue
} }
services = append(services, service) services = append(services, service)
} }
rows.Close() rows.Close()
return c.JSON(fiber.Map{ return c.JSON(fiber.Map{

View File

@ -4,19 +4,21 @@ import (
"log" "log"
"math/rand" "math/rand"
"net/http" "net/http"
"strings"
"github.com/gofiber/fiber/v2" "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) func CreateToken() string {
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 { func ErrorCheck(err error, c *fiber.Ctx) bool {
@ -34,6 +36,14 @@ func ErrorJSON(error string) fiber.Map{
} }
} }
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 { func ErrServer(c *fiber.Ctx) error {
return c.Status(http.StatusInternalServerError).JSON(ErrorJSON("Server error")) return c.Status(http.StatusInternalServerError).JSON(ErrorJSON("Server error"))
} }
@ -57,4 +67,3 @@ func ErrAuth(c *fiber.Ctx) error {
func NoError(c *fiber.Ctx) error { func NoError(c *fiber.Ctx) error {
return c.Status(http.StatusOK).JSON(ErrorJSON("")) return c.Status(http.StatusOK).JSON(ErrorJSON(""))
} }

View File

@ -1,9 +1,12 @@
<script></script> <script>
export let subtitle = ""
</script>
<header> <header>
<h1> <h1>
<slot></slot> <slot></slot>
</h1> </h1>
<h4><c>{subtitle}</c></h4>
</header> </header>
<style> <style>
@ -20,9 +23,19 @@ h1 {
font-weight: 900; font-weight: 900;
font-size: 5.5vw; font-size: 5.5vw;
padding: 120px; padding: 120px;
padding-bottom: 0;
text-align: center; text-align: center;
color: white; color: white;
text-shadow: var(--text-shadow); text-shadow: var(--text-shadow);
text-size-adjust: 80%; text-size-adjust: 80%;
} }
h4 {
padding-bottom: 120px;
font-weight: 600;
font-size: 2.2vw;
text-align: center;
color: white;
text-size-adjust: 80%;
}
</style> </style>

View File

@ -80,7 +80,7 @@
</main> </main>
<div class="version"> <div class="version">
<p>v4.8</p> <p>v4.9</p>
</div> </div>
<style> <style>

View File

@ -99,7 +99,7 @@
<link href="/markdown.css" rel="stylesheet"> <link href="/markdown.css" rel="stylesheet">
</svelte:head> </svelte:head>
<Header> <Header subtitle="{data.author} | {data.date}">
{data.title} {data.title}
</Header> </Header>
@ -107,9 +107,10 @@
<audio bind:this={audio} preload="auto"> <audio bind:this={audio} preload="auto">
<source src="/click.wav" type="audio/mpeg" /> <source src="/click.wav" type="audio/mpeg" />
</audio> </audio>
<div class="header"> <div class="content markdown-body">
<p><b>Author:</b> {data.author} <b>| Date:</b> {data.date}</p> {@html sanitized}
<div> </div>
<div class="votes">
<button on:click={async ()=>{upvote()}} class="{upvote_status}"> <button on:click={async ()=>{upvote()}} class="{upvote_status}">
<i class="nf nf-md-arrow_up_bold"></i> <i class="nf nf-md-arrow_up_bold"></i>
</button> </button>
@ -118,30 +119,22 @@
<i class="nf nf-md-arrow_down_bold"></i> <i class="nf nf-md-arrow_down_bold"></i>
</button> </button>
</div> </div>
</div>
<div class="content markdown-body">
{@html sanitized}
</div>
</main> </main>
<style> <style>
p {
font-size: 30px;
}
main { main {
padding: 50px 10% 50px 10%; padding: 50px 10% 50px 10%;
color: white; color: white;
display: flex; display: flex;
flex-direction: column; flex-direction: row;
justify-content: center; align-items: start;
} }
.content { .content {
padding: 50px; padding: 35px;
background: var(--dark-four); background: var(--dark-four);
border-radius: 0 0 var(--radius) var(--radius); border-radius: var(--radius);
box-shadow: var(--box-shadow); box-shadow: var(--box-shadow);
} }
@ -152,38 +145,28 @@ main {
} }
.content { .content {
padding: 30px;
background: var(--dark-four); background: var(--dark-four);
border-radius: 0 0 var(--radius) var(--radius); border-radius: 0 0 var(--radius) var(--radius);
box-shadow: var(--box-shadow); box-shadow: var(--box-shadow);
} }
} }
.header { .votes {
font-family: Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New, monospace;
background: var(--dark-two);
border-radius: var(--radius) var(--radius) 0 0;
padding: 30px;
box-shadow: var(--box-shadow);
display: flex; display: flex;
align-items: center; flex-direction: column;
justify-content: space-between;
}
.header div{
display: flex;
flex-direction: row;
text-align: center; text-align: center;
text-shadow: var(--text-shadow); text-shadow: var(--text-shadow);
gap: 10px; gap: 10px;
margin-top: 10px; padding: 10px;
margin-left: 10px;
} }
.header p { .votes p {
font-size: 20px; font-size: 25px;
color: var(--dark-six);
} }
.header div button{ .votes button{
display: flex; display: flex;
flex-direction: row; flex-direction: row;
gap: 10px; gap: 10px;
@ -192,14 +175,10 @@ main {
border: none; border: none;
font-size: 30px; font-size: 30px;
cursor: pointer; cursor: pointer;
color: white; color: var(--dark-six);
} }
.header div p { .votes button:hover {
font-size: 25px;
}
.header div button:hover {
animation-name: colorAnimation; animation-name: colorAnimation;
animation-iteration-count: infinite; animation-iteration-count: infinite;
animation-duration: 10s; animation-duration: 10s;

View File

@ -63,8 +63,8 @@ tr,th,td{
} }
td,th{ td,th{
border: solid 1px white; border: solid 1px var(--dark-fife);
padding: 10px; padding: 16px;
} }
th { th {
@ -75,10 +75,6 @@ th {
} }
code { code {
background: var(--dark-two);
color: white;
border-radius: var(--radius);
text-shadow: var(--text-shadow);
word-wrap: break-word; word-wrap: break-word;
white-space: pre-wrap; white-space: pre-wrap;
word-break: break-word; word-break: break-word;

View File

@ -7,6 +7,7 @@
--dark-three: #121212; --dark-three: #121212;
--dark-four: #101010; --dark-four: #101010;
--dark-fife: #3a3b3c; --dark-fife: #3a3b3c;
--dark-six: #C0C0C0;
--radius: 8px; --radius: 8px;
/* /*
old shadow animation old shadow animation
@ -37,8 +38,8 @@ body {
} }
::-webkit-scrollbar { ::-webkit-scrollbar {
border-radius: 10px; border-radius: 5px;
width: 10px; width: 1px;
} }
::-webkit-scrollbar-track { ::-webkit-scrollbar-track {

View File

@ -691,7 +691,7 @@
} }
.markdown-body span.float-right>span { .markdown-body span.float-right>span {
displaydebian: block; display: block;
margin: 13px auto 0; margin: 13px auto 0;
overflow: hidden; overflow: hidden;
text-align: right; text-align: right;
@ -703,7 +703,7 @@
margin: 0; margin: 0;
font-size: 85%; font-size: 85%;
white-space: break-spaces; white-space: break-spaces;
background-color: var(--dark-two); background: var(--dark-two);
border-radius: 6px; border-radius: 6px;
} }
@ -745,7 +745,7 @@
.markdown-body .highlight pre, .markdown-body .highlight pre,
.markdown-body pre { .markdown-body pre {
padding: 16px; padding: 16px;
overflow: scroll; overflow: auto;
font-size: 85%; font-size: 85%;
line-height: 1.45; line-height: 1.45;
background-color: var(--dark-two); background-color: var(--dark-two);