Translate error pages
This commit is contained in:
parent
8ae76cc8e8
commit
6d37c363bb
10 changed files with 90 additions and 30 deletions
29
CHANGELOG.md
29
CHANGELOG.md
|
@ -1,30 +1,45 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
This file keeps track of changes in a human-readable fashion
|
This file keeps track of changes in a human-readable fashion
|
||||||
|
|
||||||
|
## Upcoming
|
||||||
|
These changes were not yet released
|
||||||
|
|
||||||
|
* Brought default CSS up to date with my personal website
|
||||||
|
* Error pages are now translated
|
||||||
|
|
||||||
## v1.1.4
|
## v1.1.4
|
||||||
|
|
||||||
* Fixed HTML `lang` tag
|
* Fixed HTML `lang` tag
|
||||||
* Theme CSS link is now only present if non-default is set
|
* Theme CSS link is now only present if non-default is set
|
||||||
* Improved template consistency (backend)
|
* Improved template consistency (backend)
|
||||||
|
|
||||||
## v1.1.3
|
## v1.1.3
|
||||||
|
|
||||||
This release mostly consists of backend improvements
|
This release mostly consists of backend improvements
|
||||||
* List items no longer replace hyphens with spaces for consistency
|
* List items no longer replace hyphens with spaces for consistency
|
||||||
* Telegram message for SCRAM is now translatable
|
* Telegram message for SCRAM is now translatable
|
||||||
* Ensured HTML escape in list descriptions
|
* Ensured HTML escape in list descriptions
|
||||||
* Refactored many methods, improved comments
|
* Refactored many methods, improved comments
|
||||||
|
|
||||||
## v1.1.2
|
## v1.1.2
|
||||||
|
|
||||||
This release contains a few bug fixes
|
This release contains a few bug fixes
|
||||||
* Real IPs are now logged (By Rithas K.)
|
* Real IPs are now logged (By Rithas K.)
|
||||||
* CSS now has `box-sizing: border-box` to fix textarea in some cases (By Rithas K.)
|
* CSS now has `box-sizing: border-box` to fix textarea in some cases (By Rithas K.)
|
||||||
* Done some minor code housekeeping
|
* Done some minor code housekeeping
|
||||||
|
|
||||||
## v1.1.1
|
## v1.1.1
|
||||||
|
|
||||||
This release is mostly a technicality, with a move over to GitHub (`ghcr.io/andrew-71/hibiscus`) for packages due to DockerHub's prior anti-Russian actions making old "CI/CD" unsustainable.
|
This release is mostly a technicality, with a move over to GitHub (`ghcr.io/andrew-71/hibiscus`) for packages due to DockerHub's prior anti-Russian actions making old "CI/CD" unsustainable.
|
||||||
|
|
||||||
## v1.1.0
|
## v1.1.0
|
||||||
|
|
||||||
* You can now specify the Telegram *topic* to send notification to via `tg_topic` config key (By Rithas K.)
|
* You can now specify the Telegram *topic* to send notification to via `tg_topic` config key (By Rithas K.)
|
||||||
* The Telegram message is now partially translated
|
* The Telegram message is now partially translated
|
||||||
* Fixed CSS `margin` and `text-align` inherited from my website
|
* Fixed CSS `margin` and `text-align` inherited from my website
|
||||||
|
|
||||||
## v1.0.0
|
## v1.0.0
|
||||||
|
|
||||||
This release includes several **breaking** changes
|
This release includes several **breaking** changes
|
||||||
* Made a new favicon
|
* Made a new favicon
|
||||||
* English is now used as a fallback language, making incomplete translations somewhat usable
|
* English is now used as a fallback language, making incomplete translations somewhat usable
|
||||||
|
@ -38,16 +53,20 @@ This release includes several **breaking** changes
|
||||||
* Comic Sans MS for *everything*
|
* Comic Sans MS for *everything*
|
||||||
* sorry
|
* sorry
|
||||||
|
|
||||||
|
## v0.6.1
|
||||||
|
|
||||||
|
* Fixed date display when using `Local` timezone
|
||||||
|
|
||||||
## v0.6.0
|
## v0.6.0
|
||||||
|
|
||||||
* Replaced config reload with edit in info (api method still available, config reloads on save)
|
* Replaced config reload with edit in info (api method still available, config reloads on save)
|
||||||
* Bug fixes
|
* Bug fixes
|
||||||
* Filenames are now sanitized when writing files
|
* Filenames are now sanitized when writing files
|
||||||
* "Tomorrow" in days list is now also displayed if Timezone is changed and grace period is off
|
* "Tomorrow" in days list is now also displayed if Timezone is changed and grace period is off
|
||||||
* Frontend date display now uses configured timezone
|
* Frontend date display now uses configured timezone
|
||||||
### v0.6.1
|
|
||||||
* Fixed date display when using `Local` timezone
|
|
||||||
|
|
||||||
## v0.5.0
|
## v0.5.0
|
||||||
|
|
||||||
* Added a JS prompt to create new note
|
* Added a JS prompt to create new note
|
||||||
* "Sanitization" for this method is basic and assumes a well-meaning user
|
* "Sanitization" for this method is basic and assumes a well-meaning user
|
||||||
* Old instructions appear if JS is disabled
|
* Old instructions appear if JS is disabled
|
||||||
|
@ -56,6 +75,7 @@ This release includes several **breaking** changes
|
||||||
* Config reload now sets removed values to defaults
|
* Config reload now sets removed values to defaults
|
||||||
|
|
||||||
## v0.4.0
|
## v0.4.0
|
||||||
|
|
||||||
* Customisation changes
|
* Customisation changes
|
||||||
* Added `title` option to config
|
* Added `title` option to config
|
||||||
* Controls the text in the header, "🌺 Hibiscus.txt" by default
|
* Controls the text in the header, "🌺 Hibiscus.txt" by default
|
||||||
|
@ -66,17 +86,20 @@ This release includes several **breaking** changes
|
||||||
* Spaces in config options are now supported (basically just for `title`)
|
* Spaces in config options are now supported (basically just for `title`)
|
||||||
|
|
||||||
## v0.3.0
|
## v0.3.0
|
||||||
|
|
||||||
* Added themes
|
* Added themes
|
||||||
* Picked theme is set by `theme` key in config. Default is ...`default`
|
* Picked theme is set by `theme` key in config. Default is ...`default`
|
||||||
* Themes are defined in `/public/themes/<name>.css` and modify colours (or, theoretically, do more)
|
* Themes are defined in `/public/themes/<name>.css` and modify colours (or, theoretically, do more)
|
||||||
* Current pre-made themes are `default`, `gruvbox` and `high-contrast`
|
* Current pre-made themes are `default`, `gruvbox` and `high-contrast`
|
||||||
|
|
||||||
## v0.2.0
|
## v0.2.0
|
||||||
|
|
||||||
* Added config reload
|
* Added config reload
|
||||||
* Can be reloaded in info page
|
* Can be reloaded in info page
|
||||||
* Can be reloaded with new `reload` api method (be aware of the redirect if referer is present)
|
* Can be reloaded with new `reload` api method (be aware of the redirect if referer is present)
|
||||||
|
|
||||||
## 7 May 2024 - v0.1.0
|
## 7 May 2024 - v0.1.0
|
||||||
|
|
||||||
* Began move towards [semantic versioning](https://semver.org/)
|
* Began move towards [semantic versioning](https://semver.org/)
|
||||||
* Current version is now 0.1.0
|
* Current version is now 0.1.0
|
||||||
* Added `version` api method
|
* Added `version` api method
|
||||||
|
@ -91,6 +114,7 @@ This release includes several **breaking** changes
|
||||||
* Fixed export function failing
|
* Fixed export function failing
|
||||||
|
|
||||||
## 6 May 2024
|
## 6 May 2024
|
||||||
|
|
||||||
* Grace period is now non-inclusive (so `4h` means the switch will happen right at `4:00`, not `4:01`)
|
* Grace period is now non-inclusive (so `4h` means the switch will happen right at `4:00`, not `4:01`)
|
||||||
* Added API method to check if grace period is active
|
* Added API method to check if grace period is active
|
||||||
* Made changes to date display on frontend
|
* Made changes to date display on frontend
|
||||||
|
@ -100,6 +124,7 @@ This release includes several **breaking** changes
|
||||||
But I think it's fine.
|
But I think it's fine.
|
||||||
|
|
||||||
## 5 May 2024
|
## 5 May 2024
|
||||||
|
|
||||||
* Added this changelog
|
* Added this changelog
|
||||||
* Added grace period (as per suggestions)
|
* Added grace period (as per suggestions)
|
||||||
* Set in config like `grace_period=3h26m` (via Go's `time.ParseDuration`)
|
* Set in config like `grace_period=3h26m` (via Go's `time.ParseDuration`)
|
||||||
|
|
9
TODO.md
9
TODO.md
|
@ -1,8 +1,15 @@
|
||||||
# TODO
|
# TODO
|
||||||
List of things to add to this project
|
List of things to add to this project
|
||||||
|
|
||||||
|
## Urgent (1.1.5-2.0.0)
|
||||||
|
* `style.css` in config instead of theme (provide themes as examples in repo)
|
||||||
|
* man page, maybe try packaging for fun
|
||||||
* Auth improvement so it DOESN'T ASK ME FOR PASSWORD EVERY DAY UGH XD
|
* Auth improvement so it DOESN'T ASK ME FOR PASSWORD EVERY DAY UGH XD
|
||||||
|
|
||||||
|
## Nice to have
|
||||||
* Forward/backward buttons for days
|
* Forward/backward buttons for days
|
||||||
|
* Changelog included
|
||||||
|
* Refactor code
|
||||||
|
|
||||||
## Brainstorming
|
## Brainstorming
|
||||||
Don't expect any of this, these are ideas floating inside my head
|
Don't expect any of this, these are ideas floating inside my head
|
||||||
|
@ -17,4 +24,4 @@ Don't expect any of this, these are ideas floating inside my head
|
||||||
Is this even a feature that fits the vision?
|
Is this even a feature that fits the vision?
|
||||||
* Better, *multi-page* docs in case others want to use this for some reason
|
* Better, *multi-page* docs in case others want to use this for some reason
|
||||||
* Check export function for improvements
|
* Check export function for improvements
|
||||||
* *Go* dependency-less? <-- this is a terrible idea
|
* *Go* dependency-less? <-- this is a terrible idea
|
10
config.go
10
config.go
|
@ -120,13 +120,7 @@ func (c *Config) Reload() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "bool":
|
case "bool":
|
||||||
{
|
fieldElem.SetBool(v == "true")
|
||||||
if v == "true" {
|
|
||||||
fieldElem.SetBool(true)
|
|
||||||
} else {
|
|
||||||
fieldElem.SetBool(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "location":
|
case "location":
|
||||||
timezone = v
|
timezone = v
|
||||||
case "duration":
|
case "duration":
|
||||||
|
@ -179,7 +173,7 @@ func ConfigReloadApi(w http.ResponseWriter, r *http.Request) {
|
||||||
HandleWrite(w.Write([]byte(err.Error())))
|
HandleWrite(w.Write([]byte(err.Error())))
|
||||||
}
|
}
|
||||||
if r.Referer() != "" {
|
if r.Referer() != "" {
|
||||||
http.Redirect(w, r, r.Header.Get("Referer"), 302)
|
http.Redirect(w, r, r.Header.Get("Referer"), http.StatusFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -1,4 +1,4 @@
|
||||||
module hibiscus
|
module hibiscus-txt
|
||||||
|
|
||||||
go 1.22
|
go 1.22
|
||||||
|
|
||||||
|
|
|
@ -27,5 +27,9 @@
|
||||||
"info.config": "Edit config",
|
"info.config": "Edit config",
|
||||||
|
|
||||||
"info.telegram.auth_fail": "Failed auth attempt in Hibiscus.txt",
|
"info.telegram.auth_fail": "Failed auth attempt in Hibiscus.txt",
|
||||||
"info.telegram.scram": "Hibiscus SCRAM triggered, shutting down"
|
"info.telegram.scram": "Hibiscus SCRAM triggered, shutting down",
|
||||||
|
|
||||||
|
"error.404": "The page you were looking for doesn't exist",
|
||||||
|
"error.500": "It's probably not your fault, but something broke",
|
||||||
|
"error.prompt": "Go home?"
|
||||||
}
|
}
|
|
@ -27,5 +27,9 @@
|
||||||
"info.config": "Редактировать конфиг",
|
"info.config": "Редактировать конфиг",
|
||||||
|
|
||||||
"info.telegram_notification": "Неверная попытка авторизации в Hibiscus.txt",
|
"info.telegram_notification": "Неверная попытка авторизации в Hibiscus.txt",
|
||||||
"info.telegram.scram": "Активирована функция SCRAM в Hibiscus.txt, сервер выключается"
|
"info.telegram.scram": "Активирована функция SCRAM в Hibiscus.txt, сервер выключается",
|
||||||
|
|
||||||
|
"error.404": "Страница, которую вы ищете, не существует",
|
||||||
|
"error.500": "Что-то сломалось",
|
||||||
|
"error.prompt": "На главную?"
|
||||||
}
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
{{- define "404" -}}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
@ -10,8 +11,9 @@
|
||||||
<body>
|
<body>
|
||||||
<main>
|
<main>
|
||||||
<h1>Error 404 - Not Found</h1>
|
<h1>Error 404 - Not Found</h1>
|
||||||
<p>The page you were looking for doesn't exist or was moved</p>
|
<p>{{ translatableText "error.404" }}</p>
|
||||||
<h3><a href="/">Go home?</a></h3>
|
<p><a href="/">{{ translatableText "error.prompt" }}</a></p>
|
||||||
</main>
|
</main>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
{{ end }}
|
|
@ -1,3 +1,4 @@
|
||||||
|
{{- define "500" -}}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
@ -10,8 +11,9 @@
|
||||||
<body>
|
<body>
|
||||||
<main>
|
<main>
|
||||||
<h1>Error 500 - Internal Server Error</h1>
|
<h1>Error 500 - Internal Server Error</h1>
|
||||||
<p>It's probably not your fault, but something broke</p>
|
<p>{{ translatableText "error.500" }}</p>
|
||||||
<h3><a href="/">Go home?</a></h3>
|
<p><a href="/">{{ translatableText "error.prompt" }}</a></p>
|
||||||
</main>
|
</main>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
{{ end }}
|
|
@ -1,12 +1,12 @@
|
||||||
/* Default theme */
|
/* Default theme */
|
||||||
:root {
|
:root {
|
||||||
/* Light theme */
|
/* Light theme */
|
||||||
--text-light: #454545;
|
--text-light: #2b2a2a;
|
||||||
--bg-light: #f5f0e1;
|
--bg-light: #f4edd7;
|
||||||
|
|
||||||
--clickable-light: #f85552;
|
--clickable-light: #f85552;
|
||||||
--clickable-hover-light: #e66868;
|
--clickable-hover-light: #e66868;
|
||||||
--clickable-label-light: #f5f2ee;
|
--clickable-label-light: #f4edd7;
|
||||||
--text-hover-light: #656565;
|
--text-hover-light: #656565;
|
||||||
|
|
||||||
--textarea-bg-light: #f5f2ee;
|
--textarea-bg-light: #f5f2ee;
|
||||||
|
@ -14,12 +14,12 @@
|
||||||
|
|
||||||
/* Dark theme */
|
/* Dark theme */
|
||||||
--text-dark: #f5f0e1;
|
--text-dark: #f5f0e1;
|
||||||
--bg-dark: #2c2825;
|
--bg-dark: #1b1916;
|
||||||
|
|
||||||
--clickable-dark: #f85552;
|
--clickable-dark: #f85552;
|
||||||
--clickable-hover-dark: #e66868;
|
--clickable-hover-dark: #e66868;
|
||||||
--clickable-label-dark: #f5f2ee;
|
--clickable-label-dark: #f5f2ee;
|
||||||
--text-hover-dark: #656565;
|
--text-hover-dark: #a9a8a4;
|
||||||
|
|
||||||
--textarea-bg-dark: #383030;
|
--textarea-bg-dark: #383030;
|
||||||
--textarea-border-dark: #454545;
|
--textarea-border-dark: #454545;
|
||||||
|
@ -29,21 +29,29 @@
|
||||||
body {
|
body {
|
||||||
color: var(--text-light);
|
color: var(--text-light);
|
||||||
background-color: var(--bg-light);
|
background-color: var(--bg-light);
|
||||||
|
font-size: 18px;
|
||||||
margin: auto auto;
|
margin: auto auto;
|
||||||
max-width: 640px;
|
max-width: 640px;
|
||||||
padding: 1em;
|
padding: 15px;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
font-family: serif;
|
font-family: serif;
|
||||||
min-height: 85vh;
|
min-height: 85vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
h1,h2,h3,h4,h5,h6 { line-height: 1.2 }
|
||||||
|
|
||||||
a, a:visited { color: var(--clickable-light); }
|
a, a:visited { color: var(--clickable-light); }
|
||||||
a:hover, a:visited:hover { color: var(--clickable-hover-light); }
|
a:hover, a:visited:hover { color: var(--clickable-hover-light); }
|
||||||
a.no-accent, a.no-accent:visited { color: var(--text-light); }
|
a.no-accent, a.no-accent:visited { color: var(--text-light); }
|
||||||
a.no-accent:hover, a.no-accent:visited:hover { color: var(--text-hover-light); }
|
a.no-accent:hover, a.no-accent:visited:hover { color: var(--text-hover-light); }
|
||||||
|
|
||||||
h2 { margin-bottom:12px; }
|
ul:not(li ul), ol:not(li ol){
|
||||||
|
margin-left: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
list-style-position: inside;
|
||||||
|
}
|
||||||
|
|
||||||
.list-title { margin-bottom: 0}
|
.list-title { margin-bottom: 0}
|
||||||
.list-desc { margin-top: 0 }
|
.list-desc { margin-top: 0 }
|
||||||
|
|
||||||
|
@ -55,7 +63,7 @@ textarea, input {
|
||||||
resize: vertical;
|
resize: vertical;
|
||||||
outline: 0;
|
outline: 0;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
border: 0.0625em solid var(--textarea-border-light);
|
border: 2px solid var(--textarea-border-light);
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
18
routes.go
18
routes.go
|
@ -55,16 +55,30 @@ var editTemplate = template.Must(template.New("").Funcs(templateFuncs).ParseFS(P
|
||||||
var viewTemplate = template.Must(template.New("").Funcs(templateFuncs).ParseFS(Pages, "pages/base.html", "pages/entry.html"))
|
var viewTemplate = template.Must(template.New("").Funcs(templateFuncs).ParseFS(Pages, "pages/base.html", "pages/entry.html"))
|
||||||
var listTemplate = template.Must(template.New("").Funcs(templateFuncs).ParseFS(Pages, "pages/base.html", "pages/list.html"))
|
var listTemplate = template.Must(template.New("").Funcs(templateFuncs).ParseFS(Pages, "pages/base.html", "pages/list.html"))
|
||||||
|
|
||||||
|
var template404 = template.Must(template.New("404").Funcs(templateFuncs).ParseFS(Pages, "pages/error/404.html"))
|
||||||
// NotFound returns a user-friendly 404 error page.
|
// NotFound returns a user-friendly 404 error page.
|
||||||
func NotFound(w http.ResponseWriter, r *http.Request) {
|
func NotFound(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(404)
|
w.WriteHeader(404)
|
||||||
HandleWrite(w.Write(EmbeddedPage("pages/error/404.html")))
|
|
||||||
|
err := template404.Execute(w, nil)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("error rendering error 404 page", "error", err)
|
||||||
|
InternalError(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var template500 = template.Must(template.New("500").Funcs(templateFuncs).ParseFS(Pages, "pages/error/500.html"))
|
||||||
// InternalError returns a user-friendly 500 error page.
|
// InternalError returns a user-friendly 500 error page.
|
||||||
func InternalError(w http.ResponseWriter, r *http.Request) {
|
func InternalError(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
HandleWrite(w.Write(EmbeddedPage("pages/error/500.html")))
|
|
||||||
|
err := template500.Execute(w, nil)
|
||||||
|
if err != nil { // Well this is awkward
|
||||||
|
slog.Error("error rendering error 500 page", "error", err)
|
||||||
|
HandleWrite(w.Write([]byte("500. Something went *very* wrong.")))
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEntries handles showing a list.
|
// GetEntries handles showing a list.
|
||||||
|
|
Loading…
Reference in a new issue