diff --git a/.gitignore b/.gitignore index 8e27bf6..57e57de 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ Session.vim .vercel *_templ.go dist +tmp +bin diff --git a/api/hello.go b/api/hello.go new file mode 100644 index 0000000..7095201 --- /dev/null +++ b/api/hello.go @@ -0,0 +1,45 @@ +package api + +import ( + "encoding/json" + "fmt" + "io" + "math/rand/v2" + "net/http" +) + +type helloObj struct { + Language string + Hello string +} + +func getHelloList() ([]helloObj, error) { + res, err := http.Get("https://raw.githubusercontent.com/novellac/multilanguage-hello-json/master/hello.json") + if err != nil { + return nil, err + } + bytes, err := io.ReadAll(res.Body) + if err != nil { + return nil, err + } + + var hellos []helloObj + err = json.Unmarshal(bytes, &hellos) + if err != nil { + return nil, err + } + + return hellos, nil +} + +func Hello(w http.ResponseWriter, r *http.Request) { + hellos, err := getHelloList() + var hello string + if err != nil { + hello = "Welcome!" + } else { + hello = hellos[rand.IntN(len(hellos)-1)].Hello + } + + fmt.Fprint(w, hello) +} diff --git a/cmd/build/main.go b/cmd/build/main.go new file mode 100644 index 0000000..e8d73aa --- /dev/null +++ b/cmd/build/main.go @@ -0,0 +1,28 @@ +package main + +import ( + "context" + "flag" + "log" + + "www/config" + "www/internals" +) + +func main() { + dir := flag.String("d", "./dist", "the directory to write the files") + staticDir := flag.String("s", "./static", "the directory to copy static files from") + + w := internals.StaticWriter{ + DistDir: dir, + StaticDir: staticDir, + Pages: config.ROUTES, + Context: context.Background(), + Logger: *log.Default(), + } + + err := w.WriteAll() + if err != nil { + log.Fatal(err) + } +} diff --git a/cmd/vercel/main.go b/cmd/vercel/main.go new file mode 100644 index 0000000..e321e6e --- /dev/null +++ b/cmd/vercel/main.go @@ -0,0 +1,63 @@ +package main + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "log" + "net/http" + "os" + + "www/config" + "www/internals" +) + +type VercelConfig struct { + OutputDirectory string `json:"outputDirectory"` +} + +var logger = log.Default() + +func main() { + configPath := flag.String("c", "./vercel.json", "the path to the vercel.json file") + staticDir := flag.String("s", "./static", "the directory to copy static files from") + port := flag.Int("p", 8080, "the port to run the server") + + configFile, err := os.ReadFile(*configPath) + if err != nil { + logger.Fatalf("Unable to read vercel.json file due to:\n%s", err) + } + + var c VercelConfig + err = json.Unmarshal(configFile, &c) + if err != nil { + logger.Fatalf("Unable to parse vercel.json file due to:\n%s", err) + } + + w := internals.StaticWriter{ + DistDir: &c.OutputDirectory, + StaticDir: staticDir, + Pages: config.ROUTES, + Context: context.Background(), + Logger: *log.Default(), + } + + logger.Print("Writing static files") + err = w.WriteAll() + if err != nil { + logger.Fatal(err) + } + + logger.Print("Starting server") + mux := http.NewServeMux() + + config.APIROUTES(mux) + mux.Handle("/", http.FileServer(http.Dir(c.OutputDirectory))) + + logger.Printf("Running server at port: %v", *port) + err = http.ListenAndServe(fmt.Sprintf(":%v", *port), mux) + if err != nil { + logger.Fatalf("Server crashed due to:\n%s", err) + } +} diff --git a/config/routes.go b/config/routes.go new file mode 100644 index 0000000..bbce360 --- /dev/null +++ b/config/routes.go @@ -0,0 +1,17 @@ +package config + +import ( + "net/http" + + "www/api" + "www/internals" + "www/pages" +) + +var ROUTES = []internals.Page{ + {Path: "index.html", Component: pages.Homepage()}, +} + +func APIROUTES(mux *http.ServeMux) { + mux.HandleFunc("/api/hello", api.Hello) +} diff --git a/flake.lock b/flake.lock index c70a9ff..24f3c81 100644 --- a/flake.lock +++ b/flake.lock @@ -96,11 +96,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1716137900, - "narHash": "sha256-sowPU+tLQv8GlqtVtsXioTKeaQvlMz/pefcdwg8MvfM=", + "lastModified": 1716293225, + "narHash": "sha256-pU9ViBVE3XYb70xZx+jK6SEVphvt7xMTbm6yDIF4xPs=", "owner": "nixos", "repo": "nixpkgs", - "rev": "6c0b7a92c30122196a761b440ac0d46d3d9954f1", + "rev": "3eaeaeb6b1e08a016380c279f8846e0bd8808916", "type": "github" }, "original": { @@ -171,15 +171,16 @@ "xc": "xc" }, "locked": { - "lastModified": 1716195313, - "narHash": "sha256-/7UL2Oqpp9FPYVF0SJ32/q7inHJIwZDnoSmJN/323ck=", + "lastModified": 1716034419, + "narHash": "sha256-z/sb4AlFOU20sBEAu12VSXqhHQuqvj3mUu7JTvyc1pI=", "owner": "a-h", "repo": "templ", - "rev": "e369eaf5ca569e50ae3a17931d84de9d335f6db0", + "rev": "0c14a899236d115a790b5a960b5d2b50c277c77e", "type": "github" }, "original": { "owner": "a-h", + "ref": "tags/v0.2.697", "repo": "templ", "type": "github" } diff --git a/flake.nix b/flake.nix index b5cbe15..fe8ea67 100644 --- a/flake.nix +++ b/flake.nix @@ -3,7 +3,7 @@ inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; - templ.url = "github:a-h/templ"; + templ.url = "github:a-h/templ?ref=tags/v0.2.697"; }; outputs = { self @@ -29,9 +29,6 @@ templ nodePackages_latest.vercel ]; - shellHook = " - export GOOS=linux - "; }; }); } diff --git a/go.mod b/go.mod index f970146..23152ec 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module guz.one +module www go 1.22.2 diff --git a/internals/static_writer.go b/internals/static_writer.go new file mode 100644 index 0000000..976a1a7 --- /dev/null +++ b/internals/static_writer.go @@ -0,0 +1,107 @@ +package internals + +import ( + "context" + "io" + "io/fs" + "log" + "os" + "path/filepath" + "strings" + + "github.com/a-h/templ" +) + +const PERMISSIONS = 0755 + +type Page struct { + Path string + Component templ.Component +} + +type StaticWriter struct { + DistDir *string + StaticDir *string + Pages []Page + Context context.Context + Logger log.Logger +} + +func (w *StaticWriter) WritePage(path string, writer func(ctx context.Context, w io.Writer) error) error { + directory := filepath.Dir(path) + err := os.MkdirAll(directory, PERMISSIONS) + if err != nil { + return err + } + + f, err := os.Create(path) + if err != nil { + return err + } + defer f.Close() + + err = writer(w.Context, f) + return err +} + +func (w *StaticWriter) WriteAll() error { + for _, page := range w.Pages { + p := filepath.Join(*w.DistDir, page.Path) + w.Logger.Printf("Writing page %s", p) + err := w.WritePage(p, page.Component.Render) + if err != nil { + return err + } + } + + err := filepath.WalkDir(*w.StaticDir, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } else if d.IsDir() || path == *w.StaticDir { + return nil + } + + f, err := filepath.Abs(path) + if err != nil { + return err + } + s, err := filepath.Abs(*w.StaticDir) + if err != nil { + return err + } + + err = w.CopyStatic(strings.TrimPrefix(f, s)) + if err != nil { + return err + } + return nil + }) + return err +} + +func (w *StaticWriter) CopyStatic(path string) error { + c, err := os.ReadFile(filepath.Join(*w.StaticDir, path)) + if err != nil { + return err + } + + p := filepath.Join(*w.DistDir, path) + err = os.MkdirAll(filepath.Dir(p), PERMISSIONS) + if err != nil { + return err + } + + f, err := os.Create(p) + if err != nil { + return err + } + defer f.Close() + + b, err := f.Write(c) + if err != nil { + return err + } + w.Logger.Printf("Wrote %v bytes in %s", b, p) + + return nil +} diff --git a/layouts/page.templ b/layouts/page.templ new file mode 100644 index 0000000..d216377 --- /dev/null +++ b/layouts/page.templ @@ -0,0 +1,12 @@ +package layouts + +templ Page(title string) { + +
+