Merge pull request #2 from CalebQ42/htmx
Complete transformation to htmx
This commit is contained in:
@@ -1 +1,2 @@
|
|||||||
darkstorm-server
|
darkstorm-server
|
||||||
|
test.sh
|
||||||
|
|||||||
@@ -1,28 +1,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/CalebQ42/darkstorm-server/internal/backend"
|
"github.com/CalebQ42/darkstorm-server/internal/backend"
|
||||||
"github.com/CalebQ42/darkstorm-server/internal/blog"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
blogTitle = "<h1 class='blog-title'><a href='https://darkstorm.tech/%v' onclick=\"return setToPath('%v')\" style='text-decoration:none'>%v</a></h1>"
|
|
||||||
blogAuthor = "<h4 class='blog-author'><i><b>By %v</b></i></h4>"
|
|
||||||
blogCreate = "<h5 class='blog-time'><i>Written on: %v</i></h5>"
|
|
||||||
blogMain = "<div class='blog'>%v</div>"
|
|
||||||
|
|
||||||
authorInfo = `
|
|
||||||
<h2 class='blog-author-info'>About the author:</h2>
|
|
||||||
<table><tr>
|
|
||||||
<td><img src="%v" alt="%v" class='author-pic'></td>
|
|
||||||
<td><h3 class="author-title">%v</h3>%v</td>
|
|
||||||
</tr></table>`
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func latestBlogsHandle(w http.ResponseWriter, r *http.Request) {
|
func latestBlogsHandle(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -40,9 +22,13 @@ func latestBlogsHandle(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
var out string
|
var out string
|
||||||
for _, b := range latest {
|
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) {
|
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", "", "")
|
sendContent(w, r, "Error getting page", "", "")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sendContent(w, r, blogElement(r.Context(), bl), bl.Title, bl.Favicon)
|
if r.Header.Get("Hx-Request") == "true" {
|
||||||
}
|
w.Write([]byte(bl.HTMX(blogApp, r.Context())))
|
||||||
|
|
||||||
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)
|
|
||||||
} else {
|
} 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)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ func filesRequest(w http.ResponseWriter, r *http.Request) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
pageContent = "<p>404 Not Found</p>"
|
pageContent = "<p>404 Not Found</p>"
|
||||||
w.WriteHeader(http.StatusNotFound)
|
// w.WriteHeader(http.StatusNotFound)
|
||||||
} else {
|
} else {
|
||||||
pageContent = "<p>Server error!</p>"
|
pageContent = "<p>Server error!</p>"
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
@@ -52,5 +52,9 @@ func filesRequest(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sendContent(w, r, pageContent, "Files", "")
|
if r.Header.Get("Hx-Request") == "true" {
|
||||||
|
w.Write([]byte(pageContent))
|
||||||
|
} else {
|
||||||
|
sendContent(w, r, pageContent, "Files", "")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ func (b *Backend) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
if r.Method == http.MethodOptions {
|
if r.Method == http.MethodOptions {
|
||||||
w.Header().Set("Access-Control-Allow-Methods", "*")
|
w.Header().Set("Access-Control-Allow-Methods", "*")
|
||||||
w.Header().Set("Access-Control-Allow-Credentials", "true")
|
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)
|
b.m.ServeHTTP(w, r)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package blog
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -13,6 +14,12 @@ import (
|
|||||||
"go.mongodb.org/mongo-driver/mongo"
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const authorInfo = `
|
||||||
|
<table><tr>
|
||||||
|
<td><img src="%v" alt="%v" class='author-pic'></td>
|
||||||
|
<td><h3 class="author-title">%v</h3>%v</td>
|
||||||
|
</tr></table>`
|
||||||
|
|
||||||
type Author struct {
|
type Author struct {
|
||||||
ID string `json:"id" bson:"_id"`
|
ID string `json:"id" bson:"_id"`
|
||||||
Name string `json:"name" bson:"name"`
|
Name string `json:"name" bson:"name"`
|
||||||
@@ -20,6 +27,10 @@ type Author struct {
|
|||||||
PicURL string `json:"picurl" bson:"picurl"`
|
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) {
|
func (b *BlogApp) AboutMe(ctx context.Context) (*Author, error) {
|
||||||
res := b.authCol.FindOne(ctx, bson.M{"_id": "caleb_gardner"})
|
res := b.authCol.FindOne(ctx, bson.M{"_id": "caleb_gardner"})
|
||||||
if res.Err() != nil {
|
if res.Err() != nil {
|
||||||
|
|||||||
+37
-1
@@ -3,6 +3,7 @@ package blog
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -15,6 +16,13 @@ import (
|
|||||||
"go.mongodb.org/mongo-driver/mongo/options"
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
blogTitle = "<h1 class='blog-title'><a hx-push-url='true' hx-get='/%v' hx-target='#content' href='/%v' style='text-decoration:none'>%v</a></h1>"
|
||||||
|
blogAuthor = "<h4 class='blog-author'><i><b>By %v</b></i></h4>"
|
||||||
|
blogCreate = "<h5 class='blog-time'><i>Written on: %v</i></h5>"
|
||||||
|
blogMain = "<div class='blog'>%v</div>"
|
||||||
|
)
|
||||||
|
|
||||||
type Blog struct {
|
type Blog struct {
|
||||||
ID string `json:"id" bson:"_id"`
|
ID string `json:"id" bson:"_id"`
|
||||||
Author string `json:"author" bson:"author"`
|
Author string `json:"author" bson:"author"`
|
||||||
@@ -27,6 +35,30 @@ type Blog struct {
|
|||||||
UpdateTime int64 `json:"updateTime" bson:"updateTime"`
|
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 += "<h2 class='blog-author-info'>About the author:</h2>" + auth.HTML()
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
func (b *BlogApp) ConvertBlog(blog *Blog) {
|
func (b *BlogApp) ConvertBlog(blog *Blog) {
|
||||||
if !blog.StaticPage {
|
if !blog.StaticPage {
|
||||||
blog.Blog = b.conv.HTMLConvert(blog.Blog)
|
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")
|
backend.ReturnError(w, http.StatusInternalServerError, "internal", "Server error")
|
||||||
return
|
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) {
|
func (b *BlogApp) createBlog(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|||||||
@@ -3,13 +3,24 @@ package blog
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/CalebQ42/darkstorm-server/internal/backend"
|
"github.com/CalebQ42/darkstorm-server/internal/backend"
|
||||||
"go.mongodb.org/mongo-driver/bson"
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
"go.mongodb.org/mongo-driver/mongo/options"
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
portfolioTitle = "<h2 class='portfolio-title'>%v</h2>"
|
||||||
|
portfolioLink = "<p class='portfolio-link'><a href='%v'>%v</a>"
|
||||||
|
portfolioLanguage = "<p class='portfolio-language'><b>%v</b>: %v</p>"
|
||||||
|
portfolioTech = "<p class='portfolio-tech'><b>Tech: </b>%v</p>"
|
||||||
|
portfolioDesc = "<p class='portfolio-description'>%v</p>"
|
||||||
|
)
|
||||||
|
|
||||||
type PortfolioProject struct {
|
type PortfolioProject struct {
|
||||||
Title string `json:"_id" bson:"_id"`
|
Title string `json:"_id" bson:"_id"`
|
||||||
Order int `json:"order" bson:"order"`
|
Order int `json:"order" bson:"order"`
|
||||||
@@ -22,10 +33,80 @@ type PortfolioProject struct {
|
|||||||
} `json:"language" bson:"language"`
|
} `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 = `
|
||||||
|
<p>Tech Filter:
|
||||||
|
<select
|
||||||
|
id='techSelect'
|
||||||
|
name='tech'
|
||||||
|
hx-get='http://localhost:2323/blog/portfolio'
|
||||||
|
hx-target='#projects'>
|
||||||
|
%v
|
||||||
|
</select>
|
||||||
|
</p>`
|
||||||
|
portfolioSelectorOption = "<option value='%v'%v>%v</option>"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p Portfolio) FullHTMX(ctx context.Context, blogApp *BlogApp, selectedTech string) string {
|
||||||
|
aboutMe := "<h1 class='about-me-header'>About Me</h1>"
|
||||||
|
if me, err := blogApp.AboutMe(ctx); err != nil {
|
||||||
|
aboutMe += "Error getting info about me :("
|
||||||
|
} else {
|
||||||
|
aboutMe += me.HTML()
|
||||||
|
}
|
||||||
|
aboutMe += "<h1 class='my-projects-header' style='margin-bottom:15px'>My Projects</h1>"
|
||||||
|
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) + "<div id='projects'>" + p.HTMX() + "</div>"
|
||||||
|
}
|
||||||
|
|
||||||
|
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{}
|
filter := bson.M{}
|
||||||
if languageFilter != "" {
|
if techFilter != "" {
|
||||||
filter["language.language"] = languageFilter
|
filter = bson.M{"technologies": techFilter}
|
||||||
}
|
}
|
||||||
res, err := b.portfolioCol.Find(ctx, filter, options.Find().SetSort(bson.M{"order": 1}))
|
res, err := b.portfolioCol.Find(ctx, filter, options.Find().SetSort(bson.M{"order": 1}))
|
||||||
if err != nil {
|
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) {
|
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 {
|
if err != nil {
|
||||||
backend.ReturnError(w, http.StatusInternalServerError, "internal", "Server Error")
|
backend.ReturnError(w, http.StatusInternalServerError, "internal", "Server Error")
|
||||||
return
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,23 +27,30 @@ var (
|
|||||||
back *backend.Backend
|
back *backend.Backend
|
||||||
blogApp *blog.BlogApp
|
blogApp *blog.BlogApp
|
||||||
webRoot *string
|
webRoot *string
|
||||||
|
testing *bool
|
||||||
)
|
)
|
||||||
|
|
||||||
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.")
|
||||||
addr := flag.String("addr", ":443", "Set listen address. Defaults to \":443\"")
|
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()
|
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")
|
log.Fatal("You must specify key directory. ex: darkstorm-server /etc/web-keys")
|
||||||
}
|
}
|
||||||
if *mongoURL == "" || *webRoot == "" {
|
if *mongoURL == "" || *webRoot == "" {
|
||||||
log.Fatal("SPECIFY MONGO AND WEB-ROOT OR I WILL DIE, OH NO, THEY'RE COMING FOR ME.... **DEATH NOISES**")
|
log.Fatal("SPECIFY MONGO AND WEB-ROOT OR I WILL DIE, OH NO, THEY'RE COMING FOR ME.... **DEATH NOISES**")
|
||||||
}
|
}
|
||||||
go func() {
|
if !*testing {
|
||||||
log.Println("error redirecting http traffice:",
|
go func() {
|
||||||
http.ListenAndServe(":80", http.RedirectHandler("https://darkstorm.tech", http.StatusPermanentRedirect)))
|
log.Println("error redirecting http traffice:",
|
||||||
}()
|
http.ListenAndServe(":80", http.RedirectHandler("https://darkstorm.tech", http.StatusPermanentRedirect)))
|
||||||
|
}()
|
||||||
|
}
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
setupMongo(*mongoURL)
|
setupMongo(*mongoURL)
|
||||||
setupBackend(mux)
|
setupBackend(mux)
|
||||||
@@ -52,20 +59,33 @@ func main() {
|
|||||||
Addr: *addr,
|
Addr: *addr,
|
||||||
Handler: mux,
|
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)
|
log.Println("webserver closed:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupMongo(uri string) {
|
func setupMongo(uri string) {
|
||||||
mongoCert, err := tls.LoadX509KeyPair(filepath.Join(flag.Arg(0), "mongo.pem"), filepath.Join(flag.Arg(0), "key.pem"))
|
if !*testing {
|
||||||
if err != nil {
|
mongoCert, err := tls.LoadX509KeyPair(filepath.Join(flag.Arg(0), "mongo.pem"), filepath.Join(flag.Arg(0), "key.pem"))
|
||||||
log.Fatal("error loading mongo keys:", err)
|
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},
|
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 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")),
|
swassistant.NewSWBackend(mongoClient.Database("swassistant")),
|
||||||
cdr.NewBackend(mongoClient.Database("cdr")),
|
cdr.NewBackend(mongoClient.Database("cdr")),
|
||||||
)
|
)
|
||||||
back.AddCorsAddress("https://darkstorm.tech")
|
if !*testing {
|
||||||
|
back.AddCorsAddress("https://darkstorm.tech")
|
||||||
|
} else {
|
||||||
|
back.AddCorsAddress("*")
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("error setting up backend:", err)
|
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) {
|
func setupWebsite(mux *http.ServeMux) {
|
||||||
url, _ := url.Parse("https://localhost:30000")
|
if !*testing {
|
||||||
mux.Handle("rpg.darkstorm.tech/", httputil.NewSingleHostReverseProxy(url))
|
url, _ := url.Parse("https://localhost:30000")
|
||||||
|
mux.Handle("rpg.darkstorm.tech/", httputil.NewSingleHostReverseProxy(url))
|
||||||
|
}
|
||||||
mux.HandleFunc("GET /files/{w...}", filesRequest)
|
mux.HandleFunc("GET /files/{w...}", filesRequest)
|
||||||
mux.HandleFunc("GET /portfolio", portfolioRequest)
|
mux.HandleFunc("GET /portfolio", portfolioRequest)
|
||||||
mux.HandleFunc("/", mainHandle)
|
mux.HandleFunc("/", mainHandle)
|
||||||
|
|||||||
+5
-52
@@ -1,69 +1,22 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"slices"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
portfolioSelector = "<p>Language Filter: <select id='langSelect' name='langSelect'>%v</select></p>"
|
|
||||||
portfolioSelectorOption = "<option value='%v'%v>%v</option>"
|
|
||||||
portfolioTitle = "<h2 class='portfolio-title'>%v</h2>"
|
|
||||||
portfolioLink = "<p class='portfolio-link'><a href='%v'>%v</a>"
|
|
||||||
portfolioLanguage = "<p class='portfolio-language'><b>%v</b>: %v</p>"
|
|
||||||
portfolioTech = "<p class='portfolio-tech'><b>Tech: </b>%v</p>"
|
|
||||||
portfolioDesc = "<p class='portfolio-description'>%v</p>"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func portfolioRequest(w http.ResponseWriter, r *http.Request) {
|
func portfolioRequest(w http.ResponseWriter, r *http.Request) {
|
||||||
selectedLang := r.URL.Query().Get("lang")
|
selectedTech := r.URL.Query().Get("tech")
|
||||||
proj, err := blogApp.Projects(r.Context(), selectedLang)
|
proj, err := blogApp.Projects(r.Context(), selectedTech)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("error getting portfolio projects:", err)
|
log.Println("error getting portfolio projects:", err)
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
sendContent(w, r, "Error getting portfolio", "", "")
|
sendContent(w, r, "Error getting portfolio", "", "")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
aboutMe := "<h1 class='about-me-header'>About Me</h1>"
|
if r.Header.Get("Hx-Request") == "true" {
|
||||||
if me, err := blogApp.AboutMe(r.Context()); err != nil {
|
w.Write([]byte(proj.FullHTMX(r.Context(), blogApp, selectedTech)))
|
||||||
aboutMe += "Error getting info about me :("
|
|
||||||
} else {
|
} else {
|
||||||
aboutMe += authorSection(me)
|
sendContent(w, r, proj.FullHTMX(r.Context(), blogApp, selectedTech), "Portfolio", "")
|
||||||
}
|
}
|
||||||
aboutMe += "<h1 class='my-projects-header' style='margin-bottom:15px'>My Projects</h1>"
|
|
||||||
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", "")
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user