Working on blog stuff
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
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) {
|
||||
}
|
||||
|
||||
@@ -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