small pixel drawing of a pufferfish j3s.sh

main.go

package main

import (
	"bytes"
	"fmt"
	"html/template"
	"io"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	"filippo.io/age"
	"filippo.io/age/armor"
	"git.j3s.sh/j3s.sh/feed"
	"github.com/SlyMarbo/rss"
)

// templateData is a mega-struct that gets
// passed to every single template - put whatever
// you want in it tbh.
//
// "data" is a global object that contains arbitrary
// data for use in templates. it's useful for it to be
// global since it may need to be available in arbitrary
// contexts. maybe that sucks. but idfk!
type templateData struct {
	FriendFeeds []rss.Feed
}

var data templateData

// the populate function populates the global "data" variable
// with ... data. with which to pass into templates.
func (t *templateData) populate() {
	t.FriendFeeds = feed.FetchFriendFeeds()
}

func main() {
	data.populate()

	fs := http.FileServer(http.Dir("./static"))
	http.Handle("/static/", http.StripPrefix("/static/", fs))

	http.HandleFunc("/age.html", serveAge)
	http.HandleFunc("/ip", ipHandler)
	http.HandleFunc("/review/", babyHandler)
	http.HandleFunc("/thought/", babyHandler)
	http.HandleFunc("/", serveTemplate)

	log.Println("listening on :4666 tbh")
	err := http.ListenAndServe(":4666", nil)
	if err != nil {
		log.Fatal(err)
	}
}

func serveTemplate(w http.ResponseWriter, r *http.Request) {
	lp := filepath.Join("templates", "layout.html")
	if r.URL.Path == "/" {
		r.URL.Path = "index.html"
		// TODO: add some arbitrary refresh handler for fetchFriendPosts
	}
	fp := filepath.Join("templates", filepath.Clean(r.URL.Path))

	info, err := os.Stat(fp)
	if err != nil {
		if os.IsNotExist(err) {
			http.NotFound(w, r)
			return
		}
	}

	if info.IsDir() {
		http.NotFound(w, r)
		return
	}

	tmpl, err := template.ParseFiles(lp, fp)
	if err != nil {
		log.Println(err.Error())
		http.Error(w, http.StatusText(500), 500)
		return
	}

	err = tmpl.ExecuteTemplate(w, "layout", data)
	if err != nil {
		log.Println(err.Error())
		http.Error(w, http.StatusText(500), 500)
	}
}

func babyHandler(w http.ResponseWriter, r *http.Request) {
	lp := filepath.Join("templates", "simple-layout.html")
	fp := filepath.Join(strings.TrimPrefix(filepath.Clean(r.URL.Path), "/"))
	// load the named .txt file for processing
	txt := strings.ReplaceAll(fp, ".html", ".txt")

	info, err := os.Stat(txt)
	if err != nil {
		if os.IsNotExist(err) {
			http.NotFound(w, r)
			return
		}
	}

	if info.IsDir() {
		http.NotFound(w, r)
		return
	}

	content, err := os.ReadFile(txt)
	if err != nil {
		log.Println(err.Error())
		http.Error(w, http.StatusText(500), 500)
		return
	}

	tmpl, err := template.ParseFiles(lp)
	if err != nil {
		log.Println(err.Error())
		http.Error(w, http.StatusText(500), 500)
		return
	}

	err = tmpl.ExecuteTemplate(w, "simple-layout", string(content))
	if err != nil {
		log.Println(err.Error())
		http.Error(w, http.StatusText(500), 500)
	}
}

type AgeForm struct {
	Success   bool
	PublicKey string
	Message   string
}

func serveAge(w http.ResponseWriter, r *http.Request) {
	lp := filepath.Join("templates", "layout.html")
	fp := filepath.Join("templates", "age.html")

	tmpl, err := template.ParseFiles(lp, fp)
	if err != nil {
		log.Println(err.Error())
		http.Error(w, http.StatusText(500), 500)
		return
	}

	if r.Method != http.MethodPost {
		tmpl.ExecuteTemplate(w, "layout", nil)
		return
	}

	formdeets := AgeForm{
		PublicKey: r.FormValue("pubkey"),
		Message:   r.FormValue("message"),
	}

	recipient, err := age.ParseX25519Recipient(formdeets.PublicKey)
	if err != nil {
		log.Println(err.Error())
		http.Error(w, http.StatusText(500), 500)
		return
	}

	buf := &bytes.Buffer{}
	armorWriter := armor.NewWriter(buf)

	write, err := age.Encrypt(armorWriter, recipient)
	if err != nil {
		log.Println(err.Error())
		http.Error(w, http.StatusText(500), 500)
		return
	}
	if _, err := io.WriteString(write, formdeets.Message); err != nil {
		log.Println(err.Error())
		http.Error(w, http.StatusText(500), 500)
		return
	}
	if err := write.Close(); err != nil {
		log.Println(err.Error())
		http.Error(w, http.StatusText(500), 500)
		return
	}
	if err := armorWriter.Close(); err != nil {
		log.Fatalf("Failed to close armor: %v", err)
	}

	log.Printf("%+v", buf)

	tmpl.ExecuteTemplate(w, "layout", buf.String())
}

func ipHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/plain")
	// this header is always set by tlstunnel (MAYBE)
	// so we can PROBABLY expect it to be here IDFK RLY
	// IT'S LATE AND IM TIRED
	ip := r.Header.Get("X-Forwarded-For")
	if ip != "" {
		fmt.Fprintf(w, r.Header.Get("X-Forwarded-For")+"\n")
	} else {
		fmt.Fprintf(w, "not found\n")
	}
}