diff --git a/CHANGELOG.md b/CHANGELOG.md index edc7c7a..707efe9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,20 @@ # Changelog This file keeps track of changes in more human-readable fashion +## 7 May 2024 - v0.1.0 +* Began move towards [semantic versioning](https://semver.org/) + * Current version is now 0.1.0 + * Added `version` api method + * Versioned docker images may be provided in the future + * Added version to footer +* Added info page + * Accessed by clicking version number in footer + * Allows you to edit readme.txt + * Provides UI link for `export` api method + * Can be expanded with other functionality in the future (see [TODO](./TODO.md)) +* Bug fixes + * Fixed export function failing + ## 6 May 2024 * 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 diff --git a/README.md b/README.md index cd4a154..8c23bf4 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,7 @@ As a result, I can't guarantee that it's either secure or stable. * Optional Telegram notifications for failed login attempts ## Technical details -Though Hibiscus.txt isn't versioned, [CHANGELOG.md](./CHANGELOG.md) can provide a good overview of changes. -And there is a [TODO.md](./TODO.md) file that shows what I will (or *may*) work on in the future. +[CHANGELOG.md](./CHANGELOG.md) provides a good overview of updates, and [TODO.md](./TODO.md) file shows what I will (or *may*) work on in the future. You can read a relevant entry in my blog [here](https://a71.su/notes/hibiscus/). It provides some useful information and context for why this app exists in the first place. @@ -65,6 +64,8 @@ The [package](https://git.a71.su/Andrew71/hibiscus/packages) provided in this re and there is a [Dockerfile](./Dockerfile) in case you want to compile for something rarer (like a Pi). This repo contains the [compose.yml](./compose.yml) that I personally use. +**Note**: versioned images may be provided in the future, possibly via dockerhub + ### Executable flags If you for some reason decide to run plain executable instead of docker, it supports following flags: ``` @@ -91,11 +92,10 @@ GET /day/ - get file contents for a specific day GET /notes - get JSON list of all named notes GET /notes/ - get file contents for a specific note POST /notes/ - save request body into a named note - -GET /export - get .zip archive of entire data directory - GET /readme - get file contents for readme.txt in data dir's root POST /readme - save request body into readme.txt +GET /export - get .zip archive of entire data directory GET /grace - "true" if grace period is active, otherwise "false" +GET /version - get app's version ``` \ No newline at end of file diff --git a/TODO.md b/TODO.md index 0612c5d..836812b 100644 --- a/TODO.md +++ b/TODO.md @@ -1,8 +1,21 @@ # TODO List of things to add to this project +## Agenda * a 512x logo so I can enable PWA * CI/CD pipeline * Better docs in case others want to use this for some reason * Check export function for improvements -* *Go* dependency-less? <-- this is a terrible idea \ No newline at end of file + +## Brainstorming +Don't expect any of this, these are ideas floating inside my head +* Further info page functionality + * Edit config + * Statistics e.g. mb taken, number of day pages/notes +* Multi-user support through several username-pass keys + * `/data//...` + * This would be an *extremely* breaking change + * How to handle exporting *all*? Admin account? + * I don't need this, unless Hibiscus.txt somehow gets popular why bother? + Is this even a feature that fits the vision? +* *Go* dependency-less? <-- this is a terrible idea diff --git a/api.go b/api.go index 072833f..b4e00bd 100644 --- a/api.go +++ b/api.go @@ -116,3 +116,9 @@ func GraceActiveApi(w http.ResponseWriter, r *http.Request) { HandleWrite(w.Write([]byte(value))) w.WriteHeader(http.StatusOK) } + +// GetVersionApi returns current app version +func GetVersionApi(w http.ResponseWriter, r *http.Request) { + HandleWrite(w.Write([]byte(Info.Version))) + w.WriteHeader(http.StatusOK) +} diff --git a/i18n/en.json b/i18n/en.json index b3d3860..4924f8f 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -4,14 +4,21 @@ "title.today": "Your day so far", "title.days": "Previous days", "title.notes": "Notes", + "title.info": "Info", "link.today": "today", "link.tomorrow": "tomorrow", "link.days": "previous days", "link.notes": "notes", + "link.info": "app information", "description.notes": "/notes/ for a new note", "time.date": "Today is", "time.grace": "grace period active", - "button.save": "Save" + "button.save": "Save", + + "info.version": "Version", + "info.version.link": "source and changelog", + "info.export": "Export data", + "info.readme": "Edit readme.txt" } \ No newline at end of file diff --git a/i18n/ru.json b/i18n/ru.json index 6e16bbf..9d13e2a 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -4,14 +4,21 @@ "title.today": "Сегодняшний день", "title.days": "Предыдущие дни", "title.notes": "Заметки", + "title.info": "Информация", "link.today": "сегодня", "link.tomorrow": "завтра", "link.days": "раньше", "link.notes": "заметки", + "link.info": "системная информация", "description.notes": "/notes/<название> для новой заметки", "time.date": "Сегодня", "time.grace": "льготный период", - "button.save": "Сохранить" + "button.save": "Сохранить", + + "info.version": "Версия", + "info.version.link": "исходный код", + "info.export": "Экспорт данных", + "info.readme": "Редактировать readme.txt" } \ No newline at end of file diff --git a/info.go b/info.go new file mode 100644 index 0000000..7d6668b --- /dev/null +++ b/info.go @@ -0,0 +1,30 @@ +package main + +import ( + "html/template" + "log/slog" + "net/http" +) + +var infoTemplate = template.Must(template.New("").Funcs(templateFuncs).ParseFiles("./pages/base.html", "./pages/info.html")) + +type HibiscusInfo struct { + Version string + SourceLink string +} + +// Info contains app information +var Info = HibiscusInfo{ + Version: "0.1.0", + SourceLink: "https://git.a71.su/Andrew71/hibiscus", +} + +// GetInfo renders the info page +func GetInfo(w http.ResponseWriter, r *http.Request) { + err := infoTemplate.ExecuteTemplate(w, "base", Info) + if err != nil { + slog.Error("error executing template", "error", err) + InternalError(w, r) + return + } +} diff --git a/pages/base.html b/pages/base.html index 69e98da..9aaf128 100644 --- a/pages/base.html +++ b/pages/base.html @@ -33,6 +33,7 @@ {{define "footer"}} {{end}} \ No newline at end of file diff --git a/pages/info.html b/pages/info.html new file mode 100644 index 0000000..0943129 --- /dev/null +++ b/pages/info.html @@ -0,0 +1,8 @@ +{{define "main"}} +

{{ translatableText "title.info" }}

+ +{{end}} \ No newline at end of file diff --git a/public/main.css b/public/main.css index 374719c..8338463 100644 --- a/public/main.css +++ b/public/main.css @@ -13,6 +13,8 @@ body { } a, a:visited { color: #f85552; } a:hover, a:visited:hover { color: #e66868; } +a.no-accent, a.no-accent:visited { color: #454545; } +a.no-accent:hover, a.no-accent:visited:hover { color: #656565; } h2 { margin-bottom:12px; } .list-title { margin-bottom: 0} @@ -65,4 +67,6 @@ header > h1, header > p { color: #f5f0e1; background-color: #383030; } + a.no-accent, a.no-accent:visited { color: #f5f0e1; } + a.no-accent:hover, a.no-accent:visited:hover { color: #a9a8a4; } } \ No newline at end of file diff --git a/routes.go b/routes.go index db6a875..5dd155b 100644 --- a/routes.go +++ b/routes.go @@ -25,7 +25,9 @@ type Entry struct { type formatEntries func([]string) []Entry -var templateFuncs = map[string]interface{}{"translatableText": TranslatableText} +var templateFuncs = map[string]interface{}{ + "translatableText": TranslatableText, + "hibiscusVersion": func() string { return "v" + Info.Version }} var editTemplate = template.Must(template.New("").Funcs(templateFuncs).ParseFiles("./pages/base.html", "./pages/edit.html")) var viewTemplate = template.Must(template.New("").Funcs(templateFuncs).ParseFiles("./pages/base.html", "./pages/entry.html")) var listTemplate = template.Must(template.New("").Funcs(templateFuncs).ParseFiles("./pages/base.html", "./pages/list.html")) @@ -104,7 +106,7 @@ func GetDays(w http.ResponseWriter, r *http.Request) { // Fancy text for today and tomorrow // This looks bad, but strings.Title is deprecated, and I'm not importing a golang.org/x package for this... - // ( chances we ever run into tomorrow are really low) + // (chances we ever run into tomorrow are really low) if v == TodayDate() { dayString = TranslatableText("link.today") dayString = strings.ToTitle(string([]rune(dayString)[0])) + string([]rune(dayString)[1:]) @@ -130,6 +132,7 @@ func GetNotes(w http.ResponseWriter, r *http.Request) { }) } +// GetEntry handles showing a single file, editable or otherwise func GetEntry(w http.ResponseWriter, r *http.Request, title string, filename string, editable bool) { entry, err := ReadFile(filename) if err != nil { @@ -208,3 +211,17 @@ func PostNote(w http.ResponseWriter, r *http.Request) { } http.Redirect(w, r, r.Header.Get("Referer"), 302) } + +// GetReadme calls GetEntry for readme.txt +func GetReadme(w http.ResponseWriter, r *http.Request) { + GetEntry(w, r, "readme", "readme", true) +} + +// PostReadme saves contents of readme.txt file +func PostReadme(w http.ResponseWriter, r *http.Request) { + err := SaveFile("readme", []byte(r.FormValue("text"))) + if err != nil { + slog.Error("error saving readme", "error", err) + } + http.Redirect(w, r, r.Header.Get("Referer"), 302) +} diff --git a/serve.go b/serve.go index 7e38dd9..b6108c8 100644 --- a/serve.go +++ b/serve.go @@ -25,6 +25,9 @@ func Serve() { userRouter.Get("/notes", GetNotes) userRouter.Get("/notes/{note}", GetNote) userRouter.Post("/notes/{note}", PostNote) + userRouter.Get("/info", GetInfo) + userRouter.Get("/readme", GetReadme) + userRouter.Post("/readme", PostReadme) r.Mount("/", userRouter) // API ============= @@ -41,6 +44,7 @@ func Serve() { apiRouter.Post("/today", PostTodayApi) apiRouter.Get("/export", GetExport) apiRouter.Get("/grace", GraceActiveApi) + apiRouter.Get("/version", GetVersionApi) r.Mount("/api", apiRouter) // Static files