Working on blog stuff
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -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 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.
|
// 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.
|
// 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) {
|
func (b *Backend) VerifyHeader(w http.ResponseWriter, r *http.Request, keyPerm string, allowManagementKey bool) (*ParsedHeader, error) {
|
||||||
hdr, err := b.ParseHeader(r)
|
hdr, err := b.ParseHeader(r)
|
||||||
if hdr == nil || hdr.Key == nil {
|
if hdr == nil || hdr.Key == nil {
|
||||||
|
|||||||
@@ -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
|
||||||
|
},
|
||||||
|
...
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -1,22 +1,55 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"flag"
|
"flag"
|
||||||
"log"
|
"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() {
|
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.")
|
webRoot := flag.String("web-root", "", "Sets root directory of web server.")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
if flag.NArg() != 1 {
|
if flag.NArg() != 1 {
|
||||||
log.Fatal("You must specify key directory. ex: darkstorm-server /etc/web-keys")
|
log.Fatal("You must specify key directory. ex: darkstorm-server /etc/web-keys")
|
||||||
}
|
}
|
||||||
if *mongoURL != "" {
|
if *mongoURL == "" || *webRoot == "" {
|
||||||
}
|
log.Fatal("SPECIFY MONGO AND WEB-ROOT OR I WILL DIE (Death noises).")
|
||||||
mongoCert, err := tls.LoadX509KeyPair(flag.Arg(0)+"mongo.pem", flag.Arg(0)+"key.pem")
|
|
||||||
if err != nil {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
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) {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
|
)
|
||||||
|
|
||||||
|
func portfolio(client *mongo.Client) {
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user