mirror of
https://github.com/ditatompel/xmr-remote-nodes.git
synced 2024-11-16 17:07:36 +00:00
feat!: templ + htmx build system
This commit is contained in:
parent
03570a2200
commit
be32011cfa
19 changed files with 527 additions and 17 deletions
|
@ -7,14 +7,14 @@ tmp_dir = "tmp"
|
|||
bin = "./tmp/main"
|
||||
cmd = "make dev"
|
||||
delay = 0
|
||||
exclude_dir = ["assets", "tmp", "testdata", "frontend/node_modules", "data", "bin"]
|
||||
exclude_dir = ["assets", "tmp", "testdata", "node_modules", "data", "bin", "internal/handler/views/assets"]
|
||||
exclude_file = []
|
||||
exclude_regex = ["_test.go"]
|
||||
exclude_regex = ["_test.go", ".*_templ.go"]
|
||||
exclude_unchanged = false
|
||||
follow_symlink = false
|
||||
full_bin = ""
|
||||
include_dir = []
|
||||
include_ext = ["go", "tpl", "tmpl", "html", "css", "js"]
|
||||
include_ext = ["go", "templ", "html", "css", "js"]
|
||||
include_file = []
|
||||
kill_delay = "0s"
|
||||
log = "build-errors.log"
|
||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -3,3 +3,5 @@
|
|||
/node_modules
|
||||
/tmp
|
||||
/assets/geoip
|
||||
/internal/handler/views/assets/css/**/*
|
||||
/internal/handler/views/assets/js/**/*
|
||||
|
|
39
Makefile
39
Makefile
|
@ -31,16 +31,12 @@ BUILD_LDFLAGS := -s -w -X github.com/ditatompel/xmr-remote-nodes/internal/config
|
|||
|
||||
# This called from air cmd (see .air.toml)
|
||||
.PHONY: dev
|
||||
dev:
|
||||
dev: templ tailwind
|
||||
go build -ldflags="$(BUILD_LDFLAGS)" -tags server -o ./tmp/main .
|
||||
|
||||
.PHONY: build
|
||||
build: client server
|
||||
|
||||
.PHONY: ui
|
||||
ui:
|
||||
go generate ./...
|
||||
|
||||
.PHONY: client
|
||||
client:
|
||||
CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build \
|
||||
|
@ -51,7 +47,7 @@ client:
|
|||
-o bin/${BINARY_NAME}-client-linux-arm64
|
||||
|
||||
.PHONY: server
|
||||
server: ui
|
||||
server: prepare templ tailwind
|
||||
CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build \
|
||||
-ldflags="$(BUILD_LDFLAGS)" -tags server \
|
||||
-o bin/${BINARY_NAME}-server-linux-amd64
|
||||
|
@ -59,11 +55,34 @@ server: ui
|
|||
-ldflags="$(BUILD_LDFLAGS)" -tags server \
|
||||
-o bin/${BINARY_NAME}-server-linux-arm64
|
||||
|
||||
.PHONY: prepare
|
||||
prepare:
|
||||
bun install --frozen-lockfile
|
||||
@mkdir -p ./internal/handler/views/assets/js
|
||||
cp ./node_modules/htmx.org/dist/htmx.min.js ./internal/handler/views/assets/js
|
||||
|
||||
# Compile template
|
||||
.PHONY: templ
|
||||
templ:
|
||||
@echo "Compiling Templ template..."
|
||||
templ generate
|
||||
|
||||
.PHONY: tailwind
|
||||
tailwind:
|
||||
mkdir -p ./internal/handler/views/assets/css
|
||||
@echo "Compiling TailwindCSS..."
|
||||
bun tailwindcss -i ./internal/handler/views/src/css/main.css \
|
||||
-o ./internal/handler/views/assets/css/main.min.css \
|
||||
-c ./tailwind.config.js \
|
||||
--minify
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
go clean
|
||||
rm -rfv ./bin
|
||||
rm -rf ./frontend/build
|
||||
rm -rfv ./tmp/main
|
||||
rm -rf ./internal/handler/views/*_templ.go
|
||||
rm -rf ./internal/handler/views/assets/css/
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
|
@ -82,8 +101,10 @@ bench:
|
|||
# And make sure the inventory and deploy-*.yml file is properly configured.
|
||||
.PHONY: deploy-server
|
||||
deploy-server:
|
||||
ansible-playbook -i ./deployment/ansible/inventory.ini -l server ./deployment/ansible/deploy-server.yml -K
|
||||
ansible-playbook -i ./deployment/ansible/inventory.ini \
|
||||
-l server ./deployment/ansible/deploy-server.yml -K
|
||||
|
||||
.PHONY: deploy-prober
|
||||
deploy-prober:
|
||||
ansible-playbook -i ./deployment/ansible/inventory.ini -l prober ./deployment/ansible/deploy-prober.yml -K
|
||||
ansible-playbook -i ./deployment/ansible/inventory.ini \
|
||||
-l prober ./deployment/ansible/deploy-prober.yml -K
|
||||
|
|
15
README.md
15
README.md
|
@ -28,7 +28,15 @@ serves the `/api` endpoint that is used by the clients and the Web UI itself.
|
|||
To build the executable binaries, you need:
|
||||
|
||||
- Go >= 1.22
|
||||
- NodeJS >= 20
|
||||
- Bun >= 1.1.26
|
||||
- templ v0.2.778
|
||||
|
||||
> **Note**:
|
||||
>
|
||||
> - If you want to contribute to the code, please use exact templ version
|
||||
> (v0.2.778).
|
||||
> - The UI is using Preline UI that uses [Lucide Icons][lucide-icons], use
|
||||
> that for SVG icons.
|
||||
|
||||
### Server & Prober requirements
|
||||
|
||||
|
@ -67,6 +75,10 @@ Systemd example: [xmr-nodes-prober.service][prober-systemd-service] and
|
|||
|
||||
## Development and Deployment
|
||||
|
||||
1. Clone or fork this repository.
|
||||
2. Prepare the assets: `make prepare`,
|
||||
3. Run `air serve` (live reload using [air-verse/air][air-repo]).
|
||||
|
||||
See the [Makefile](./Makefile).
|
||||
|
||||
## ToDo's
|
||||
|
@ -113,6 +125,7 @@ This project is licensed under [GLWTPL](./LICENSE).
|
|||
[server-systemd-service]: ./deployment/init/xmr-nodes-server.service "systemd service example for server"
|
||||
[prober-systemd-service]: ./deployment/init/xmr-nodes-prober.service "systemd service example for prober"
|
||||
[prober-systemd-timer]: ./deployment/init/xmr-nodes-prober.timer "systemd timer example for prober"
|
||||
[air-repo]: https://github.com/air-verse/air "Air - Live reload for Go apps"
|
||||
[jtgrassie-monero-pool]: https://github.com/jtgrassie/monero-pool "A Monero mining pool server written in C"
|
||||
[rclone]: https://github.com/rclone/rclone "rclone GitHub repository"
|
||||
[monerofail-repo]: https://github.com/lalanza808/monero.fail "Lalanza808's monero.fail GitHub repository"
|
||||
|
|
BIN
bun.lockb
Executable file
BIN
bun.lockb
Executable file
Binary file not shown.
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/ditatompel/xmr-remote-nodes/internal/cron"
|
||||
"github.com/ditatompel/xmr-remote-nodes/internal/database"
|
||||
"github.com/ditatompel/xmr-remote-nodes/internal/handler"
|
||||
"github.com/ditatompel/xmr-remote-nodes/internal/handler/views"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||
|
@ -79,6 +80,8 @@ func serve() {
|
|||
AllowCredentials: true,
|
||||
}))
|
||||
|
||||
app.Use("/assets", views.EmbedAssets())
|
||||
|
||||
handler.V1Api(app)
|
||||
app.Use("/", filesystem.New(filesystem.Config{
|
||||
Root: frontend.SvelteKitHandler(),
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
# UI
|
||||
|
||||
The UI is generated and embedded when the Go project is built. See [./frontend/embed.go](https://github.com/ditatompel/xmr-remote-nodes/blob/main/frontend/embed.go#L10-L13).
|
||||
The UI is generated and embedded when the Go project is built. See
|
||||
[./frontend/embed.go](https://github.com/ditatompel/xmr-remote-nodes/blob/main/frontend/embed.go#L10-L13).
|
||||
|
||||
> **NOTE**:
|
||||
>
|
||||
> Since this project will not using Svelte anymore, anything under this
|
||||
> directory will soon be removed.
|
||||
|
|
3
go.mod
3
go.mod
|
@ -3,6 +3,7 @@ module github.com/ditatompel/xmr-remote-nodes
|
|||
go 1.22.2
|
||||
|
||||
require (
|
||||
github.com/a-h/templ v0.2.778
|
||||
github.com/go-sql-driver/mysql v1.8.1
|
||||
github.com/gofiber/fiber/v2 v2.52.5
|
||||
github.com/google/uuid v1.6.0
|
||||
|
@ -15,7 +16,7 @@ require (
|
|||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.17.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
|
|
8
go.sum
8
go.sum
|
@ -1,7 +1,9 @@
|
|||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/a-h/templ v0.2.778 h1:VzhOuvWECrwOec4790lcLlZpP4Iptt5Q4K9aFxQmtaM=
|
||||
github.com/a-h/templ v0.2.778/go.mod h1:lq48JXoUvuQrU0VThrK31yFwdRjTCnIE5bcPCM9IP1w=
|
||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
@ -9,6 +11,8 @@ github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpv
|
|||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo=
|
||||
github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
|
|
|
@ -1,13 +1,35 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/a-h/templ"
|
||||
"github.com/ditatompel/xmr-remote-nodes/internal/handler/views"
|
||||
"github.com/ditatompel/xmr-remote-nodes/internal/monero"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/adaptor"
|
||||
)
|
||||
|
||||
// Render Home Page
|
||||
func homeHandler(c *fiber.Ctx) error {
|
||||
p := views.Meta{
|
||||
Title: "Monero Remote Node",
|
||||
Description: "A website that helps you monitor your favourite Monero remote nodes, but YOU BETTER RUN AND USE YOUR OWN NODE.",
|
||||
Keywords: "monero,monero,xmr,monero node,xmrnode,cryptocurrency,monero remote node,monero testnet,monero stagenet",
|
||||
Robots: "INDEX,FOLLOW",
|
||||
Permalink: "https://xmr.ditatompel.com",
|
||||
Identifier: "/",
|
||||
}
|
||||
|
||||
c.Set("Link", fmt.Sprintf(`<%s>; rel="canonical"`, p.Permalink))
|
||||
home := views.BaseLayout(p, views.Home())
|
||||
handler := adaptor.HTTPHandler(templ.Handler(home))
|
||||
|
||||
return handler(c)
|
||||
}
|
||||
|
||||
// Returns a single node information based on `id` query param
|
||||
func Node(c *fiber.Ctx) error {
|
||||
nodeId, err := c.ParamsInt("id", 0)
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
// V1 API routes
|
||||
func V1Api(app *fiber.App) {
|
||||
app.Get("/", homeHandler)
|
||||
v1 := app.Group("/api/v1")
|
||||
|
||||
// these routes are public, they don't require a prober api key
|
||||
|
|
20
internal/handler/views/embed.go
Normal file
20
internal/handler/views/embed.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
package views
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"net/http"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/filesystem"
|
||||
)
|
||||
|
||||
//go:embed assets/*
|
||||
var embedStatic embed.FS
|
||||
|
||||
func EmbedAssets() fiber.Handler {
|
||||
return filesystem.New(filesystem.Config{
|
||||
Root: http.FS(embedStatic),
|
||||
PathPrefix: "assets",
|
||||
Browse: false,
|
||||
})
|
||||
}
|
4
internal/handler/views/home.templ
Normal file
4
internal/handler/views/home.templ
Normal file
|
@ -0,0 +1,4 @@
|
|||
package views
|
||||
|
||||
templ Home() {
|
||||
}
|
36
internal/handler/views/home_templ.go
Normal file
36
internal/handler/views/home_templ.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.778
|
||||
package views
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
func Home() templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
64
internal/handler/views/layout.templ
Normal file
64
internal/handler/views/layout.templ
Normal file
|
@ -0,0 +1,64 @@
|
|||
package views
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ditatompel/xmr-remote-nodes/internal/config"
|
||||
"time"
|
||||
)
|
||||
|
||||
var buildTime = time.Now().Unix()
|
||||
|
||||
type Meta struct {
|
||||
Title string
|
||||
Description string
|
||||
Keywords string
|
||||
Robots string
|
||||
Permalink string
|
||||
Identifier string
|
||||
}
|
||||
|
||||
templ base(m Meta) {
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="dark">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>{ m.Title } — xmr.ditatompel.com</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<meta name="title" content={ fmt.Sprintf("%s — xmr.ditatompel.com", m.Title) }/>
|
||||
<meta name="description" content={ m.Description }/>
|
||||
<meta name="keywords" content={ m.Keywords }/>
|
||||
<meta name="robots" content={ m.Robots }/>
|
||||
<meta name="theme-color" content="#272b31"/>
|
||||
<meta name="author" content="ditatompel"/>
|
||||
<meta property="og:site_name" content="xmr.ditatompel.com"/>
|
||||
<meta property="og:type" content="website"/>
|
||||
<meta property="og:url" content={ m.Permalink }/>
|
||||
<meta property="og:locale" content="en_US"/>
|
||||
<meta property="og:title" content={ fmt.Sprintf("%s — xmr.ditatompel.com", m.Title) }/>
|
||||
<meta property="og:description" content={ m.Description }/>
|
||||
<link href={ fmt.Sprintf("/assets/css/main.min.css?t=%d", buildTime) } rel="stylesheet"/>
|
||||
<script src={ fmt.Sprintf("/assets/js/htmx.min.js?t=%d", buildTime) }></script>
|
||||
</head>
|
||||
<body class="bg-neutral-900" hx-boost="true" hx-indicator="#hx-indicator-main">
|
||||
<main class="flex flex-col h-screen">
|
||||
{ children... }
|
||||
</main>
|
||||
<footer class="absolute bottom-0 inset-x-0 text-center py-5">
|
||||
<div class="max-w-[85rem] mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<p class="text-sm text-white/50">XMR Nodes { config.Version }, <a href="https://github.com/ditatompel/xmr-remote-nodes">source code</a> licensed under <strong>GLWTPL</strong>.</p>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
}
|
||||
|
||||
templ BaseLayout(m Meta, cmp templ.Component) {
|
||||
@base(m) {
|
||||
@cmp
|
||||
}
|
||||
}
|
||||
|
||||
templ BlankLayout(cmp templ.Component) {
|
||||
@cmp
|
||||
}
|
284
internal/handler/views/layout_templ.go
Normal file
284
internal/handler/views/layout_templ.go
Normal file
|
@ -0,0 +1,284 @@
|
|||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.778
|
||||
package views
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ditatompel/xmr-remote-nodes/internal/config"
|
||||
"time"
|
||||
)
|
||||
|
||||
var buildTime = time.Now().Unix()
|
||||
|
||||
type Meta struct {
|
||||
Title string
|
||||
Description string
|
||||
Keywords string
|
||||
Robots string
|
||||
Permalink string
|
||||
Identifier string
|
||||
}
|
||||
|
||||
func base(m Meta) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<!doctype html><html lang=\"en\" class=\"dark\"><head><meta charset=\"utf-8\"><title>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(m.Title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/layout.templ`, Line: 25, Col: 19}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" — xmr.ditatompel.com</title><meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><meta name=\"title\" content=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%s — xmr.ditatompel.com", m.Title))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/layout.templ`, Line: 28, Col: 81}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><meta name=\"description\" content=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(m.Description)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/layout.templ`, Line: 29, Col: 51}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><meta name=\"keywords\" content=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(m.Keywords)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/layout.templ`, Line: 30, Col: 45}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><meta name=\"robots\" content=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(m.Robots)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/layout.templ`, Line: 31, Col: 41}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><meta name=\"theme-color\" content=\"#272b31\"><meta name=\"author\" content=\"ditatompel\"><meta property=\"og:site_name\" content=\"xmr.ditatompel.com\"><meta property=\"og:type\" content=\"website\"><meta property=\"og:url\" content=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(m.Permalink)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/layout.templ`, Line: 36, Col: 48}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><meta property=\"og:locale\" content=\"en_US\"><meta property=\"og:title\" content=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var8 string
|
||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%s — xmr.ditatompel.com", m.Title))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/layout.templ`, Line: 38, Col: 88}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><meta property=\"og:description\" content=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var9 string
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(m.Description)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/layout.templ`, Line: 39, Col: 58}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><link href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var10 string
|
||||
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("/assets/css/main.min.css?t=%d", buildTime))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/layout.templ`, Line: 40, Col: 71}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" rel=\"stylesheet\"><script src=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var11 string
|
||||
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("/assets/js/htmx.min.js?t=%d", buildTime))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/layout.templ`, Line: 41, Col: 70}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"></script></head><body class=\"bg-neutral-900\" hx-boost=\"true\" hx-indicator=\"#hx-indicator-main\"><main class=\"flex flex-col h-screen\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ_7745c5c3_Var1.Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</main><footer class=\"absolute bottom-0 inset-x-0 text-center py-5\"><div class=\"max-w-[85rem] mx-auto px-4 sm:px-6 lg:px-8\"><p class=\"text-sm text-white/50\">XMR Nodes ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var12 string
|
||||
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(config.Version)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/layout.templ`, Line: 49, Col: 64}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(", <a href=\"https://github.com/ditatompel/xmr-remote-nodes\">source code</a> licensed under <strong>GLWTPL</strong>.</p></div></footer></body></html>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
func BaseLayout(m Meta, cmp templ.Component) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var13 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var13 == nil {
|
||||
templ_7745c5c3_Var13 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Var14 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = cmp.Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
templ_7745c5c3_Err = base(m).Render(templ.WithChildren(ctx, templ_7745c5c3_Var14), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
func BlankLayout(cmp templ.Component) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var15 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var15 == nil {
|
||||
templ_7745c5c3_Var15 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = cmp.Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
3
internal/handler/views/src/css/main.css
Normal file
3
internal/handler/views/src/css/main.css
Normal file
|
@ -0,0 +1,3 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
9
package.json
Normal file
9
package.json
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"@tailwindcss/forms": "^0.5.9",
|
||||
"@tailwindcss/typography": "^0.5.15",
|
||||
"htmx.org": "^1.9.12",
|
||||
"preline": "^2.5.1",
|
||||
"tailwindcss": "^3.4.14"
|
||||
}
|
||||
}
|
17
tailwind.config.js
Normal file
17
tailwind.config.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
"./internal/handler/views/*.templ",
|
||||
"node_modules/preline/dist/*.js",
|
||||
],
|
||||
// enable dark mode via class strategy
|
||||
// darkMode: "class",
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [
|
||||
require("@tailwindcss/typography"),
|
||||
require("@tailwindcss/forms"),
|
||||
require("preline/plugin"),
|
||||
],
|
||||
};
|
Loading…
Reference in a new issue