This commit is contained in:
parent
d2710d1718
commit
6607e1dc5e
@ -11,5 +11,6 @@
|
|||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="library" name="Python 3.9 interpreter library" level="application" />
|
<orderEntry type="library" name="Python 3.9 interpreter library" level="application" />
|
||||||
|
<orderEntry type="library" name="daisyui" level="application" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
6
.idea/jsLibraryMappings.xml
Normal file
6
.idea/jsLibraryMappings.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="JavaScriptLibraryMappings">
|
||||||
|
<file url="PROJECT" libraries="{daisyui}" />
|
||||||
|
</component>
|
||||||
|
</project>
|
3
go.mod
3
go.mod
@ -17,6 +17,9 @@ require (
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||||
|
github.com/gofiber/template v1.8.2 // indirect
|
||||||
|
github.com/gofiber/template/html/v2 v2.1.0 // indirect
|
||||||
|
github.com/gofiber/utils v1.1.0 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
|
6
go.sum
6
go.sum
@ -16,6 +16,12 @@ github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbS
|
|||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/gofiber/fiber/v2 v2.52.0 h1:S+qXi7y+/Pgvqq4DrSmREGiFwtB7Bu6+QFLuIHYw/UE=
|
github.com/gofiber/fiber/v2 v2.52.0 h1:S+qXi7y+/Pgvqq4DrSmREGiFwtB7Bu6+QFLuIHYw/UE=
|
||||||
github.com/gofiber/fiber/v2 v2.52.0/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
github.com/gofiber/fiber/v2 v2.52.0/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
||||||
|
github.com/gofiber/template v1.8.2 h1:PIv9s/7Uq6m+Fm2MDNd20pAFFKt5wWs7ZBd8iV9pWwk=
|
||||||
|
github.com/gofiber/template v1.8.2/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8=
|
||||||
|
github.com/gofiber/template/html/v2 v2.1.0 h1:FjwzqhhdJpnhyCvav60Z1ytnBqOUr5sGO/aTeob9/ng=
|
||||||
|
github.com/gofiber/template/html/v2 v2.1.0/go.mod h1:txXsRQN/G7Fr2cqGfr6zhVHgreCfpsBS+9+DJyrddJc=
|
||||||
|
github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM=
|
||||||
|
github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0=
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
@ -10,88 +10,86 @@ import (
|
|||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
)
|
)
|
||||||
|
|
||||||
func UseProxies(app *fiber.App) {
|
func ProxiesHandler(ctx *fiber.Ctx) error {
|
||||||
app.All("/*", func(ctx *fiber.Ctx) error {
|
host := ctx.Hostname()
|
||||||
host := ctx.Hostname()
|
path := ctx.Path()
|
||||||
path := ctx.Path()
|
queries := ctx.Queries()
|
||||||
queries := ctx.Queries()
|
headers := ctx.GetReqHeaders()
|
||||||
headers := ctx.GetReqHeaders()
|
|
||||||
|
|
||||||
// Filtering sites
|
// Filtering sites
|
||||||
for _, region := range navi.R.Regions {
|
for _, region := range navi.R.Regions {
|
||||||
// Matching rules
|
// Matching rules
|
||||||
for _, location := range region.Locations {
|
for _, location := range region.Locations {
|
||||||
if !lo.Contains(location.Host, host) {
|
if !lo.Contains(location.Host, host) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if !func() bool {
|
if !func() bool {
|
||||||
flag := false
|
flag := false
|
||||||
for _, pattern := range location.Path {
|
for _, pattern := range location.Path {
|
||||||
if ok, _ := regexp.MatchString(pattern, path); ok {
|
if ok, _ := regexp.MatchString(pattern, path); ok {
|
||||||
flag = true
|
flag = true
|
||||||
break
|
break
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return flag
|
|
||||||
}() {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
return flag
|
||||||
|
}() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Filter query strings
|
// Filter query strings
|
||||||
flag := true
|
flag := true
|
||||||
for rk, rv := range location.Queries {
|
for rk, rv := range location.Queries {
|
||||||
for ik, iv := range queries {
|
for ik, iv := range queries {
|
||||||
if rk != ik && rv != iv {
|
if rk != ik && rv != iv {
|
||||||
flag = false
|
flag = false
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !flag {
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !flag {
|
if !flag {
|
||||||
continue
|
break
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if !flag {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Filter headers
|
// Filter headers
|
||||||
for rk, rv := range location.Headers {
|
for rk, rv := range location.Headers {
|
||||||
for ik, iv := range headers {
|
for ik, iv := range headers {
|
||||||
if rk == ik {
|
if rk == ik {
|
||||||
for _, ov := range iv {
|
for _, ov := range iv {
|
||||||
if !lo.Contains(rv, ov) {
|
if !lo.Contains(rv, ov) {
|
||||||
flag = false
|
flag = false
|
||||||
break
|
break
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !flag {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if !flag {
|
if !flag {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !flag {
|
if !flag {
|
||||||
continue
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
idx := rand.Intn(len(location.Destinations))
|
|
||||||
dest := location.Destinations[idx]
|
|
||||||
|
|
||||||
// Passing all the rules means the site is what we are looking for.
|
|
||||||
// Let us respond to our client!
|
|
||||||
return makeResponse(ctx, region, &location, &dest)
|
|
||||||
}
|
}
|
||||||
}
|
if !flag {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// There is no site available for this request.
|
idx := rand.Intn(len(location.Destinations))
|
||||||
// Just ignore it and give our client a not found status.
|
dest := location.Destinations[idx]
|
||||||
// Do not care about the user experience, we can do it in custom error handler.
|
|
||||||
return fiber.ErrNotFound
|
// Passing all the rules means the site is what we are looking for.
|
||||||
})
|
// Let us respond to our client!
|
||||||
|
return makeResponse(ctx, region, &location, &dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// There is no site available for this request.
|
||||||
|
// Just ignore it and give our client a not found status.
|
||||||
|
// Do not care about the user experience, we can do it in custom error handler.
|
||||||
|
return fiber.ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeResponse(c *fiber.Ctx, region *navi.Region, location *navi.Location, dest *navi.Destination) error {
|
func makeResponse(c *fiber.Ctx, region *navi.Region, location *navi.Location, dest *navi.Destination) error {
|
||||||
|
@ -1,25 +1,32 @@
|
|||||||
package hypertext
|
package hypertext
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.smartsheep.studio/goatworks/roadsign/pkg/hypertext/status"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
jsoniter "github.com/json-iterator/go"
|
jsoniter "github.com/json-iterator/go"
|
||||||
"net"
|
"net"
|
||||||
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/gofiber/fiber/v2/middleware/limiter"
|
"github.com/gofiber/fiber/v2/middleware/limiter"
|
||||||
"github.com/gofiber/fiber/v2/middleware/logger"
|
"github.com/gofiber/fiber/v2/middleware/logger"
|
||||||
|
"github.com/gofiber/template/html/v2"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
func InitServer() *fiber.App {
|
func InitServer() *fiber.App {
|
||||||
|
views := html.NewFileSystem(http.FS(status.FS), ".gohtml")
|
||||||
app := fiber.New(fiber.Config{
|
app := fiber.New(fiber.Config{
|
||||||
|
ViewsLayout: "views/index",
|
||||||
AppName: "RoadSign",
|
AppName: "RoadSign",
|
||||||
ServerHeader: "RoadSign",
|
ServerHeader: "RoadSign",
|
||||||
DisableStartupMessage: true,
|
DisableStartupMessage: true,
|
||||||
EnableIPValidation: true,
|
EnableIPValidation: true,
|
||||||
|
Views: views,
|
||||||
|
ErrorHandler: status.StatusPageHandler,
|
||||||
JSONDecoder: jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal,
|
JSONDecoder: jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal,
|
||||||
JSONEncoder: jsoniter.ConfigCompatibleWithStandardLibrary.Marshal,
|
JSONEncoder: jsoniter.ConfigCompatibleWithStandardLibrary.Marshal,
|
||||||
Prefork: viper.GetBool("performance.prefork"),
|
Prefork: viper.GetBool("performance.prefork"),
|
||||||
@ -50,10 +57,13 @@ func InitServer() *fiber.App {
|
|||||||
app.Use(limiter.New(limiter.Config{
|
app.Use(limiter.New(limiter.Config{
|
||||||
Max: viper.GetInt("hypertext.limitation.max_qps"),
|
Max: viper.GetInt("hypertext.limitation.max_qps"),
|
||||||
Expiration: 1 * time.Second,
|
Expiration: 1 * time.Second,
|
||||||
|
LimitReached: func(c *fiber.Ctx) error {
|
||||||
|
return fiber.ErrTooManyRequests
|
||||||
|
},
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
UseProxies(app)
|
app.All("/*", ProxiesHandler)
|
||||||
|
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
6
pkg/hypertext/status/embed.go
Normal file
6
pkg/hypertext/status/embed.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package status
|
||||||
|
|
||||||
|
import "embed"
|
||||||
|
|
||||||
|
//go:embed all:views
|
||||||
|
var FS embed.FS
|
56
pkg/hypertext/status/serve.go
Normal file
56
pkg/hypertext/status/serve.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package status
|
||||||
|
|
||||||
|
import (
|
||||||
|
roadsign "code.smartsheep.studio/goatworks/roadsign/pkg"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ErrorPayload struct {
|
||||||
|
Title string `json:"title"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func StatusPageHandler(c *fiber.Ctx, err error) error {
|
||||||
|
var reqErr *fiber.Error
|
||||||
|
var status = fiber.StatusInternalServerError
|
||||||
|
if errors.As(err, &reqErr) {
|
||||||
|
status = reqErr.Code
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Status(status)
|
||||||
|
|
||||||
|
payload := ErrorPayload{
|
||||||
|
Version: roadsign.AppVersion,
|
||||||
|
}
|
||||||
|
|
||||||
|
switch status {
|
||||||
|
case fiber.StatusNotFound:
|
||||||
|
payload.Title = "Not Found"
|
||||||
|
payload.Message = fmt.Sprintf("no resource for \"%s\"", c.OriginalURL())
|
||||||
|
return c.Render("views/not-found", payload)
|
||||||
|
case fiber.StatusTooManyRequests:
|
||||||
|
payload.Title = "Request Too Fast"
|
||||||
|
payload.Message = fmt.Sprintf("you have sent over %d request(s) in a second", viper.GetInt("hypertext.limitation.max_qps"))
|
||||||
|
return c.Render("views/too-many-requests", payload)
|
||||||
|
case fiber.StatusRequestEntityTooLarge:
|
||||||
|
payload.Title = "Request Too Large"
|
||||||
|
payload.Message = fmt.Sprintf("you have sent a request over %d bytes", viper.GetInt("hypertext.limitation.max_body_size"))
|
||||||
|
return c.Render("views/request-too-large", payload)
|
||||||
|
case fiber.StatusBadGateway:
|
||||||
|
payload.Title = "Backend Down"
|
||||||
|
payload.Message = fmt.Sprintf("all destnations configured to handle your request are down: %s", err.Error())
|
||||||
|
return c.Render("views/bad-gateway", payload)
|
||||||
|
case fiber.StatusGatewayTimeout:
|
||||||
|
payload.Title = "Backend Took Too Long To Response"
|
||||||
|
payload.Message = fmt.Sprintf("the destnation took too long to response your request: %s", err.Error())
|
||||||
|
return c.Render("views/gateway-timeout", payload)
|
||||||
|
default:
|
||||||
|
payload.Title = "Oops"
|
||||||
|
payload.Message = err.Error()
|
||||||
|
return c.Render("views/fallback", payload)
|
||||||
|
}
|
||||||
|
}
|
6
pkg/hypertext/status/views/bad-gateway.gohtml
Normal file
6
pkg/hypertext/status/views/bad-gateway.gohtml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<h1 class="text-2xl font-bold">502</h1>
|
||||||
|
<h2 class="text-lg">No one is standing...</h2>
|
||||||
|
|
||||||
|
<div class="mt-3 mx-auto p-5 w-[360px] max-w-screen bg-neutral text-neutral-content rounded">
|
||||||
|
<code class="capitalize">{{ .Message }}</code>
|
||||||
|
</div>
|
6
pkg/hypertext/status/views/fallback.gohtml
Normal file
6
pkg/hypertext/status/views/fallback.gohtml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<h1 class="text-2xl font-bold">Oops</h1>
|
||||||
|
<h2 class="text-lg">Something went wrong...</h2>
|
||||||
|
|
||||||
|
<div class="mt-3 mx-auto p-5 w-[360px] max-w-screen bg-neutral text-neutral-content rounded">
|
||||||
|
<code class="capitalize">{{ .Message }}</code>
|
||||||
|
</div>
|
6
pkg/hypertext/status/views/gateway-timeout.gohtml
Normal file
6
pkg/hypertext/status/views/gateway-timeout.gohtml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<h1 class="text-2xl font-bold">504</h1>
|
||||||
|
<h2 class="text-lg">Looks like the server in the back fell asleep</h2>
|
||||||
|
|
||||||
|
<div class="mt-3 mx-auto p-5 w-[360px] max-w-screen bg-neutral text-neutral-content rounded">
|
||||||
|
<code class="capitalize">{{ .Message }}</code>
|
||||||
|
</div>
|
83
pkg/hypertext/status/views/index.gohtml
Normal file
83
pkg/hypertext/status/views/index.gohtml
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/daisyui@4.6.1/dist/full.min.css" rel="stylesheet" type="text/css" />
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
tailwind.config = {
|
||||||
|
daisyui: {
|
||||||
|
themes: [
|
||||||
|
{
|
||||||
|
light: {
|
||||||
|
primary: "#4750a3",
|
||||||
|
secondary: "#93c5fd",
|
||||||
|
accent: "#0f766e",
|
||||||
|
info: "#67e8f9",
|
||||||
|
success: "#15803d",
|
||||||
|
warning: "#f97316",
|
||||||
|
error: "#dc2626",
|
||||||
|
neutral: "#2B3440",
|
||||||
|
"secondary-content": "oklch(98.71% 0.0106 342.55)",
|
||||||
|
"neutral-content": "#D7DDE4",
|
||||||
|
"base-100": "oklch(100% 0 0)",
|
||||||
|
"base-200": "#F2F2F2",
|
||||||
|
"base-300": "#E5E6E6",
|
||||||
|
"base-content": "#1f2937",
|
||||||
|
"color-scheme": "light",
|
||||||
|
"--rounded-box": "0",
|
||||||
|
"--rounded-btn": "0",
|
||||||
|
"--rounded-badge": "0",
|
||||||
|
"--tab-radius": "0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dark: {
|
||||||
|
primary: "#4750a3",
|
||||||
|
secondary: "#93c5fd",
|
||||||
|
accent: "#0f766e",
|
||||||
|
info: "#67e8f9",
|
||||||
|
success: "#15803d",
|
||||||
|
warning: "#f97316",
|
||||||
|
error: "#dc2626",
|
||||||
|
neutral: "#2a323c",
|
||||||
|
"neutral-content": "#A6ADBB",
|
||||||
|
"base-100": "#1d232a",
|
||||||
|
"base-200": "#191e24",
|
||||||
|
"base-300": "#15191e",
|
||||||
|
"base-content": "#A6ADBB",
|
||||||
|
"color-scheme": "dark",
|
||||||
|
"--rounded-box": "0",
|
||||||
|
"--rounded-btn": "0",
|
||||||
|
"--rounded-badge": "0",
|
||||||
|
"--tab-radius": "0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<title>{{ .Title }} | RoadSign</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="w-full h-screen flex justify-center items-center">
|
||||||
|
<div class="text-center">
|
||||||
|
{{embed}}
|
||||||
|
|
||||||
|
<footer class="mt-3 text-sm text-neutral">
|
||||||
|
<p>
|
||||||
|
Powered by
|
||||||
|
<a href="https://wiki.smartsheep.studio/roadsign/index.html" target="_blank" class="link link-primary">
|
||||||
|
RoadSign
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p class="text-xs">v{{ .Version }}</p>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
6
pkg/hypertext/status/views/not-found.gohtml
Normal file
6
pkg/hypertext/status/views/not-found.gohtml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<h1 class="text-2xl font-bold">404</h1>
|
||||||
|
<h2 class="text-lg">Not Found</h2>
|
||||||
|
|
||||||
|
<div class="mt-3 mx-auto p-5 w-[360px] max-w-screen bg-neutral text-neutral-content rounded">
|
||||||
|
<code class="capitalize">{{ .Message }}</code>
|
||||||
|
</div>
|
6
pkg/hypertext/status/views/request-too-large.gohtml
Normal file
6
pkg/hypertext/status/views/request-too-large.gohtml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<h1 class="text-2xl font-bold">413</h1>
|
||||||
|
<h2 class="text-lg">Auh, you are too big.</h2>
|
||||||
|
|
||||||
|
<div class="mt-3 mx-auto p-5 w-[360px] max-w-screen bg-neutral text-neutral-content rounded">
|
||||||
|
<code class="capitalize">{{ .Message }}</code>
|
||||||
|
</div>
|
6
pkg/hypertext/status/views/too-many-requests.gohtml
Normal file
6
pkg/hypertext/status/views/too-many-requests.gohtml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<h1 class="text-2xl font-bold">429</h1>
|
||||||
|
<h2 class="text-lg">Stop it, you just to fast!</h2>
|
||||||
|
|
||||||
|
<div class="mt-3 mx-auto p-5 w-[360px] max-w-screen bg-neutral text-neutral-content rounded">
|
||||||
|
<code class="capitalize">{{ .Message }}</code>
|
||||||
|
</div>
|
@ -1,14 +1,13 @@
|
|||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import("tailwindcss").Config} */
|
||||||
export default {
|
export default {
|
||||||
content: ["./src/**/*.{js,jsx,ts,tsx}"],
|
content: ["./src/**/*.{js,jsx,ts,tsx}"],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {},
|
extend: {}
|
||||||
},
|
},
|
||||||
daisyui: {
|
daisyui: {
|
||||||
themes: [
|
themes: [
|
||||||
{
|
{
|
||||||
light: {
|
light: {
|
||||||
...require("daisyui/src/theming/themes")["light"],
|
|
||||||
primary: "#4750a3",
|
primary: "#4750a3",
|
||||||
secondary: "#93c5fd",
|
secondary: "#93c5fd",
|
||||||
accent: "#0f766e",
|
accent: "#0f766e",
|
||||||
@ -16,15 +15,22 @@ export default {
|
|||||||
success: "#15803d",
|
success: "#15803d",
|
||||||
warning: "#f97316",
|
warning: "#f97316",
|
||||||
error: "#dc2626",
|
error: "#dc2626",
|
||||||
|
neutral: "#2B3440",
|
||||||
|
"secondary-content": "oklch(98.71% 0.0106 342.55)",
|
||||||
|
"neutral-content": "#D7DDE4",
|
||||||
|
"base-100": "oklch(100% 0 0)",
|
||||||
|
"base-200": "#F2F2F2",
|
||||||
|
"base-300": "#E5E6E6",
|
||||||
|
"base-content": "#1f2937",
|
||||||
|
"color-scheme": "light",
|
||||||
"--rounded-box": "0",
|
"--rounded-box": "0",
|
||||||
"--rounded-btn": "0",
|
"--rounded-btn": "0",
|
||||||
"--rounded-badge": "0",
|
"--rounded-badge": "0",
|
||||||
"--tab-radius": "0",
|
"--tab-radius": "0"
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
dark: {
|
dark: {
|
||||||
...require("daisyui/src/theming/themes")["dark"],
|
|
||||||
primary: "#4750a3",
|
primary: "#4750a3",
|
||||||
secondary: "#93c5fd",
|
secondary: "#93c5fd",
|
||||||
accent: "#0f766e",
|
accent: "#0f766e",
|
||||||
@ -32,13 +38,20 @@ export default {
|
|||||||
success: "#15803d",
|
success: "#15803d",
|
||||||
warning: "#f97316",
|
warning: "#f97316",
|
||||||
error: "#dc2626",
|
error: "#dc2626",
|
||||||
|
neutral: "#2a323c",
|
||||||
|
"neutral-content": "#A6ADBB",
|
||||||
|
"base-100": "#1d232a",
|
||||||
|
"base-200": "#191e24",
|
||||||
|
"base-300": "#15191e",
|
||||||
|
"base-content": "#A6ADBB",
|
||||||
|
"color-scheme": "dark",
|
||||||
"--rounded-box": "0",
|
"--rounded-box": "0",
|
||||||
"--rounded-btn": "0",
|
"--rounded-btn": "0",
|
||||||
"--rounded-badge": "0",
|
"--rounded-badge": "0",
|
||||||
"--tab-radius": "0",
|
"--tab-radius": "0"
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
],
|
]
|
||||||
},
|
},
|
||||||
plugins: [require("daisyui")],
|
plugins: [require("daisyui")]
|
||||||
};
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user