diff --git a/.gitignore b/.gitignore
index e0fe3a4..e373643 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
darkstorm-server
+test.sh
diff --git a/blog.go b/blog.go
index fdc8ddc..91fbcf2 100644
--- a/blog.go
+++ b/blog.go
@@ -1,28 +1,10 @@
package main
import (
- "context"
- "fmt"
"log"
"net/http"
- "time"
"github.com/CalebQ42/darkstorm-server/internal/backend"
- "github.com/CalebQ42/darkstorm-server/internal/blog"
-)
-
-const (
- blogTitle = "
"
- blogAuthor = "By %v
"
- blogCreate = "Written on: %v
"
- blogMain = "%v
"
-
- authorInfo = `
-About the author:
-
-  |
- %v%v |
-
`
)
func latestBlogsHandle(w http.ResponseWriter, r *http.Request) {
@@ -40,9 +22,13 @@ func latestBlogsHandle(w http.ResponseWriter, r *http.Request) {
}
var out string
for _, b := range latest {
- out += blogElement(r.Context(), b)
+ out += b.HTMX(blogApp, r.Context())
+ }
+ if r.Header.Get("Hx-Request") == "true" {
+ w.Write([]byte(out))
+ } else {
+ sendContent(w, r, out, "", "")
}
- sendContent(w, r, out, "", "")
}
func blogHandle(w http.ResponseWriter, r *http.Request, blog string) {
@@ -58,33 +44,9 @@ func blogHandle(w http.ResponseWriter, r *http.Request, blog string) {
sendContent(w, r, "Error getting page", "", "")
return
}
- sendContent(w, r, blogElement(r.Context(), bl), bl.Title, bl.Favicon)
-}
-
-func blogElement(ctx context.Context, b *blog.Blog) (out string) {
- if b.StaticPage {
- return b.Blog
- }
- out = fmt.Sprintf(blogTitle, b.ID, b.ID, b.Title)
- auth, err := blogApp.GetAuthor(ctx, b)
- if err == nil {
- out += fmt.Sprintf(blogAuthor, auth.Name)
+ if r.Header.Get("Hx-Request") == "true" {
+ w.Write([]byte(bl.HTMX(blogApp, r.Context())))
} else {
- out += fmt.Sprintf(blogAuthor, "unknown")
+ sendContent(w, r, bl.HTMX(blogApp, r.Context()), bl.Title, bl.Favicon)
}
- cTime := time.Unix(b.CreateTime, 0).Format(time.DateOnly)
- if b.UpdateTime > b.CreateTime {
- out += fmt.Sprintf(blogCreate, cTime+"; Last updated on: "+time.Unix(b.UpdateTime, 0).Format(time.DateOnly))
- } else {
- out += fmt.Sprintf(blogCreate, cTime)
- }
- out += fmt.Sprintf(blogMain, b.Blog)
- if err == nil {
- out += authorSection(auth)
- }
- return
-}
-
-func authorSection(a *blog.Author) string {
- return fmt.Sprintf(authorInfo, a.PicURL, a.Name+"'s profile picture", a.Name, a.About)
}
diff --git a/files.go b/files.go
index 7f7f033..6488b8f 100644
--- a/files.go
+++ b/files.go
@@ -21,7 +21,7 @@ func filesRequest(w http.ResponseWriter, r *http.Request) {
if err != nil {
if os.IsNotExist(err) {
pageContent = "404 Not Found
"
- w.WriteHeader(http.StatusNotFound)
+ // w.WriteHeader(http.StatusNotFound)
} else {
pageContent = "Server error!
"
w.WriteHeader(http.StatusInternalServerError)
@@ -52,5 +52,9 @@ func filesRequest(w http.ResponseWriter, r *http.Request) {
return
}
}
- sendContent(w, r, pageContent, "Files", "")
+ if r.Header.Get("Hx-Request") == "true" {
+ w.Write([]byte(pageContent))
+ } else {
+ sendContent(w, r, pageContent, "Files", "")
+ }
}
diff --git a/internal/backend/darkstorm.go b/internal/backend/darkstorm.go
index 0c3cc5a..bfb22a5 100644
--- a/internal/backend/darkstorm.go
+++ b/internal/backend/darkstorm.go
@@ -104,7 +104,7 @@ func (b *Backend) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodOptions {
w.Header().Set("Access-Control-Allow-Methods", "*")
w.Header().Set("Access-Control-Allow-Credentials", "true")
- w.Header().Set("Access-Control-Allow-Headers", "Access-Control-Allow-Headers, Authorization, X-API-Key, Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers")
+ w.Header().Set("Access-Control-Allow-Headers", "*")
}
}
b.m.ServeHTTP(w, r)
diff --git a/internal/blog/author.go b/internal/blog/author.go
index da47f5f..b553190 100644
--- a/internal/blog/author.go
+++ b/internal/blog/author.go
@@ -3,6 +3,7 @@ package blog
import (
"context"
"encoding/json"
+ "fmt"
"log"
"net/http"
"strconv"
@@ -13,6 +14,12 @@ import (
"go.mongodb.org/mongo-driver/mongo"
)
+const authorInfo = `
+
+  |
+ %v%v |
+
`
+
type Author struct {
ID string `json:"id" bson:"_id"`
Name string `json:"name" bson:"name"`
@@ -20,6 +27,10 @@ type Author struct {
PicURL string `json:"picurl" bson:"picurl"`
}
+func (a Author) HTML() string {
+ return fmt.Sprintf(authorInfo, a.PicURL, a.Name+"'s profile picture", a.Name, a.About)
+}
+
func (b *BlogApp) AboutMe(ctx context.Context) (*Author, error) {
res := b.authCol.FindOne(ctx, bson.M{"_id": "caleb_gardner"})
if res.Err() != nil {
diff --git a/internal/blog/blog.go b/internal/blog/blog.go
index 8cddc1a..8057193 100644
--- a/internal/blog/blog.go
+++ b/internal/blog/blog.go
@@ -3,6 +3,7 @@ package blog
import (
"context"
"encoding/json"
+ "fmt"
"log"
"net/http"
"strconv"
@@ -15,6 +16,13 @@ import (
"go.mongodb.org/mongo-driver/mongo/options"
)
+const (
+ blogTitle = ""
+ blogAuthor = "By %v
"
+ blogCreate = "Written on: %v
"
+ blogMain = "%v
"
+)
+
type Blog struct {
ID string `json:"id" bson:"_id"`
Author string `json:"author" bson:"author"`
@@ -27,6 +35,30 @@ type Blog struct {
UpdateTime int64 `json:"updateTime" bson:"updateTime"`
}
+func (b *Blog) HTMX(blogApp *BlogApp, ctx context.Context) string {
+ if b.StaticPage {
+ return b.Blog
+ }
+ out := fmt.Sprintf(blogTitle, b.ID, b.ID, b.Title)
+ auth, err := blogApp.GetAuthor(ctx, b)
+ if err == nil {
+ out += fmt.Sprintf(blogAuthor, auth.Name)
+ } else {
+ out += fmt.Sprintf(blogAuthor, "unknown")
+ }
+ cTime := time.Unix(b.CreateTime, 0).Format(time.DateOnly)
+ if b.UpdateTime > b.CreateTime {
+ out += fmt.Sprintf(blogCreate, cTime+"; Last updated on: "+time.Unix(b.UpdateTime, 0).Format(time.DateOnly))
+ } else {
+ out += fmt.Sprintf(blogCreate, cTime)
+ }
+ out += fmt.Sprintf(blogMain, b.Blog)
+ if err == nil {
+ out += "About the author:
" + auth.HTML()
+ }
+ return out
+}
+
func (b *BlogApp) ConvertBlog(blog *Blog) {
if !blog.StaticPage {
blog.Blog = b.conv.HTMLConvert(blog.Blog)
@@ -95,7 +127,11 @@ func (b *BlogApp) reqBlog(w http.ResponseWriter, r *http.Request) {
backend.ReturnError(w, http.StatusInternalServerError, "internal", "Server error")
return
}
- json.NewEncoder(w).Encode(blog)
+ if r.Header.Get("Hx-Request") == "true" {
+ w.Write([]byte(blog.HTMX(b, r.Context())))
+ } else {
+ json.NewEncoder(w).Encode(blog)
+ }
}
func (b *BlogApp) createBlog(w http.ResponseWriter, r *http.Request) {
diff --git a/internal/blog/portfolio.go b/internal/blog/portfolio.go
index b9aca36..2fba534 100644
--- a/internal/blog/portfolio.go
+++ b/internal/blog/portfolio.go
@@ -3,13 +3,24 @@ package blog
import (
"context"
"encoding/json"
+ "fmt"
"net/http"
+ "slices"
+ "strings"
"github.com/CalebQ42/darkstorm-server/internal/backend"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
)
+const (
+ portfolioTitle = "%v
"
+ portfolioLink = "%v"
+ portfolioLanguage = "
%v: %v
"
+ portfolioTech = "Tech: %v
"
+ portfolioDesc = "%v
"
+)
+
type PortfolioProject struct {
Title string `json:"_id" bson:"_id"`
Order int `json:"order" bson:"order"`
@@ -22,10 +33,80 @@ type PortfolioProject struct {
} `json:"language" bson:"language"`
}
-func (b *BlogApp) Projects(ctx context.Context, languageFilter string) ([]PortfolioProject, error) {
+func (p PortfolioProject) HTMX() string {
+ out := fmt.Sprintf(portfolioTitle, p.Title)
+ out += fmt.Sprintf(portfolioLink, p.Repository, p.Repository)
+ for _, l := range p.Languages {
+ out += fmt.Sprintf(portfolioLanguage, l.Language, l.Dates)
+ }
+ out += fmt.Sprintf(portfolioTech, strings.Join(p.Technologies, ", "))
+ out += fmt.Sprintf(portfolioDesc, p.Description)
+ return out
+}
+
+type Portfolio []PortfolioProject
+
+const (
+ portfolioSelector = `
+Tech Filter:
+
+
`
+ portfolioSelectorOption = ""
+)
+
+func (p Portfolio) FullHTMX(ctx context.Context, blogApp *BlogApp, selectedTech string) string {
+ aboutMe := ""
+ if me, err := blogApp.AboutMe(ctx); err != nil {
+ aboutMe += "Error getting info about me :("
+ } else {
+ aboutMe += me.HTML()
+ }
+ aboutMe += ""
+ tech := make(map[string]struct{})
+ for i := range p {
+ for _, t := range p[i].Technologies {
+ tech[t] = struct{}{}
+ }
+ }
+ techKeys := make([]string, 0, len(tech))
+ for k := range tech {
+ techKeys = append(techKeys, k)
+ }
+ slices.Sort(techKeys)
+ var out string
+ if selectedTech == "" {
+ out = fmt.Sprintf(portfolioSelectorOption, "", " selected=true", "All")
+ } else {
+ out = fmt.Sprintf(portfolioSelectorOption, "", "", "All")
+ }
+ for _, k := range techKeys {
+ if selectedTech == strings.ToLower(k) {
+ out += fmt.Sprintf(portfolioSelectorOption, k, " selected=true", k)
+ } else {
+ out += fmt.Sprintf(portfolioSelectorOption, k, "", k)
+ }
+ }
+ return aboutMe + fmt.Sprintf(portfolioSelector, out) + "" + p.HTMX() + "
"
+}
+
+func (p Portfolio) HTMX() string {
+ out := ""
+ for _, proj := range p {
+ out += proj.HTMX()
+ }
+ return out
+}
+
+func (b *BlogApp) Projects(ctx context.Context, techFilter string) (Portfolio, error) {
filter := bson.M{}
- if languageFilter != "" {
- filter["language.language"] = languageFilter
+ if techFilter != "" {
+ filter = bson.M{"technologies": techFilter}
}
res, err := b.portfolioCol.Find(ctx, filter, options.Find().SetSort(bson.M{"order": 1}))
if err != nil {
@@ -37,10 +118,18 @@ func (b *BlogApp) Projects(ctx context.Context, languageFilter string) ([]Portfo
}
func (b *BlogApp) reqPortfolio(w http.ResponseWriter, r *http.Request) {
- folio, err := b.Projects(r.Context(), r.URL.Query().Get("lang"))
+ folio, err := b.Projects(r.Context(), r.URL.Query().Get("tech"))
if err != nil {
backend.ReturnError(w, http.StatusInternalServerError, "internal", "Server Error")
return
}
- json.NewEncoder(w).Encode(folio)
+ if r.Header.Get("Hx-Request") == "true" {
+ if r.URL.Query().Has("tech") {
+ w.Write([]byte(folio.HTMX()))
+ } else {
+ w.Write([]byte(folio.FullHTMX(r.Context(), b, r.URL.Query().Get("tech"))))
+ }
+ } else {
+ json.NewEncoder(w).Encode(folio)
+ }
}
diff --git a/main.go b/main.go
index 367fcdd..4a55683 100644
--- a/main.go
+++ b/main.go
@@ -27,23 +27,30 @@ var (
back *backend.Backend
blogApp *blog.BlogApp
webRoot *string
+ testing *bool
)
func main() {
mongoURL := flag.String("mongo", "", "Enables MongoDB usage for Darkstorm backend.")
webRoot = flag.String("web-root", "", "Sets root directory of web server.")
addr := flag.String("addr", ":443", "Set listen address. Defaults to \":443\"")
+ testing = flag.Bool("testing", false, "Start in testing mode. If you don't know what this is, don't use it.")
flag.Parse()
- if flag.NArg() != 1 {
+ if *testing {
+ *addr = ":4242"
+ }
+ if !*testing && flag.NArg() != 1 {
log.Fatal("You must specify key directory. ex: darkstorm-server /etc/web-keys")
}
if *mongoURL == "" || *webRoot == "" {
log.Fatal("SPECIFY MONGO AND WEB-ROOT OR I WILL DIE, OH NO, THEY'RE COMING FOR ME.... **DEATH NOISES**")
}
- go func() {
- log.Println("error redirecting http traffice:",
- http.ListenAndServe(":80", http.RedirectHandler("https://darkstorm.tech", http.StatusPermanentRedirect)))
- }()
+ if !*testing {
+ go func() {
+ log.Println("error redirecting http traffice:",
+ http.ListenAndServe(":80", http.RedirectHandler("https://darkstorm.tech", http.StatusPermanentRedirect)))
+ }()
+ }
mux := http.NewServeMux()
setupMongo(*mongoURL)
setupBackend(mux)
@@ -52,20 +59,33 @@ func main() {
Addr: *addr,
Handler: mux,
}
- err := serv.ListenAndServeTLS(filepath.Join(flag.Arg(0), "fullchain.pem"), filepath.Join(flag.Arg(0), "key.pem"))
+ var err error
+ if *testing {
+ err = serv.ListenAndServe()
+ } else {
+ err = serv.ListenAndServeTLS(filepath.Join(flag.Arg(0), "fullchain.pem"), filepath.Join(flag.Arg(0), "key.pem"))
+ }
log.Println("webserver closed:", err)
}
func setupMongo(uri string) {
- 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)
- }
- mongoClient, 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)
+ if !*testing {
+ 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)
+ }
+ mongoClient, 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)
+ }
+ } else {
+ var err error
+ mongoClient, err = mongo.Connect(context.Background(), options.Client().ApplyURI(uri))
+ if err != nil {
+ log.Fatal("error connecting to mongo:", err)
+ }
}
}
@@ -78,16 +98,28 @@ func setupBackend(mux *http.ServeMux) {
swassistant.NewSWBackend(mongoClient.Database("swassistant")),
cdr.NewBackend(mongoClient.Database("cdr")),
)
- back.AddCorsAddress("https://darkstorm.tech")
+ if !*testing {
+ back.AddCorsAddress("https://darkstorm.tech")
+ } else {
+ back.AddCorsAddress("*")
+ }
if err != nil {
log.Fatal("error setting up backend:", err)
}
- mux.Handle("api.darkstorm.tech/", back)
+ if !*testing {
+ mux.Handle("api.darkstorm.tech/", back)
+ } else {
+ go func() {
+ http.ListenAndServe(":2323", back)
+ }()
+ }
}
func setupWebsite(mux *http.ServeMux) {
- url, _ := url.Parse("https://localhost:30000")
- mux.Handle("rpg.darkstorm.tech/", httputil.NewSingleHostReverseProxy(url))
+ if !*testing {
+ url, _ := url.Parse("https://localhost:30000")
+ mux.Handle("rpg.darkstorm.tech/", httputil.NewSingleHostReverseProxy(url))
+ }
mux.HandleFunc("GET /files/{w...}", filesRequest)
mux.HandleFunc("GET /portfolio", portfolioRequest)
mux.HandleFunc("/", mainHandle)
diff --git a/portfolio.go b/portfolio.go
index b46c83e..765221b 100644
--- a/portfolio.go
+++ b/portfolio.go
@@ -1,69 +1,22 @@
package main
import (
- "fmt"
"log"
"net/http"
- "slices"
- "strings"
-)
-
-const (
- portfolioSelector = "Language Filter:
"
- portfolioSelectorOption = ""
- portfolioTitle = "%v
"
- portfolioLink = "%v"
- portfolioLanguage = "
%v: %v
"
- portfolioTech = "Tech: %v
"
- portfolioDesc = "%v
"
)
func portfolioRequest(w http.ResponseWriter, r *http.Request) {
- selectedLang := r.URL.Query().Get("lang")
- proj, err := blogApp.Projects(r.Context(), selectedLang)
+ selectedTech := r.URL.Query().Get("tech")
+ proj, err := blogApp.Projects(r.Context(), selectedTech)
if err != nil {
log.Println("error getting portfolio projects:", err)
w.WriteHeader(http.StatusInternalServerError)
sendContent(w, r, "Error getting portfolio", "", "")
return
}
- aboutMe := ""
- if me, err := blogApp.AboutMe(r.Context()); err != nil {
- aboutMe += "Error getting info about me :("
+ if r.Header.Get("Hx-Request") == "true" {
+ w.Write([]byte(proj.FullHTMX(r.Context(), blogApp, selectedTech)))
} else {
- aboutMe += authorSection(me)
+ sendContent(w, r, proj.FullHTMX(r.Context(), blogApp, selectedTech), "Portfolio", "")
}
- aboutMe += ""
- langs := make(map[string]struct{})
- out := ""
- for _, p := range proj {
- out += fmt.Sprintf(portfolioTitle, p.Title)
- out += fmt.Sprintf(portfolioLink, p.Repository, p.Repository)
- for _, l := range p.Languages {
- langs[l.Language] = struct{}{}
- out += fmt.Sprintf(portfolioLanguage, l.Language, l.Dates)
- }
- out += fmt.Sprintf(portfolioTech, strings.Join(p.Technologies, ", "))
- out += fmt.Sprintf(portfolioDesc, p.Description)
- }
- langKeys := make([]string, 0, len(langs))
- for k := range langs {
- langKeys = append(langKeys, k)
- }
- slices.Sort(langKeys)
- var tmp string
- if selectedLang == "" {
- tmp = fmt.Sprintf(portfolioSelectorOption, "", " selected=true", "All")
- } else {
- tmp = fmt.Sprintf(portfolioSelectorOption, "", "", "All")
- }
- for _, k := range langKeys {
- if selectedLang == strings.ToLower(k) {
- tmp += fmt.Sprintf(portfolioSelectorOption, strings.ToLower(k), " selected=true", k)
- } else {
- tmp += fmt.Sprintf(portfolioSelectorOption, strings.ToLower(k), "", k)
- }
- }
- out = aboutMe + fmt.Sprintf(portfolioSelector, tmp) + out
- sendContent(w, r, out, "Portfolio", "")
}