Working on blog stuff

This commit is contained in:
Caleb Gardner
2024-06-17 07:28:33 -05:00
parent 7b15aab7ec
commit fdd8d49055
8 changed files with 388 additions and 7 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2021 Caleb Gardner
Copyright (c) 2024 Caleb Gardner
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+2
View File
@@ -80,6 +80,8 @@ func (b *Backend) ParseHeader(r *http.Request) (*ParsedHeader, error) {
// If the check if failed, ReturnError will be called and the returned *ParsedHeader will be nil.
// If token is present but invalid, no error will be returned just ParsedHeader.User will be nil.
// The error return will only be populated on "internal" errors and should *probably* be logged.
//
// This function does not check the Key's appID so after calling VerifyHeader it's recommended to check the Key's appID.
func (b *Backend) VerifyHeader(w http.ResponseWriter, r *http.Request, keyPerm string, allowManagementKey bool) (*ParsedHeader, error) {
hdr, err := b.ParseHeader(r)
if hdr == nil || hdr.Key == nil {
+83
View File
@@ -0,0 +1,83 @@
# Blog module
A simple blog module for darkstorm-backend.
## Requests
### Author info
> GET /author/{authorID}
```json
```
### Blog
#### Specific blog
> GET /blog/{blogID}
Return:
```json
{
id: "blogID",
createTime: 0, // creation time in Unix format
updateTime: 0, // last update time in Unix format
author: "authorID",
favicon: "favicon url",
title: "blog title",
blog: "blog", // blog will have been converted to HTML
}
```
#### Latest blogs
> GET /blog?page=0
Will return up to 5 blogs. `page` query is optional (implies 0 if not set).
Return:
```json
{
num: 1, // Number of returned results, returns up to 5 results
blogs: [
{
id: "blogID",
createTime: 0, // creation time in Unix format
updateTime: 0, // last update time in Unix format
author: "authorID",
favicon: "favicon url",
title: "blog title",
blog: "blog", // blog will have been converted to HTML
}
...
]
}
```
#### Blog List
> GET /blog/list?page=0
Will return up to 50 IDs. `page` query is optional (implies 0 if not set).
Return:
```json
{
num: 1, // Number of returned results, returns up to 50 results
blogList: [
{
id: "blogID",
createTime: 0, // Unix format
},
{
id: "blogID",
createTime: 0, // Unix format
},
...
]
}
```
+54
View File
@@ -0,0 +1,54 @@
package blog
import (
"context"
"log"
"net/http"
"github.com/CalebQ42/darkstorm-server/internal/backend"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
)
type Author struct {
ID string `json:"id" bson:"_id"`
About string `json:"about" bson:"about"`
PicURL string `json:"picurl" bson:"picurl"`
}
func (b *BlogApp) AboutCaleb() (*Author, error) {
res := b.authCol.FindOne(context.Background(), bson.M{"_id": "caleb_gardner"})
if res.Err() != nil {
log.Println("error getting about me:", res.Err())
if res.Err() == mongo.ErrNoDocuments {
return nil, backend.ErrNotFound
}
return nil, res.Err()
}
var aboutMe Author
err := res.Decode(&aboutMe)
if err != nil {
log.Println("error decoding about me:", res)
return nil, err
}
return &aboutMe, nil
}
func (b *BlogApp) GetAuthorInfo(w http.ResponseWriter, r *http.Request) {
}
func (b *BlogApp) SetAuthorInfo(w http.ResponseWriter, r *http.Request) {
hdr, err := b.back.VerifyHeader(w, r, "managment", true)
if hdr == nil {
if err != nil {
log.Println("error verifying apiKey:", err)
}
return
}
if hdr.Key.AppID != "blog" {
backend.ReturnError(w, http.StatusUnauthorized, "invalidKey", "Application not authorized")
return
}
}
+173
View File
@@ -0,0 +1,173 @@
package blog
import (
"context"
"encoding/json"
"log"
"net/http"
"strconv"
"github.com/CalebQ42/darkstorm-server/internal/backend"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
type Blog struct {
ID string `json:"id" bson:"_id"`
Author string `json:"author" bson:"author"`
Favicon string `json:"favicon" bson:"favicon"`
Title string `json:"title" bson:"title"`
Blog string `json:"blog" bson:"blog"`
CreateTime int `json:"createTime" bson:"createTime"`
UpdateTime int `json:"updateTime" bson:"updateTime"`
}
func (b *Blog) ConvertBlog() {
//TODO: parse BBCode/Markdown from blog
//b.Blog = bbCodeConvert(b.Blog)
}
func (b *BlogApp) GetAuthor(blog *Blog) (*Author, error) {
res := b.authCol.FindOne(context.Background(), bson.M{"_id": blog.Author})
if res.Err() != nil {
if res.Err() == mongo.ErrNoDocuments {
return nil, backend.ErrNotFound
}
return nil, res.Err()
}
var author Author
err := res.Decode(&author)
return &author, err
}
func (b *BlogApp) GetBlog(ID string) (*Blog, error) {
res := b.blogCol.FindOne(context.Background(), bson.M{"_id": ID})
if res.Err() != nil {
if res.Err() == mongo.ErrNoDocuments {
return nil, backend.ErrNotFound
}
return nil, res.Err()
}
var blog Blog
err := res.Decode(blog)
if err != nil {
return nil, err
}
blog.ConvertBlog()
return &blog, nil
}
func (b *BlogApp) Blog(w http.ResponseWriter, r *http.Request) {
blogID := r.PathValue("blogID")
if blogID == "" {
backend.ReturnError(w, http.StatusBadRequest, "badRequest", "Must provide a blogID")
return
}
blog, err := b.GetBlog(blogID)
if err != nil {
if err == backend.ErrNotFound {
backend.ReturnError(w, http.StatusNotFound, "notFound", "Not blog found with the given ID")
return
}
log.Println("error getting blog:", err)
backend.ReturnError(w, http.StatusInternalServerError, "internal", "Server error")
return
}
json.NewEncoder(w).Encode(blog)
}
func (b *BlogApp) GetLatestBlogs(page int64) ([]Blog, error) {
res, err := b.blogCol.Find(context.Background(), bson.M{}, options.Find().
SetSort(bson.M{"createTime": 1}).
SetLimit(5).
SetSkip(page*5))
if err != nil {
if err == mongo.ErrNoDocuments {
return nil, backend.ErrNotFound
}
return nil, err
}
var out []Blog
err = res.All(context.Background(), &out)
if err != nil {
return nil, err
}
for i := range out {
out[i].ConvertBlog()
}
return out, nil
}
func (b *BlogApp) LatestBlogs(w http.ResponseWriter, r *http.Request) {
var page int
var err error
pagQuery := r.URL.Query().Get("page")
if pagQuery != "" {
page, err = strconv.Atoi(pagQuery)
if err != nil {
page = 0
}
}
blogs, err := b.GetLatestBlogs(int64(page))
if err != nil && err != backend.ErrNotFound {
backend.ReturnError(w, http.StatusInternalServerError, "internal", "internal error")
return
}
var ret struct {
Blogs []Blog `json:"blogs"`
Num int `json:"num"`
}
ret.Num = len(blogs)
ret.Blogs = blogs
json.NewEncoder(w).Encode(ret)
}
type BlogListResult struct {
ID string `json:"id" bson:"_id"`
CreateTime int `json:"createTime" bson:"createTime"`
}
func (b *BlogApp) GetBlogList(page int64) ([]BlogListResult, error) {
res, err := b.blogCol.Find(context.Background(), bson.M{}, options.Find().
SetProjection(bson.M{"_id": 1, "createTime": 1}).
SetSort(bson.M{"createTime": 1}).
SetLimit(50).
SetSkip(page*50))
if err != nil {
if err == mongo.ErrNoDocuments {
return nil, backend.ErrNotFound
}
return nil, err
}
var out []BlogListResult
err = res.All(context.Background(), &out)
if err != nil {
return nil, err
}
return out, nil
}
func (b *BlogApp) BlogList(w http.ResponseWriter, r *http.Request) {
var page int
var err error
pagQuery := r.URL.Query().Get("page")
if pagQuery != "" {
page, err = strconv.Atoi(pagQuery)
if err != nil {
page = 0
}
}
blogList, err := b.GetBlogList(int64(page))
if err != nil && err != backend.ErrNotFound {
backend.ReturnError(w, http.StatusInternalServerError, "internal", "internal error")
return
}
var ret struct {
BlogList []BlogListResult `json:"blogList"`
Num int `json:"num"`
}
ret.Num = len(blogList)
ret.BlogList = blogList
json.NewEncoder(w).Encode(ret)
}
+27
View File
@@ -0,0 +1,27 @@
package blog
import (
"net/http"
"github.com/CalebQ42/darkstorm-server/internal/backend"
"go.mongodb.org/mongo-driver/mongo"
)
type BlogApp struct {
back *backend.Backend
blogCol *mongo.Collection
authCol *mongo.Collection
}
func NewBlogApp(b *backend.Backend, db *mongo.Database, mux *http.ServeMux) *BlogApp {
out := &BlogApp{
back: b,
blogCol: db.Collection("blog"),
authCol: db.Collection("author"),
}
// setup mux
mux.HandleFunc("GET /blog/", out.LatestBlogs)
mux.HandleFunc("GET /blog/{blogID}", out.Blog)
//TODO
return out
}
+39 -6
View File
@@ -1,22 +1,55 @@
package main
import (
"context"
"crypto/tls"
"flag"
"log"
"net/http"
"path/filepath"
"github.com/CalebQ42/darkstorm-server/internal/blog"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
var (
mongoClient *mongo.Client
blogApp *blog.BlogApp
)
func main() {
mongoURL := flag.String("mongo", "", "Enables MongoDB usage for darkstorm-backend.")
mongoURL := flag.String("mongo", "", "Enables MongoDB usage for Darkstorm backend.")
webRoot := flag.String("web-root", "", "Sets root directory of web server.")
flag.Parse()
if flag.NArg() != 1 {
log.Fatal("You must specify key directory. ex: darkstorm-server /etc/web-keys")
}
if *mongoURL != "" {
}
mongoCert, err := tls.LoadX509KeyPair(flag.Arg(0)+"mongo.pem", flag.Arg(0)+"key.pem")
if err != nil {
if *mongoURL == "" || *webRoot == "" {
log.Fatal("SPECIFY MONGO AND WEB-ROOT OR I WILL DIE (Death noises).")
}
mux := http.NewServeMux()
mongoClient = setupMongo(*mongoURL)
setupBackend(mux)
setupWebsite(mux, *webRoot)
http.ListenAndServeTLS(":443", filepath.Join(flag.Arg(0), "cert.pem"), filepath.Join(flag.Arg(0), "key.pem"), mux)
}
func setupMongo(uri string) *mongo.Client {
mongoCert, err := tls.LoadX509KeyPair(filepath.Join(flag.Arg(0), "mongo.pem"), filepath.Join(flag.Arg(0)+"key.pem"))
if err != nil {
log.Fatal("error loading mongo keys:", err)
}
client, err := mongo.Connect(context.Background(), options.Client().ApplyURI(uri).SetTLSConfig(&tls.Config{
Certificates: []tls.Certificate{mongoCert},
}))
if err != nil {
log.Fatal("error connecting to mongo:", err)
}
return client
}
func setupWebsite(mux *http.ServeMux, root string) {}
func setupBackend(mux *http.ServeMux) {
}
+9
View File
@@ -0,0 +1,9 @@
package main
import (
"go.mongodb.org/mongo-driver/mongo"
)
func portfolio(client *mongo.Client) {
//TODO
}