mirror of
https://github.com/ditatompel/xmr-remote-nodes.git
synced 2025-04-09 21:16:28 +07:00
Compare commits
25 commits
Author | SHA1 | Date | |
---|---|---|---|
|
0ac3d6837b | ||
9fe1ed5351 | |||
|
8e32103ae4 | ||
|
31edbc0ecf | ||
866c48e85f | |||
7e66528180 | |||
|
2bc41ed1a8 | ||
|
6b4f880dbd | ||
|
6bee3768f6 | ||
|
44ac10bb8a | ||
|
60a4c83011 | ||
|
e66e087d44 | ||
|
ff682dd0bc | ||
|
8a3921edde | ||
|
bdd85078cf | ||
|
16681f6b51 | ||
|
46a29fb9cc | ||
48a25bece0 | |||
d19e5844b0 | |||
f1b9ffde33 | |||
b780783df0 | |||
84ad053413 | |||
9b48c4731a | |||
a936cb343b | |||
76c6a5514d |
21 changed files with 1106 additions and 864 deletions
|
@ -16,6 +16,11 @@ IPV6_CAPABLE=false
|
|||
# #############
|
||||
APP_URL="https://xmr.ditatompel.com" # URL where user can access the web UI, don't put trailing slash
|
||||
|
||||
# APP_SECRET is random 64-character hex string that give us 32 random bytes.
|
||||
# For now, this used for ip address salt, but may be useful for another feature
|
||||
# in the future. You can achieve this using `openssl rand -hex 32`.
|
||||
APP_SECRET=
|
||||
|
||||
# Fiber Config
|
||||
APP_PREFORK=false
|
||||
APP_HOST="127.0.0.1"
|
||||
|
|
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
|
@ -20,10 +20,10 @@ jobs:
|
|||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.22.x
|
||||
go-version: 1.23.x
|
||||
|
||||
- name: Setup templ
|
||||
run: go install github.com/a-h/templ/cmd/templ@v0.2.778
|
||||
run: go install github.com/a-h/templ/cmd/templ@v0.3.857
|
||||
|
||||
- name: Prepare assets
|
||||
run: make prepare
|
||||
|
|
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
|
@ -22,10 +22,10 @@ jobs:
|
|||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.22.x
|
||||
go-version: 1.23.x
|
||||
|
||||
- name: Setup templ
|
||||
run: go install github.com/a-h/templ/cmd/templ@v0.2.778
|
||||
run: go install github.com/a-h/templ/cmd/templ@v0.3.857
|
||||
|
||||
# Need to build the UI here before build the server binary with go-release-action
|
||||
- name: Prepare assets
|
||||
|
|
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
|
@ -21,10 +21,10 @@ jobs:
|
|||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.22.x
|
||||
go-version: 1.23.x
|
||||
|
||||
- name: Setup templ
|
||||
run: go install github.com/a-h/templ/cmd/templ@v0.2.778
|
||||
run: go install github.com/a-h/templ/cmd/templ@v0.3.857
|
||||
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v3
|
||||
|
|
41
README.md
41
README.md
|
@ -20,31 +20,31 @@ The **clients** is used to fetch node information given by the server. First,
|
|||
it will ask the server which node to fetch. Then, it will fetch the information
|
||||
and report back to the server.
|
||||
|
||||
The **server** serves an embedded Svelte static site for the Web UI. It also
|
||||
serves the `/api` endpoint that is used by the clients and the Web UI itself.
|
||||
The **server** serves the Web UI and the `/api` endpoint that is used by the
|
||||
clients.
|
||||
|
||||
## Requirements
|
||||
|
||||
To build the executable binaries, you need:
|
||||
|
||||
- Go >= 1.22
|
||||
- Bun >= 1.1.26
|
||||
- [a-h/templ][templ-repo] v0.2.778
|
||||
- Go >= 1.23
|
||||
- Bun >= 1.1.26
|
||||
- [a-h/templ][templ-repo] v0.3.857
|
||||
|
||||
> **Note**:
|
||||
>
|
||||
> - If you want to contribute to the code, please use exact templ version
|
||||
> (v0.2.778).
|
||||
> - If you want to contribute to the code, please use exact templ version
|
||||
> (v0.3.857).
|
||||
|
||||
### Server & Prober requirements
|
||||
|
||||
- Linux Machines (AMD64 or ARM64)
|
||||
- Linux Machines (AMD64 or ARM64)
|
||||
|
||||
### Server requirements
|
||||
|
||||
- MySQL/MariaDB
|
||||
- [GeoIP Database][geoip-doc] (optional). Place it to `./assets/geoip`,
|
||||
see [./internal/ip/geo/geoip.go](./internal/ip/geo/geoip.go).
|
||||
- MySQL/MariaDB
|
||||
- [GeoIP Database][geoip-doc] (optional). Place it to `./assets/geoip`,
|
||||
see [./internal/ip/geo/geoip.go](./internal/ip/geo/geoip.go).
|
||||
|
||||
## Installation
|
||||
|
||||
|
@ -81,23 +81,24 @@ See the [Makefile](./Makefile).
|
|||
|
||||
## ToDo's
|
||||
|
||||
- :white_check_mark: Accept IPv6 nodes.
|
||||
- :white_check_mark: Use `a-h/templ` and `HTMX` instead of `Svelte`.
|
||||
- Use Go standard `net/http` instead of `fiber`.
|
||||
- :white_check_mark: Accept I2P nodes.
|
||||
- :white_check_mark: Accept IPv6 nodes.
|
||||
- :white_check_mark: Use `a-h/templ` and `HTMX` instead of `Svelte`.
|
||||
- Use Go standard `net/http` instead of `fiber`.
|
||||
- :white_check_mark: Accept I2P nodes.
|
||||
- :white_check_mark: Support Tor hidden service (beta, inform via HTTP header).
|
||||
|
||||
## Acknowledgement
|
||||
|
||||
The creators and contributors of these projects have provided valuable
|
||||
resources, which I am grateful for:
|
||||
|
||||
- [jtgrassie/monero-pool][jtgrassie-monero-pool]
|
||||
- [rclone/rclone][rclone]
|
||||
- [jtgrassie/monero-pool][jtgrassie-monero-pool]
|
||||
- [rclone/rclone][rclone]
|
||||
|
||||
## Similar Projects
|
||||
|
||||
- [lalanza808/monero.fail][monerofail-repo]
|
||||
- [cake-tech/upptime-monerocom][uptime-monerocom-repo]
|
||||
- [lalanza808/monero.fail][monerofail-repo]
|
||||
- [cake-tech/upptime-monerocom][uptime-monerocom-repo]
|
||||
|
||||
## Donation
|
||||
|
||||
|
@ -121,7 +122,7 @@ Thank you!
|
|||
This project is licensed under [BSD-3-Clause](./LICENSE) license.
|
||||
|
||||
[templ-repo]: https://github.com/a-h/templ "a-h/templ GitHub repository"
|
||||
[geoip-doc]: https://dev.maxmind.com/geoip/geoip2/geolite2/ "GeoIP documentation"
|
||||
[geoip-doc]: https://dev.maxmind.com/geoip/geolite2-free-geolocation-data/ "GeoLite2 Free documentation"
|
||||
[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"
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
v0.2.0
|
||||
v0.2.2
|
||||
|
|
|
@ -44,6 +44,9 @@ server {
|
|||
add_header X-XSS-Protection "1; mode=block";
|
||||
add_header X-Download-Options noopen;
|
||||
|
||||
# Add your onion URL here if you support it
|
||||
# add_header Onion-Location http://<YOUR-ONION-ADDRESS>.onion$request_uri;
|
||||
|
||||
location = /robots.txt {
|
||||
log_not_found off;
|
||||
access_log off;
|
||||
|
|
22
go.mod
22
go.mod
|
@ -1,33 +1,35 @@
|
|||
module github.com/ditatompel/xmr-remote-nodes
|
||||
|
||||
go 1.22.2
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.24.1
|
||||
|
||||
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/a-h/templ v0.3.857
|
||||
github.com/go-sql-driver/mysql v1.9.1
|
||||
github.com/gofiber/fiber/v2 v2.52.6
|
||||
github.com/google/go-querystring v1.1.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/jmoiron/sqlx v1.4.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/oschwald/geoip2-golang v1.11.0
|
||||
github.com/spf13/cobra v1.8.1
|
||||
golang.org/x/net v0.31.0
|
||||
github.com/spf13/cobra v1.9.1
|
||||
golang.org/x/net v0.38.0
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // 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/klauspost/compress v1.17.9 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/oschwald/maxminddb-golang v1.13.0 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.51.0 // indirect
|
||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||
golang.org/x/sys v0.27.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
)
|
||||
|
|
37
go.sum
37
go.sum
|
@ -1,16 +1,17 @@
|
|||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/a-h/templ v0.2.778 h1:VzhOuvWECrwOec4790lcLlZpP4Iptt5Q4K9aFxQmtaM=
|
||||
github.com/a-h/templ v0.2.778/go.mod h1:lq48JXoUvuQrU0VThrK31yFwdRjTCnIE5bcPCM9IP1w=
|
||||
github.com/a-h/templ v0.3.857 h1:6EqcJuGZW4OL+2iZ3MD+NnIcG7nGkaQeF2Zq5kf9ZGg=
|
||||
github.com/a-h/templ v0.3.857/go.mod h1:qhrhAkRFubE7khxLZHsBFHfX+gWwVNKbzKeF9GlPV4M=
|
||||
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/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
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=
|
||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||
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/go-sql-driver/mysql v1.9.1 h1:FrjNGn/BsJQjVRuSa8CBrM5BWA9BWoXXat3KrtSb/iI=
|
||||
github.com/go-sql-driver/mysql v1.9.1/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||
github.com/gofiber/fiber/v2 v2.52.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI=
|
||||
github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
|
@ -24,8 +25,8 @@ github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
|
|||
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
|
||||
github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
|
@ -33,8 +34,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
|
|||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/oschwald/geoip2-golang v1.11.0 h1:hNENhCn1Uyzhf9PTmquXENiWS6AlxAEnBII6r8krA3w=
|
||||
|
@ -46,10 +47,10 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
|||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
|
@ -58,12 +59,12 @@ github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1S
|
|||
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
|
||||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
|
||||
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
|
|
@ -13,7 +13,8 @@ type App struct {
|
|||
LogLevel string
|
||||
|
||||
// configuration for server
|
||||
URL string // URL where user can access the web UI, don't put trailing slash
|
||||
URL string // URL where user can access the web UI, don't put trailing slash
|
||||
Secret string // random 64-character hex string that give us 32 random bytes
|
||||
|
||||
// fiber specific config
|
||||
Prefork bool
|
||||
|
@ -61,6 +62,7 @@ func LoadApp() {
|
|||
|
||||
// server configuration
|
||||
app.URL = os.Getenv("APP_URL")
|
||||
app.Secret = os.Getenv("APP_SECRET")
|
||||
|
||||
// fiber specific config
|
||||
app.Host = os.Getenv("APP_HOST")
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
|
||||
type migrateFn func(*DB) error
|
||||
|
||||
var dbMigrate = [...]migrateFn{v1, v2, v3, v4}
|
||||
var dbMigrate = [...]migrateFn{v1, v2, v3, v4, v5}
|
||||
|
||||
func MigrateDb(db *DB) error {
|
||||
version := getSchemaVersion(db)
|
||||
|
@ -287,3 +287,18 @@ func v4(db *DB) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func v5(db *DB) error {
|
||||
// table: tbl_node
|
||||
slog.Debug("[DB] Adding additional columns to tbl_node")
|
||||
_, err := db.Exec(`
|
||||
ALTER TABLE tbl_node
|
||||
ADD COLUMN submitter_iphash CHAR(64) NOT NULL DEFAULT ''
|
||||
COMMENT 'hashed IP address who submitted the node'
|
||||
AFTER date_entered;`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ func (s *fiberServer) addNodeHandler(c *fiber.Ctx) error {
|
|||
}
|
||||
|
||||
moneroRepo := monero.New()
|
||||
if err := moneroRepo.Add(f.Protocol, f.Hostname, uint(f.Port)); err != nil {
|
||||
if err := moneroRepo.Add(c.IP(), s.secret, f.Protocol, f.Hostname, uint(f.Port)); err != nil {
|
||||
handler := adaptor.HTTPHandler(templ.Handler(views.Alert("error", err.Error())))
|
||||
return handler(c)
|
||||
}
|
||||
|
@ -354,7 +354,7 @@ func (s *fiberServer) addNodeAPI(c *fiber.Ctx) error {
|
|||
hostname := c.FormValue("hostname")
|
||||
|
||||
moneroRepo := monero.New()
|
||||
if err := moneroRepo.Add(protocol, hostname, uint(port)); err != nil {
|
||||
if err := moneroRepo.Add(c.IP(), s.secret, protocol, hostname, uint(port)); err != nil {
|
||||
return c.JSON(fiber.Map{
|
||||
"status": "error",
|
||||
"message": err.Error(),
|
||||
|
|
|
@ -8,8 +8,9 @@ import (
|
|||
|
||||
type fiberServer struct {
|
||||
*fiber.App
|
||||
db *database.DB
|
||||
url string
|
||||
db *database.DB
|
||||
url string
|
||||
secret string
|
||||
}
|
||||
|
||||
// NewServer returns a new fiber server
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.778
|
||||
// templ: version: v0.3.857
|
||||
package views
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
@ -29,7 +29,7 @@ func AddNode() templ.Component {
|
|||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<!-- Hero --><section class=\"relative overflow-hidden pt-6\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!-- Hero --><section class=\"relative overflow-hidden pt-6\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -37,11 +37,11 @@ func AddNode() templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"relative z-10\"><div class=\"max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-10 lg:py-16\"><div class=\"text-center\"><div class=\"mt-5\"><h1 class=\"block font-extrabold text-4xl md:text-5xl lg:text-6xl text-neutral-200\">Add Monero Node</h1></div><div class=\"mt-5\"><p class=\"text-lg text-neutral-300\">You can use this page to add known remote node to the system so my bots can monitor it.</p></div></div><hr class=\"my-6 border-orange-400 mx-auto max-w-3xl\"><div class=\"max-w-4xl mx-auto px-4\"><div class=\"p-4 bg-blue-800/10 border border-blue-900 text-sm text-white rounded-lg\" role=\"alert\" tabindex=\"-1\" aria-labelledby=\"add-node-notice\"><div class=\"flex\"><div class=\"ms-4\"><h2 id=\"add-node-notice\" class=\"text-xl font-bold text-center\">Important Note</h2><div class=\"mt-2 text-sm\"><ul class=\"list-disc space-y-1 ps-5\"><li>As an administrator of this instance, I have full rights to delete, and blacklist any submitted node with or without providing any reason.</li><li>I2P nodes monitoring is beta.</li></ul></div></div></div></div></div><div class=\"max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-6\"><p class=\"mt-1 text-center\">Enter your Monero node information below:</p><div class=\"mt-12\"><form method=\"put\" hx-swap=\"transition:true\" hx-target=\"#form-result\" hx-disabled-elt=\".form\" hx-on::after-request=\"this.reset()\"><div class=\"grid grid-cols-1 sm:grid-cols-4 gap-6\"><div><label for=\"protocol\" class=\"block text-neutral-200\">Protocol *</label> <select id=\"protocol\" name=\"protocol\" class=\"frameless form\" autocomplete=\"off\"><option value=\"http\">HTTP</option> <option value=\"https\">HTTPS</option></select></div><div class=\"md:col-span-2\"><label for=\"hostname\" class=\"block text-neutral-200\">Host / IP *</label> <input type=\"text\" name=\"hostname\" id=\"hostname\" class=\"frameless form\" autocomplete=\"off\" placeholder=\"Eg: node.example.com or 172.16.17.18\" required></div><div><label for=\"port\" class=\"block text-neutral-200\">Port *</label> <input type=\"text\" name=\"port\" id=\"port\" class=\"frameless form\" autocomplete=\"off\" placeholder=\"Eg: 18081\" required></div></div><div class=\"mt-6 grid\"><button type=\"submit\" class=\"form w-full py-3 px-4 inline-flex justify-center items-center gap-x-2 text-sm font-bold rounded-lg border border-transparent bg-orange-600 text-white hover:bg-orange-500 focus:outline-none disabled:opacity-60 disabled:pointer-events-none\">Submit</button></div></form><div id=\"form-result\" class=\"max-w-4xl mx-auto my-6\"></div><div class=\"mt-3 text-center\"><p class=\"text-sm text-gray-500 dark:text-neutral-500\">Existing remote nodes can be found in <a href=\"/remote-nodes\" class=\"link\">/remote-nodes</a> page.</p></div></div></div></div></div></section><!-- End Hero -->")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div class=\"relative z-10\"><div class=\"max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-10 lg:py-16\"><div class=\"text-center\"><div class=\"mt-5\"><h1 class=\"block font-extrabold text-4xl md:text-5xl lg:text-6xl text-neutral-200\">Add Monero Node</h1></div><div class=\"mt-5\"><p class=\"text-lg text-neutral-300\">You can use this page to add known remote node to the system so my bots can monitor it.</p></div></div><hr class=\"my-6 border-orange-400 mx-auto max-w-3xl\"><div class=\"max-w-4xl mx-auto px-4\"><div class=\"p-4 bg-blue-800/10 border border-blue-900 text-sm text-white rounded-lg\" role=\"alert\" tabindex=\"-1\" aria-labelledby=\"add-node-notice\"><div class=\"flex\"><div class=\"ms-4\"><h2 id=\"add-node-notice\" class=\"text-xl font-bold text-center\">Important Note</h2><div class=\"mt-2 text-sm\"><ul class=\"list-disc space-y-1 ps-5\"><li>As an administrator of this instance, I have full rights to delete, and blacklist any submitted node with or without providing any reason.</li><li>I2P nodes monitoring is beta.</li></ul></div></div></div></div></div><div class=\"max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-6\"><p class=\"mt-1 text-center\">Enter your Monero node information below:</p><div class=\"mt-12\"><form method=\"put\" hx-swap=\"transition:true\" hx-target=\"#form-result\" hx-disabled-elt=\".form\" hx-on::after-request=\"this.reset()\"><div class=\"grid grid-cols-1 sm:grid-cols-4 gap-6\"><div><label for=\"protocol\" class=\"block text-neutral-200\">Protocol *</label> <select id=\"protocol\" name=\"protocol\" class=\"frameless form\" autocomplete=\"off\"><option value=\"http\">HTTP</option> <option value=\"https\">HTTPS</option></select></div><div class=\"md:col-span-2\"><label for=\"hostname\" class=\"block text-neutral-200\">Host / IP *</label> <input type=\"text\" name=\"hostname\" id=\"hostname\" class=\"frameless form\" autocomplete=\"off\" placeholder=\"Eg: node.example.com or 172.16.17.18\" required></div><div><label for=\"port\" class=\"block text-neutral-200\">Port *</label> <input type=\"text\" name=\"port\" id=\"port\" class=\"frameless form\" autocomplete=\"off\" placeholder=\"Eg: 18081\" required></div></div><div class=\"mt-6 grid\"><button type=\"submit\" class=\"form w-full py-3 px-4 inline-flex justify-center items-center gap-x-2 text-sm font-bold rounded-lg border border-transparent bg-orange-600 text-white hover:bg-orange-500 focus:outline-none disabled:opacity-60 disabled:pointer-events-none\">Submit</button></div></form><div id=\"form-result\" class=\"max-w-4xl mx-auto my-6\"></div><div class=\"mt-3 text-center\"><p class=\"text-sm text-gray-500 dark:text-neutral-500\">Existing remote nodes can be found in <a href=\"/remote-nodes\" class=\"link\">/remote-nodes</a> page.</p></div></div></div></div></div></section><!-- End Hero -->")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,6 +1,6 @@
|
|||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.778
|
||||
// templ: version: v0.3.857
|
||||
package views
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
@ -46,7 +46,7 @@ func base(m Meta) templ.Component {
|
|||
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>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\" class=\"dark\"><head><meta charset=\"utf-8\"><title>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ func base(m Meta) templ.Component {
|
|||
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=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, " — 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
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ func base(m Meta) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><meta name=\"description\" content=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\"><meta name=\"description\" content=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ func base(m Meta) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><meta name=\"keywords\" content=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\"><meta name=\"keywords\" content=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ func base(m Meta) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><meta name=\"robots\" content=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "\"><meta name=\"robots\" content=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ func base(m Meta) templ.Component {
|
|||
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=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "\"><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
|
||||
}
|
||||
|
@ -124,7 +124,7 @@ func base(m Meta) templ.Component {
|
|||
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=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "\"><meta property=\"og:locale\" content=\"en_US\"><meta property=\"og:title\" content=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -137,7 +137,7 @@ func base(m Meta) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><meta property=\"og:description\" content=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "\"><meta property=\"og:description\" content=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -150,7 +150,7 @@ func base(m Meta) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><link rel=\"icon\" href=\"/assets/favicon.ico\"><link href=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "\"><link rel=\"icon\" href=\"/assets/favicon.ico\"><link href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -163,7 +163,7 @@ func base(m Meta) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" rel=\"stylesheet\"><script src=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "\" rel=\"stylesheet\"><script src=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -176,7 +176,7 @@ func base(m Meta) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"></script><script src=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\"></script><script src=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -189,7 +189,7 @@ func base(m Meta) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"></script><script src=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "\"></script><script src=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -202,7 +202,7 @@ func base(m Meta) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"></script></head><body class=\"bg-neutral-900 text-neutral-400\" hx-boost=\"true\" hx-indicator=\"#hx-indicator-main\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\"></script></head><body class=\"bg-neutral-900 text-neutral-400\" hx-boost=\"true\" hx-indicator=\"#hx-indicator-main\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -210,7 +210,7 @@ func base(m Meta) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<main class=\"shrink-0 min-h-screen\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<main class=\"shrink-0 min-h-screen\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -218,7 +218,7 @@ func base(m Meta) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</main><footer class=\"mt-auto py-3 bg-neutral-800 text-center\"><div class=\"max-w-[85rem] mx-auto px-4 sm:px-6 lg:px-8\"><p class=\"text-sm\">XMR Nodes ")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</main><footer class=\"mt-auto py-3 bg-neutral-800 text-center\"><div class=\"max-w-[85rem] mx-auto px-4 sm:px-6 lg:px-8\"><p class=\"text-sm\">XMR Nodes ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -231,11 +231,11 @@ func base(m Meta) templ.Component {
|
|||
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\" target=\"_blank\" rel=\"noopener\" class=\"external\">source code</a> licensed under <strong>BSD-3-Clause</strong> license.</p></div></footer><div id=\"modal-section\" class=\"hs-overlay hidden size-full fixed top-0 start-0 z-[80] overflow-x-hidden overflow-y-auto pointer-events-none\" role=\"dialog\" tabindex=\"-1\" aria-labelledby=\"modal-section-label\"></div></body></html>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, ", <a href=\"https://github.com/ditatompel/xmr-remote-nodes\" target=\"_blank\" rel=\"noopener\" class=\"external\">source code</a> licensed under <strong>BSD-3-Clause</strong> license.</p></div></footer><div id=\"modal-section\" class=\"hs-overlay hidden size-full fixed top-0 start-0 z-[80] overflow-x-hidden overflow-y-auto pointer-events-none\" role=\"dialog\" tabindex=\"-1\" aria-labelledby=\"modal-section-label\"></div></body></html>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -276,13 +276,13 @@ func BaseLayout(m Meta, cmp templ.Component) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = base(m).Render(templ.WithChildren(ctx, templ_7745c5c3_Var16), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -311,7 +311,7 @@ func BlankLayout(cmp templ.Component) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -336,7 +336,7 @@ func ModalLayout(title string, cmp templ.Component) templ.Component {
|
|||
templ_7745c5c3_Var18 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"hs-overlay-open:mt-7 hs-overlay-open:opacity-100 hs-overlay-open:duration-500 mt-0 opacity-0 ease-out transition-all lg:max-w-4xl lg:w-full m-3 lg:mx-auto h-[calc(100%-3.5rem)] min-h-[calc(100%-3.5rem)] flex items-center\"><div class=\"modal-container\"><div class=\"modal-header\"><h3 class=\"font-bold\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<div class=\"hs-overlay-open:mt-7 hs-overlay-open:opacity-100 hs-overlay-open:duration-500 mt-0 opacity-0 ease-out transition-all lg:max-w-4xl lg:w-full m-3 lg:mx-auto h-[calc(100%-3.5rem)] min-h-[calc(100%-3.5rem)] flex items-center\"><div class=\"modal-container\"><div class=\"modal-header\"><h3 class=\"font-bold\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -349,7 +349,7 @@ func ModalLayout(title string, cmp templ.Component) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h3><button type=\"button\" class=\"btn-close\" data-hs-overlay=\"#modal-section\"><span class=\"sr-only\">Close</span> <svg class=\"flex-shrink-0 size-4\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 6 6 18\"></path> <path d=\"m6 6 12 12\"></path></svg></button></div><div class=\"modal-body\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</h3><button type=\"button\" class=\"btn-close\" data-hs-overlay=\"#modal-section\"><span class=\"sr-only\">Close</span> <svg class=\"flex-shrink-0 size-4\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 6 6 18\"></path> <path d=\"m6 6 12 12\"></path></svg></button></div><div class=\"modal-body\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -357,11 +357,11 @@ func ModalLayout(title string, cmp templ.Component) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><div class=\"modal-footer\"><button type=\"button\" class=\"py-2 px-3 inline-flex items-center gap-x-2 bg-neutral-900 text-sm text-white rounded-lg shadow-sm border border-neutral-700 hover:bg-neutral-800\" data-hs-overlay=\"#modal-section\">Close</button></div></div></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</div><div class=\"modal-footer\"><button type=\"button\" class=\"py-2 px-3 inline-flex items-center gap-x-2 bg-neutral-900 text-sm text-white rounded-lg shadow-sm border border-neutral-700 hover:bg-neutral-800\" data-hs-overlay=\"#modal-section\">Close</button></div></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -386,11 +386,11 @@ func heroGradient() templ.Component {
|
|||
templ_7745c5c3_Var20 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div aria-hidden=\"true\" class=\"flex absolute -top-96 start-1/2 transform -translate-x-1/2\"><div class=\"bg-gradient-to-r blur-3xl w-[25rem] h-[44rem] rotate-[-60deg] transform -translate-x-[10rem] from-amber-800/30 to-orange-800/40\"></div><div class=\"bg-gradient-to-tl blur-3xl w-[90rem] h-[50rem] rounded-fulls origin-top-left -rotate-12 -translate-x-[15rem] from-orange-900/60 via-orange-900/40 to-amber-900/80\"></div></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "<div aria-hidden=\"true\" class=\"flex absolute -top-96 start-1/2 transform -translate-x-1/2\"><div class=\"bg-gradient-to-r blur-3xl w-[25rem] h-[44rem] rotate-[-60deg] transform -translate-x-[10rem] from-amber-800/30 to-orange-800/40\"></div><div class=\"bg-gradient-to-tl blur-3xl w-[90rem] h-[50rem] rounded-fulls origin-top-left -rotate-12 -translate-x-[15rem] from-orange-900/60 via-orange-900/40 to-amber-900/80\"></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -417,7 +417,7 @@ func Alert(status, message string) templ.Component {
|
|||
ctx = templ.ClearChildren(ctx)
|
||||
switch status {
|
||||
case "success":
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"mt-2 bg-green-600 text-white rounded-lg p-4\"><strong>Success:</strong> ")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<div class=\"mt-2 bg-green-600 text-white rounded-lg p-4\"><strong>Success:</strong> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -430,12 +430,12 @@ func Alert(status, message string) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
case "error":
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"mt-2 bg-red-600 text-white rounded-lg p-4\"><strong>Error:</strong> ")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<div class=\"mt-2 bg-red-600 text-white rounded-lg p-4\"><strong>Error:</strong> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -448,12 +448,12 @@ func Alert(status, message string) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
default:
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"mt-2 bg-blue-600 text-white rounded-lg p-4\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<div class=\"mt-2 bg-blue-600 text-white rounded-lg p-4\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -466,12 +466,12 @@ func Alert(status, message string) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.778
|
||||
// templ: version: v0.3.857
|
||||
package views
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
@ -37,7 +37,7 @@ func DtRowPerPage(url, hxTarget string, rowsPerPage int, q interface{}) templ.Co
|
|||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"max-w-sm space-y-3\"><select name=\"limit\" id=\"dt_limit\" class=\"py-2 px-3 pe-9 block bg-neutral-900 border-neutral-700 rounded-lg text-sm focus:border-orange-400 focus:ring-orange-400\" autocomplete=\"off\" hx-get=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"max-w-sm space-y-3\"><select name=\"limit\" id=\"dt_limit\" class=\"py-2 px-3 pe-9 block bg-neutral-900 border-neutral-700 rounded-lg text-sm focus:border-orange-400 focus:ring-orange-400\" autocomplete=\"off\" hx-get=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ func DtRowPerPage(url, hxTarget string, rowsPerPage int, q interface{}) templ.Co
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-trigger=\"change\" hx-push-url=\"false\" hx-target=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\" hx-trigger=\"change\" hx-push-url=\"false\" hx-target=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -63,12 +63,12 @@ func DtRowPerPage(url, hxTarget string, rowsPerPage int, q interface{}) templ.Co
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-swap=\"outerHTML\"><option disabled>CHOOSE</option> ")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\" hx-swap=\"outerHTML\"><option disabled>CHOOSE</option> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, page := range availablePages {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<option value=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<option value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -81,17 +81,17 @@ func DtRowPerPage(url, hxTarget string, rowsPerPage int, q interface{}) templ.Co
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if page == rowsPerPage {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" selected")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, " selected")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, ">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -104,16 +104,16 @@ func DtRowPerPage(url, hxTarget string, rowsPerPage int, q interface{}) templ.Co
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</option>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</option>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</select></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</select></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -138,7 +138,7 @@ func DtRefreshInterval(url, hxTarget, interval string, q interface{}) templ.Comp
|
|||
templ_7745c5c3_Var6 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"inline-flex gap-x-2 items-center\"><div>Auto refresh:</div><div class=\"max-w-sm space-y-3\"><select name=\"refresh\" id=\"dt_refresh\" class=\"py-2 px-3 pe-9 block text-sm text-neutral-400 bg-neutral-900 border-neutral-700 rounded-lg focus:border-orange-400 focus:ring-orange-400\" autocomplete=\"off\" hx-get=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<div class=\"inline-flex gap-x-2 items-center\"><div>Auto refresh:</div><div class=\"max-w-sm space-y-3\"><select name=\"refresh\" id=\"dt_refresh\" class=\"py-2 px-3 pe-9 block text-sm text-neutral-400 bg-neutral-900 border-neutral-700 rounded-lg focus:border-orange-400 focus:ring-orange-400\" autocomplete=\"off\" hx-get=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -151,7 +151,7 @@ func DtRefreshInterval(url, hxTarget, interval string, q interface{}) templ.Comp
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-trigger=\"change\" hx-push-url=\"false\" hx-target=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\" hx-trigger=\"change\" hx-push-url=\"false\" hx-target=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -164,12 +164,12 @@ func DtRefreshInterval(url, hxTarget, interval string, q interface{}) templ.Comp
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-swap=\"outerHTML\"><option value=\"\">off</option> ")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "\" hx-swap=\"outerHTML\"><option value=\"\">off</option> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, v := range refreshIntevals {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<option value=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<option value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -182,17 +182,17 @@ func DtRefreshInterval(url, hxTarget, interval string, q interface{}) templ.Comp
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if v == interval {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" selected")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, " selected")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, ">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -205,17 +205,17 @@ func DtRefreshInterval(url, hxTarget, interval string, q interface{}) templ.Comp
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</option>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "</option>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</select></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</select></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if slices.Contains(refreshIntevals, interval) {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div hx-get=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "<div hx-get=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -228,7 +228,7 @@ func DtRefreshInterval(url, hxTarget, interval string, q interface{}) templ.Comp
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-push-url=\"false\" hx-target=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "\" hx-push-url=\"false\" hx-target=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -241,7 +241,7 @@ func DtRefreshInterval(url, hxTarget, interval string, q interface{}) templ.Comp
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-trigger=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "\" hx-trigger=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -254,16 +254,16 @@ func DtRefreshInterval(url, hxTarget, interval string, q interface{}) templ.Comp
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-swap=\"outerHTML\"></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\" hx-swap=\"outerHTML\"></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -288,7 +288,7 @@ func DtReload(url, hxTarget string, q interface{}) templ.Component {
|
|||
templ_7745c5c3_Var14 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<button class=\"py-2 px-3 inline-flex items-center gap-x-2 text-sm font-bold rounded-full border border-transparent bg-orange-600 text-white hover:bg-orange-500 focus:outline-none\" hx-get=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "<button class=\"py-2 px-3 inline-flex items-center gap-x-2 text-sm font-bold rounded-full border border-transparent bg-orange-600 text-white hover:bg-orange-500 focus:outline-none\" hx-get=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -301,7 +301,7 @@ func DtReload(url, hxTarget string, q interface{}) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-push-url=\"false\" hx-target=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "\" hx-push-url=\"false\" hx-target=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -314,11 +314,11 @@ func DtReload(url, hxTarget string, q interface{}) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-swap=\"outerHTML\"><svg class=\"flex-shrink-0 size-4\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-refresh-cw\"><path d=\"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8\"></path><path d=\"M21 3v5h-5\"></path><path d=\"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16\"></path><path d=\"M8 16H3v5\"></path></svg> Reload</button>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "\" hx-swap=\"outerHTML\"><svg class=\"flex-shrink-0 size-4\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-refresh-cw\"><path d=\"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8\"></path><path d=\"M21 3v5h-5\"></path><path d=\"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16\"></path><path d=\"M8 16H3v5\"></path></svg> Reload</button>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -353,7 +353,7 @@ func DtThSort(url, hxTarget, title, expectedSort, sortBy, sortDir string, q inte
|
|||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
if expectedSort == sortBy && sortDir == "asc" {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<th scope=\"col\" class=\"cursor-pointer\" hx-push-url=\"false\" hx-target=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<th scope=\"col\" class=\"cursor-pointer\" hx-push-url=\"false\" hx-target=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -366,7 +366,7 @@ func DtThSort(url, hxTarget, title, expectedSort, sortBy, sortDir string, q inte
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-swap=\"outerHTML\" hx-get=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "\" hx-swap=\"outerHTML\" hx-get=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -379,7 +379,7 @@ func DtThSort(url, hxTarget, title, expectedSort, sortBy, sortDir string, q inte
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -392,12 +392,12 @@ func DtThSort(url, hxTarget, title, expectedSort, sortBy, sortDir string, q inte
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ▾</th>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, " ▾</th>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else if expectedSort == sortBy && sortDir == "desc" {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<th scope=\"col\" class=\"cursor-pointer\" hx-push-url=\"false\" hx-target=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "<th scope=\"col\" class=\"cursor-pointer\" hx-push-url=\"false\" hx-target=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -410,7 +410,7 @@ func DtThSort(url, hxTarget, title, expectedSort, sortBy, sortDir string, q inte
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-swap=\"outerHTML\" hx-get=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "\" hx-swap=\"outerHTML\" hx-get=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -423,7 +423,7 @@ func DtThSort(url, hxTarget, title, expectedSort, sortBy, sortDir string, q inte
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -436,12 +436,12 @@ func DtThSort(url, hxTarget, title, expectedSort, sortBy, sortDir string, q inte
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ▴</th>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, " ▴</th>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<th scope=\"col\" class=\"cursor-pointer\" hx-push-url=\"false\" hx-target=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<th scope=\"col\" class=\"cursor-pointer\" hx-push-url=\"false\" hx-target=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -454,7 +454,7 @@ func DtThSort(url, hxTarget, title, expectedSort, sortBy, sortDir string, q inte
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-swap=\"outerHTML\" hx-get=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "\" hx-swap=\"outerHTML\" hx-get=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -467,7 +467,7 @@ func DtThSort(url, hxTarget, title, expectedSort, sortBy, sortDir string, q inte
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -480,12 +480,12 @@ func DtThSort(url, hxTarget, title, expectedSort, sortBy, sortDir string, q inte
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ▴▾</th>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, " ▴▾</th>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -510,17 +510,17 @@ func DtRowCount(currentPage, rowsPerPage, totalRows int) templ.Component {
|
|||
templ_7745c5c3_Var27 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div><p class=\"text-sm\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "<div><p class=\"text-sm\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if totalRows <= 0 {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("No entries found")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "No entries found")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<b>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "<b>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -533,12 +533,12 @@ func DtRowCount(currentPage, rowsPerPage, totalRows int) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</b> ")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "</b> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if rowsPerPage*currentPage > totalRows {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("- <b>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "- <b>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -551,12 +551,12 @@ func DtRowCount(currentPage, rowsPerPage, totalRows int) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</b>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "</b>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("- <b>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "- <b>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -569,12 +569,12 @@ func DtRowCount(currentPage, rowsPerPage, totalRows int) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</b>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "</b>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" <b>/ ")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, " <b>/ ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -587,16 +587,16 @@ func DtRowCount(currentPage, rowsPerPage, totalRows int) templ.Component {
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</b>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "</b>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</p></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "</p></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -621,18 +621,18 @@ func DtPagination(url, hxTarget string, q interface{}, p paging.Pagination) temp
|
|||
templ_7745c5c3_Var32 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div><nav class=\"pagination inline-flex gap-x-2\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "<div><nav class=\"pagination inline-flex gap-x-2\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, page := range p.Pages {
|
||||
if page == -1 {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<button class=\"cursor-not-allowed\" disabled>...</button>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "<button class=\"cursor-not-allowed\" disabled>...</button>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else if page == p.CurrentPage {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<button class=\"active\" disabled>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "<button class=\"active\" disabled>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -645,12 +645,12 @@ func DtPagination(url, hxTarget string, q interface{}, p paging.Pagination) temp
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</button>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "</button>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<button hx-get=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "<button hx-get=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -663,7 +663,7 @@ func DtPagination(url, hxTarget string, q interface{}, p paging.Pagination) temp
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-push-url=\"false\" hx-target=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "\" hx-push-url=\"false\" hx-target=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -676,7 +676,7 @@ func DtPagination(url, hxTarget string, q interface{}, p paging.Pagination) temp
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-swap=\"outerHTML\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "\" hx-swap=\"outerHTML\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -689,17 +689,17 @@ func DtPagination(url, hxTarget string, q interface{}, p paging.Pagination) temp
|
|||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</button>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "</button>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</nav></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "</nav></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.778
|
||||
// templ: version: v0.3.857
|
||||
package views
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
@ -29,41 +29,41 @@ func navbar(pageIdentifier string) templ.Component {
|
|||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<header class=\"fixed top-4 inset-x-0 flex flex-wrap z-50 w-full md:justify-start md:flex-nowrap before:absolute before:inset-0 before:max-w-7xl before:mx-2 before:lg:mx-auto before:rounded-[26px] before:bg-neutral-800/70 before:backdrop-blur-sm before:shadow-md before:shadow-orange-400/40\"><nav class=\"relative max-w-7xl w-full py-2.5 px-5 md:flex md:items-center md:justify-between md:py-0 mx-2 lg:mx-auto\"><div class=\"flex items-center justify-between\"><div class=\"flex-none inline-block\"><a class=\"text-xl font-semibold text-white focus:outline-none\" href=\"/\" aria-label=\"XMR Nodes\">XMR Nodes</a><div id=\"hx-indicator-main\" class=\"htmx-indicator animate-spin ml-2 inline-block size-4 border-[3px] border-current border-t-transparent text-orange-400 rounded-full\" role=\"status\" aria-label=\"loading indicator\"><span class=\"sr-only\">Loading...</span></div></div><div class=\"md:hidden\"><button type=\"button\" class=\"hs-collapse-toggle size-8 flex justify-center items-center text-sm font-semibold rounded-full bg-neutral-800 text-white disabled:opacity-50 disabled:pointer-events-none\" id=\"hs-navbar-floating-dark-collapse\" aria-expanded=\"false\" aria-controls=\"hs-navbar-floating-dark\" aria-label=\"Toggle navigation\" data-hs-collapse=\"#main-navbar\"><svg class=\"hs-collapse-open:hidden shrink-0 size-4\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"3\" x2=\"21\" y1=\"6\" y2=\"6\"></line><line x1=\"3\" x2=\"21\" y1=\"12\" y2=\"12\"></line><line x1=\"3\" x2=\"21\" y1=\"18\" y2=\"18\"></line></svg> <svg class=\"hs-collapse-open:block hidden shrink-0 size-4\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 6 6 18\"></path><path d=\"m6 6 12 12\"></path></svg></button></div></div><div id=\"main-navbar\" class=\"hs-collapse hidden overflow-hidden transition-all duration-300 basis-full grow md:block\" aria-labelledby=\"main-navbar-collapse\"><div class=\"flex flex-col md:flex-row md:items-center md:justify-end gap-2 md:gap-3 mt-3 md:mt-0 py-2 md:py-0 md:ps-7\"><a href=\"/\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<header class=\"fixed top-4 inset-x-0 flex flex-wrap z-50 w-full md:justify-start md:flex-nowrap before:absolute before:inset-0 before:max-w-7xl before:mx-2 before:lg:mx-auto before:rounded-[26px] before:bg-neutral-800/70 before:backdrop-blur-sm before:shadow-md before:shadow-orange-400/40\"><nav class=\"relative max-w-7xl w-full py-2.5 px-5 md:flex md:items-center md:justify-between md:py-0 mx-2 lg:mx-auto\"><div class=\"flex items-center justify-between\"><div class=\"flex-none inline-block\"><a class=\"text-xl font-semibold text-white focus:outline-none\" href=\"/\" aria-label=\"XMR Nodes\">XMR Nodes</a><div id=\"hx-indicator-main\" class=\"htmx-indicator animate-spin ml-2 inline-block size-4 border-[3px] border-current border-t-transparent text-orange-400 rounded-full\" role=\"status\" aria-label=\"loading indicator\"><span class=\"sr-only\">Loading...</span></div></div><div class=\"md:hidden\"><button type=\"button\" class=\"hs-collapse-toggle size-8 flex justify-center items-center text-sm font-semibold rounded-full bg-neutral-800 text-white disabled:opacity-50 disabled:pointer-events-none\" id=\"hs-navbar-floating-dark-collapse\" aria-expanded=\"false\" aria-controls=\"hs-navbar-floating-dark\" aria-label=\"Toggle navigation\" data-hs-collapse=\"#main-navbar\"><svg class=\"hs-collapse-open:hidden shrink-0 size-4\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"3\" x2=\"21\" y1=\"6\" y2=\"6\"></line><line x1=\"3\" x2=\"21\" y1=\"12\" y2=\"12\"></line><line x1=\"3\" x2=\"21\" y1=\"18\" y2=\"18\"></line></svg> <svg class=\"hs-collapse-open:block hidden shrink-0 size-4\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 6 6 18\"></path><path d=\"m6 6 12 12\"></path></svg></button></div></div><div id=\"main-navbar\" class=\"hs-collapse hidden overflow-hidden transition-all duration-300 basis-full grow md:block\" aria-labelledby=\"main-navbar-collapse\"><div class=\"flex flex-col md:flex-row md:items-center md:justify-end gap-2 md:gap-3 mt-3 md:mt-0 py-2 md:py-0 md:ps-7\"><a href=\"/\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if pageIdentifier == "/" {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" class=\"active\" aria-current=\"page\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, " class=\"active\" aria-current=\"page\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(">Home</a> <a href=\"/remote-nodes\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, ">Home</a> <a href=\"/remote-nodes\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if pageIdentifier == "/remote-nodes" {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" class=\"active\" aria-current=\"page\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, " class=\"active\" aria-current=\"page\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(">Remote Nodes</a> <a href=\"/add-node\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, ">Remote Nodes</a> <a href=\"/add-node\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if pageIdentifier == "/add-node" {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" class=\"active\" aria-current=\"page\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, " class=\"active\" aria-current=\"page\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(">Add Node</a></div></div></nav></header>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, ">Add Node</a></div></div></nav></header>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ templ RemoteNodes(data monero.Nodes, countries []monero.Countries, q monero.Quer
|
|||
<li>Nodes with 0% uptime within 1 month with more than 300 check attempt will be removed. You can always add your node again latter.</li>
|
||||
<li>You can filter remote node by selecting on <strong>nettype</strong>, <strong>protocol</strong>, <strong>country</strong>, <strong>tor</strong>, and <strong>online status</strong> option.</li>
|
||||
<li>If you want to add more remote node, you can add them using <a href="/add-node" class="link">/add-node</a> page.</li>
|
||||
<li>I deliberately cut the long Tor addresses, click the <span class="text-orange-300">👁 torhostname...</span> to see the full Tor address.</li>
|
||||
<li>I deliberately cut the long Tor and I2P addresses, click the <span class="text-orange-300">👁 hostname...</span> to open more detailed information about the Node.</li>
|
||||
<li>You can found larger remote nodes database from <a href="https://monero.fail/" target="_blank" rel="noopener" class="external">monero.fail</a>.</li>
|
||||
<li>If you are developer or power user who like to fetch Monero remote node above in JSON format, you can read <a href="https://insights.ditatompel.com/en/blog/2022/01/public-api-monero-remote-node-list/" class="external">Public API Monero Remote Node List</a> blog post for more detailed information.</li>
|
||||
</ul>
|
||||
|
@ -206,10 +206,16 @@ templ TableNodes(data monero.Nodes, countries []monero.Countries, q monero.Query
|
|||
@cellHostPort(row.ID, row.Port, row.Hostname, row.IPAddresses, row.IsTor, row.IsI2P, row.IPv6Only)
|
||||
</td>
|
||||
<td>
|
||||
@cellNettype(row.Nettype, row.Height)
|
||||
@fmtNettype(row.Nettype)
|
||||
<br/>
|
||||
{ fmt.Sprintf("%d", row.Height) }
|
||||
</td>
|
||||
<td>
|
||||
@cellProtocol(row.Protocol, row.CORSCapable)
|
||||
@fmtProtocol(row.Protocol)
|
||||
if row.CORSCapable {
|
||||
<br/>
|
||||
(CORS 💪)
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@cellCountry(row.CountryCode, row.CountryName, row.City, row.ASNName, row.ASN)
|
||||
|
@ -259,7 +265,12 @@ templ Node(data monero.Node) {
|
|||
</dt>
|
||||
<dd>
|
||||
<ul>
|
||||
<li class="uppercase">{ data.Protocol }</li>
|
||||
<li class="uppercase">
|
||||
@fmtProtocol(data.Protocol)
|
||||
if data.CORSCapable {
|
||||
<span class="ml-2">(CORS 💪)</span>
|
||||
}
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
|
@ -270,7 +281,14 @@ templ Node(data monero.Node) {
|
|||
</dt>
|
||||
<dd>
|
||||
<ul>
|
||||
<li class="uppercase">{ data.Nettype }</li>
|
||||
<li class="uppercase">
|
||||
if data.IsI2P {
|
||||
<span class="badge bg-green-600 mr-2">I2P</span>
|
||||
} else if data.IsTor {
|
||||
<span class="badge bg-purple-800 mr-2">TOR</span>
|
||||
}
|
||||
@fmtNettype(data.Nettype)
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
|
@ -287,6 +305,50 @@ templ Node(data monero.Node) {
|
|||
</dd>
|
||||
</dl>
|
||||
}
|
||||
if data.CountryCode != "" {
|
||||
<dl class="flex flex-col sm:flex-row gap-1">
|
||||
<dt class="min-w-40">
|
||||
<span class="block text-white text-bold">Country:</span>
|
||||
</dt>
|
||||
<dd>
|
||||
<ul>
|
||||
<li class="whitespace-break-spaces">
|
||||
@cellCountry(data.CountryCode, data.CountryName, data.City, data.ASNName, data.ASN)
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
}
|
||||
<dl class="flex flex-col sm:flex-row gap-1">
|
||||
<dt class="min-w-40">
|
||||
<span class="block text-white text-bold">Monitored Since:</span>
|
||||
</dt>
|
||||
<dd>
|
||||
<ul>
|
||||
<li class="whitespace-break-spaces">
|
||||
{ time.Unix(data.DateEntered, 0).UTC().Format("Jan 2, 2006 15:04 MST") } (about { utils.TimeSince(data.DateEntered) })
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
<dl class="flex flex-col sm:flex-row gap-1">
|
||||
<dt class="min-w-40">
|
||||
<span class="block text-white text-bold">cURL get_info Eg.:</span>
|
||||
</dt>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="curl-getinfo-eg" class="sr-only">cURL get_info Example</label>
|
||||
<div class="flex rounded-lg shadow-sm">
|
||||
<input type="text" id="curl-getinfo-eg" name="stagenet-ssl" class="py-1 px-2 block w-full text-neutral-400 bg-neutral-900 border-neutral-700 shadow-sm rounded-0 text-sm focus:z-10 focus:border-orange-500 focus:ring-orange-500" value={ monero.ParseCURLGetInfo(data) } readonly/>
|
||||
<button class="clipboard copy-input" data-clipboard-target="#curl-getinfo-eg">
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
@ -413,6 +475,26 @@ templ TableLogs(hxPath string, data monero.FetchLogs, q monero.QueryLogs, p pagi
|
|||
</div>
|
||||
}
|
||||
|
||||
templ fmtNettype(nettype string) {
|
||||
switch nettype {
|
||||
case "stagenet":
|
||||
<span class="font-semibold uppercase text-sky-500">{ nettype }</span>
|
||||
case "testnet":
|
||||
<span class="font-semibold uppercase text-rose-500">{ nettype }</span>
|
||||
default:
|
||||
<span class="font-semibold uppercase text-green-500">{ nettype }</span>
|
||||
}
|
||||
}
|
||||
|
||||
templ fmtProtocol(protocol string) {
|
||||
switch protocol {
|
||||
case "http":
|
||||
<span class="font-semibold uppercase text-sky-500">{ protocol }</span>
|
||||
default:
|
||||
<span class="font-semibold uppercase text-green-500">{ protocol }</span>
|
||||
}
|
||||
}
|
||||
|
||||
templ cellHostPort(id, port uint, hostname, ips string, isTor, isI2P, ipv6Only bool) {
|
||||
if isTor {
|
||||
<button
|
||||
|
@ -447,7 +529,19 @@ templ cellHostPort(id, port uint, hostname, ips string, isTor, isI2P, ipv6Only b
|
|||
.i2p:<span class="text-indigo-400">{ fmt.Sprintf("%d", port) }</span>
|
||||
<span class="badge bg-green-600">I2P</span>
|
||||
} else {
|
||||
{ ip.FormatHostname(hostname) }:<span class="text-indigo-400">{ fmt.Sprintf("%d", port) }</span>
|
||||
<button
|
||||
class="text-orange-400 hover:brightness-125"
|
||||
hx-get={ fmt.Sprintf("/remote-nodes/id/%d", id) }
|
||||
hx-push-url="false"
|
||||
hx-target="#modal-section"
|
||||
aria-haspopup="dialog"
|
||||
aria-expanded="false"
|
||||
aria-controls="modal-section"
|
||||
data-hs-overlay="#modal-section"
|
||||
>
|
||||
👁 { ip.FormatHostname(hostname) }
|
||||
</button>
|
||||
:<span class="text-indigo-400">{ fmt.Sprintf("%d", port) }</span>
|
||||
<br/>
|
||||
<div class="max-w-40 text-ellipsis overflow-x-auto md:overflow-hidden hover:overflow-visible">
|
||||
<span class="whitespace-break-spaces text-gray-400">{ strings.ReplaceAll(ips, ",", " ") }</span>
|
||||
|
@ -458,32 +552,6 @@ templ cellHostPort(id, port uint, hostname, ips string, isTor, isI2P, ipv6Only b
|
|||
}
|
||||
}
|
||||
|
||||
templ cellNettype(nettype string, height uint) {
|
||||
switch nettype {
|
||||
case "stagenet":
|
||||
<span class="font-semibold uppercase text-sky-500">{ nettype }</span>
|
||||
case "testnet":
|
||||
<span class="font-semibold uppercase text-rose-500">{ nettype }</span>
|
||||
default:
|
||||
<span class="font-semibold uppercase text-green-500">{ nettype }</span>
|
||||
}
|
||||
<br/>
|
||||
{ fmt.Sprintf("%d", height) }
|
||||
}
|
||||
|
||||
templ cellProtocol(protocol string, cors bool) {
|
||||
switch protocol {
|
||||
case "http":
|
||||
<span class="font-semibold uppercase text-sky-500">{ protocol }</span>
|
||||
default:
|
||||
<span class="font-semibold uppercase text-green-500">{ protocol }</span>
|
||||
}
|
||||
if cors {
|
||||
<br/>
|
||||
(CORS 💪)
|
||||
}
|
||||
}
|
||||
|
||||
templ cellCountry(cc, countryName, city, asnName string, asn uint) {
|
||||
if cc != "" {
|
||||
if city != "" {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,9 @@
|
|||
package monero
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -53,7 +55,8 @@ type Node struct {
|
|||
City string `json:"city" db:"city"`
|
||||
Latitude float64 `json:"latitude" db:"lat"`
|
||||
Longitude float64 `json:"longitude" db:"lon"`
|
||||
DateEntered uint `json:"date_entered,omitempty" db:"date_entered"`
|
||||
DateEntered int64 `json:"date_entered,omitempty" db:"date_entered"`
|
||||
SubmitterIPHash string `json:"submitter_iphash,omitempty" db:"submitter_iphash"`
|
||||
LastChecked int64 `json:"last_checked" db:"last_checked"`
|
||||
FailedCount uint `json:"failed_count,omitempty" db:"failed_count"`
|
||||
LastCheckStatus types.JSONText `json:"last_check_statuses" db:"last_check_status"`
|
||||
|
@ -176,7 +179,35 @@ func (r *moneroRepo) Nodes(q QueryNodes) (Nodes, error) {
|
|||
|
||||
query := fmt.Sprintf(`
|
||||
SELECT
|
||||
*
|
||||
id,
|
||||
hostname,
|
||||
ip_addr,
|
||||
port,
|
||||
protocol,
|
||||
is_tor,
|
||||
is_i2p,
|
||||
is_available,
|
||||
nettype,
|
||||
height,
|
||||
adjusted_time,
|
||||
database_size,
|
||||
difficulty,
|
||||
version,
|
||||
uptime,
|
||||
estimate_fee,
|
||||
asn,
|
||||
asn_name,
|
||||
country,
|
||||
country_name,
|
||||
city,
|
||||
lat,
|
||||
lon,
|
||||
date_entered,
|
||||
last_checked,
|
||||
last_check_status,
|
||||
cors_capable,
|
||||
ipv6_only,
|
||||
ip_addresses
|
||||
FROM
|
||||
tbl_node
|
||||
%s
|
||||
|
@ -190,7 +221,7 @@ func (r *moneroRepo) Nodes(q QueryNodes) (Nodes, error) {
|
|||
return nodes, err
|
||||
}
|
||||
|
||||
func (r *moneroRepo) Add(protocol string, hostname string, port uint) error {
|
||||
func (r *moneroRepo) Add(submitterIP, salt, protocol, hostname string, port uint) error {
|
||||
if protocol != "http" && protocol != "https" {
|
||||
return errors.New("Invalid protocol, must one of or HTTP/HTTPS")
|
||||
}
|
||||
|
@ -270,6 +301,7 @@ func (r *moneroRepo) Add(protocol string, hostname string, port uint) error {
|
|||
lat,
|
||||
lon,
|
||||
date_entered,
|
||||
submitter_iphash,
|
||||
last_checked,
|
||||
last_check_status,
|
||||
ip_addresses,
|
||||
|
@ -288,6 +320,7 @@ func (r *moneroRepo) Add(protocol string, hostname string, port uint) error {
|
|||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?
|
||||
)`,
|
||||
protocol,
|
||||
|
@ -300,6 +333,7 @@ func (r *moneroRepo) Add(protocol string, hostname string, port uint) error {
|
|||
0,
|
||||
0,
|
||||
time.Now().Unix(),
|
||||
hashIPWithSalt(submitterIP, salt),
|
||||
0,
|
||||
string(statusDb),
|
||||
ips,
|
||||
|
@ -397,6 +431,14 @@ func (r *moneroRepo) Countries() ([]Countries, error) {
|
|||
return c, err
|
||||
}
|
||||
|
||||
// hashIPWithSalt hashes IP address with salt designed for checksumming, but
|
||||
// still maintain user privacy, this is NOT cryptographic security.
|
||||
func hashIPWithSalt(ip, salt string) string {
|
||||
hasher := sha256.New()
|
||||
hasher.Write([]byte(salt + ip)) // Combine salt and IP
|
||||
return hex.EncodeToString(hasher.Sum(nil))
|
||||
}
|
||||
|
||||
// ParseNodeStatuses parses JSONText into [5]int
|
||||
// Used this to parse last_check_status for templ engine
|
||||
func ParseNodeStatuses(statuses types.JSONText) [5]int {
|
||||
|
@ -407,3 +449,28 @@ func ParseNodeStatuses(statuses types.JSONText) [5]int {
|
|||
|
||||
return s
|
||||
}
|
||||
|
||||
// ParseCURLGetInfo generates curl command to get node info from given node
|
||||
//
|
||||
// Primarily used for Web UI to display example curl command.
|
||||
func ParseCURLGetInfo(node Node) string {
|
||||
d := `'{"jsonrpc":"2.0","id":"0","method":"get_info"}' -H 'Content-Type: application/json'`
|
||||
|
||||
if node.IsI2P {
|
||||
return fmt.Sprintf(
|
||||
"curl -x socks5h://127.0.0.1:4447 %s://%s:%d/json_rpc -d %s -sL",
|
||||
node.Protocol, node.Hostname, node.Port, d,
|
||||
)
|
||||
}
|
||||
if node.IsTor {
|
||||
return fmt.Sprintf(
|
||||
"curl -x socks5h://127.0.0.1:9050 %s://%s:%d/json_rpc -d %s -sL",
|
||||
node.Protocol, node.Hostname, node.Port, d,
|
||||
)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"curl %s://%s:%d/json_rpc -d %s -sL",
|
||||
node.Protocol, node.Hostname, node.Port, d,
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue