pango2 template engine
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
0cf5eb4d40
commit
37cb244ffb
14 changed files with 320 additions and 165 deletions
|
@ -38,3 +38,4 @@ __DONE__
|
|||
* about
|
||||
* migrate to gRPC
|
||||
* routing, not found
|
||||
* template engine
|
||||
|
|
34
formatter.go
34
formatter.go
|
@ -1,9 +1,43 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (file *TreeFile) CategoriesAsUrl() string {
|
||||
escapedCategories := []string{}
|
||||
for _, c := range file.Category {
|
||||
escapedCategories = append(escapedCategories, url.QueryEscape(c))
|
||||
}
|
||||
|
||||
return strings.Join(escapedCategories, "/")
|
||||
}
|
||||
|
||||
func (file *TreeFile) GmiName() string {
|
||||
return strings.Replace(file.Name, ".md", ".gmi", 1)
|
||||
}
|
||||
|
||||
func (tree *Tree) GetIndexFile() (*TreeFile, error) {
|
||||
for _, file := range tree.RootFiles {
|
||||
if file.Name == "index.md" {
|
||||
return file, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("index file not found")
|
||||
}
|
||||
func (tree *Tree) HasIndexFile() bool {
|
||||
_, err := tree.GetIndexFile()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func GetLastFiles(files []*TreeFile) []*TreeFile {
|
||||
sortingFiles := make(map[string]*TreeFile)
|
||||
for _, f := range files {
|
||||
|
|
1
go.mod
1
go.mod
|
@ -4,6 +4,7 @@ go 1.18
|
|||
|
||||
require (
|
||||
git.sr.ht/~adnano/go-gemini v0.2.3
|
||||
github.com/flosch/pongo2/v6 v6.0.0
|
||||
google.golang.org/grpc v1.48.0
|
||||
google.golang.org/protobuf v1.28.1
|
||||
)
|
||||
|
|
5
go.sum
5
go.sum
|
@ -20,6 +20,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
|
|||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/flosch/pongo2/v6 v6.0.0 h1:lsGru8IAzHgIAw6H2m4PCyleO58I40ow6apih0WprMU=
|
||||
github.com/flosch/pongo2/v6 v6.0.0/go.mod h1:CuDpFm47R0uGGE7z13/tTlt1Y6zdxvr2RLT5LJhsHEU=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
|
@ -47,6 +49,8 @@ github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
|||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
|
@ -126,6 +130,7 @@ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
|
|||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
281
main.go
281
main.go
|
@ -6,13 +6,13 @@ import (
|
|||
"fmt"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.sr.ht/~adnano/go-gemini"
|
||||
"git.sr.ht/~adnano/go-gemini/certificate"
|
||||
"github.com/flosch/pongo2/v6"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
|
@ -62,7 +62,10 @@ func main() {
|
|||
|
||||
func processFavicon(_ context.Context, w gemini.ResponseWriter, _ *gemini.Request) {
|
||||
w.SetMediaType("text/plain")
|
||||
w.Write([]byte("\U0001F31B"))
|
||||
_, err := w.Write([]byte("\U0001F31B"))
|
||||
if err != nil {
|
||||
w.WriteHeader(gemini.StatusNotFound, "Out of space")
|
||||
}
|
||||
}
|
||||
|
||||
func process(_ context.Context, w gemini.ResponseWriter, r *gemini.Request) {
|
||||
|
@ -93,12 +96,175 @@ func process(_ context.Context, w gemini.ResponseWriter, r *gemini.Request) {
|
|||
renderSearch(lang, w, r, client)
|
||||
case regexp.MustCompile(`^/(sgs|en)/f/?$`).MatchString(r.URL.Path):
|
||||
renderAllFiles(lang, w, client)
|
||||
case regexp.MustCompile(`^/(sgs|en)/f/([\p{L}\d_+.]+/)+[\d\w]+/[\w_]+.gmi$`).MatchString(r.URL.Path):
|
||||
renderFile(lang, w, r, client)
|
||||
case regexp.MustCompile(`^/(sgs|en)/f/[\p{L}\d_+.]+(/[\p{L}\d_+.]+)*/?$`).MatchString(r.URL.Path):
|
||||
renderCategory(lang, w, r, client)
|
||||
default:
|
||||
w.WriteHeader(gemini.StatusNotFound, "Out of space")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func renderIndex(lang string, w gemini.ResponseWriter, client TreeManagerClient) {
|
||||
langFilter := TreeRequest_Filter{Key: "lang", Value: lang}
|
||||
filters := []*TreeRequest_Filter{&langFilter}
|
||||
|
||||
path := ""
|
||||
tr := TreeRequest{Path: &path, Filter: filters}
|
||||
|
||||
tree, err := client.GetSummery(context.Background(), &tr)
|
||||
if err != nil {
|
||||
w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error")
|
||||
return
|
||||
}
|
||||
|
||||
w.SetMediaType("text/gemini")
|
||||
tpl := pongo2.Must(pongo2.FromFile(fmt.Sprintf("templates/%s/index.gmi", lang)))
|
||||
page, err := tpl.Execute(pongo2.Context{"tree": tree, "lang": lang, "lastFiles": GetLastFiles(tree.Files)})
|
||||
if err != nil {
|
||||
log.Fatalf("template failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = w.Write([]byte(page))
|
||||
if err != nil {
|
||||
w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func renderAbout(lang string, w gemini.ResponseWriter) {
|
||||
w.SetMediaType("text/gemini")
|
||||
tpl := pongo2.Must(pongo2.FromFile(fmt.Sprintf("templates/%s/about.gmi", lang)))
|
||||
page, err := tpl.Execute(pongo2.Context{})
|
||||
if err != nil {
|
||||
log.Fatalf("template failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = w.Write([]byte(page))
|
||||
if err != nil {
|
||||
w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error")
|
||||
}
|
||||
}
|
||||
|
||||
func renderSearch(lang string, w gemini.ResponseWriter, r *gemini.Request, client TreeManagerClient) {
|
||||
q, err := gemini.QueryUnescape(r.URL.RawQuery)
|
||||
if err != nil || q == "" {
|
||||
searchStr := "Input searching tag, created date or word in description"
|
||||
if lang == "sgs" {
|
||||
searchStr = "Ivesk ėiškuoma žīma, sokūrėma data arba žuodi ėš aprašīma"
|
||||
}
|
||||
w.WriteHeader(gemini.StatusInput, searchStr)
|
||||
return
|
||||
}
|
||||
|
||||
descFilter := TreeRequest_Filter{Key: "description", Value: q}
|
||||
createdFilter := TreeRequest_Filter{Key: "created", Value: q}
|
||||
tagFilter := TreeRequest_Filter{Key: "tag", Value: q}
|
||||
filters := []*TreeRequest_Filter{&descFilter, &createdFilter, &tagFilter}
|
||||
|
||||
path := ""
|
||||
tr := TreeRequest{Path: &path, Filter: filters}
|
||||
|
||||
tree, err := client.GetSummery(context.Background(), &tr)
|
||||
if err != nil {
|
||||
w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error")
|
||||
return
|
||||
}
|
||||
|
||||
w.SetMediaType("text/gemini")
|
||||
tpl := pongo2.Must(pongo2.FromFile("templates/search.gmi"))
|
||||
page, err := tpl.Execute(pongo2.Context{"lang": lang, "tree": tree, "q": q})
|
||||
if err != nil {
|
||||
log.Fatalf("template failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = w.Write([]byte(page))
|
||||
if err != nil {
|
||||
w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func renderCategory(lang string, w gemini.ResponseWriter, r *gemini.Request, client TreeManagerClient) {
|
||||
urlParts := strings.Split(strings.Trim(r.URL.Path, "/"), "/")
|
||||
path := "/" + strings.Join(urlParts[2:], "/")
|
||||
|
||||
tr := TreeRequest{Path: &path, Filter: []*TreeRequest_Filter{}}
|
||||
|
||||
tree, err := client.GetSummery(context.Background(), &tr)
|
||||
if err != nil {
|
||||
w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error")
|
||||
return
|
||||
}
|
||||
|
||||
indexFile, err := tree.GetIndexFile()
|
||||
var file *FileContent
|
||||
if err == nil {
|
||||
fr := FileRequest{Id: indexFile.Id}
|
||||
file, err = client.GetFile(context.Background(), &fr)
|
||||
if err != nil {
|
||||
w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
w.SetMediaType("text/gemini")
|
||||
tpl := pongo2.Must(pongo2.FromFile("templates/category.gmi"))
|
||||
fmt.Println(file, &file)
|
||||
page, err := tpl.Execute(pongo2.Context{"lang": lang, "tree": tree, "path": path, "indexFile": file})
|
||||
if err != nil {
|
||||
log.Fatalf("template failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = w.Write([]byte(page))
|
||||
if err != nil {
|
||||
w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func renderFile(lang string, w gemini.ResponseWriter, r *gemini.Request, client TreeManagerClient) {
|
||||
urlParts := strings.Split(r.URL.Path, "/")
|
||||
id := urlParts[len(urlParts)-2]
|
||||
|
||||
fr := FileRequest{Id: id}
|
||||
file, err := client.GetFile(context.Background(), &fr)
|
||||
if err != nil {
|
||||
log.Fatalf("client.GetSummery failed: %v", err)
|
||||
w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error")
|
||||
return
|
||||
}
|
||||
|
||||
category := ""
|
||||
for i, cat := range file.File.Category {
|
||||
category = category + fmt.Sprintf("=> /%s/f/%s .%s└─ %s\n",
|
||||
lang,
|
||||
strings.Join(file.File.Category[:i+1], "/"),
|
||||
strings.Repeat(" ", i),
|
||||
cat,
|
||||
)
|
||||
}
|
||||
content := fmt.Sprintf(
|
||||
"# %s\n\n"+
|
||||
"%s\n\n"+
|
||||
"%s\n%s\n\n"+
|
||||
"%s\n\n[ %s ]",
|
||||
file.File.Description,
|
||||
file.Content,
|
||||
file.File.Created,
|
||||
file.File.Copyright,
|
||||
category,
|
||||
strings.Join(file.File.Tags, " "),
|
||||
)
|
||||
|
||||
w.Write([]byte(content))
|
||||
}
|
||||
|
||||
func renderAllFiles(lang string, w gemini.ResponseWriter, client TreeManagerClient) {
|
||||
langFilter := TreeRequest_Filter{Key: "lang", Value: lang}
|
||||
filters := []*TreeRequest_Filter{&langFilter}
|
||||
|
@ -121,15 +287,7 @@ func renderAllFiles(lang string, w gemini.ResponseWriter, client TreeManagerClie
|
|||
}
|
||||
|
||||
for _, f := range tree.Files {
|
||||
content = content + fmt.Sprintf(
|
||||
"=> /%s/f/%s/%s/%s %s (%s)\n",
|
||||
lang,
|
||||
strings.Join(f.Category, "/"),
|
||||
f.Id,
|
||||
strings.Replace(f.Name, ".md", ".gmi", 1),
|
||||
f.Description,
|
||||
f.Created,
|
||||
)
|
||||
content = appendFileLink(lang, content, f)
|
||||
}
|
||||
|
||||
if lang == "sgs" {
|
||||
|
@ -141,98 +299,15 @@ func renderAllFiles(lang string, w gemini.ResponseWriter, client TreeManagerClie
|
|||
w.Write([]byte(content))
|
||||
}
|
||||
|
||||
func renderSearch(lang string, w gemini.ResponseWriter, r *gemini.Request, client TreeManagerClient) {
|
||||
q, err := gemini.QueryUnescape(r.URL.RawQuery)
|
||||
if err != nil || q == "" {
|
||||
searchStr := "Input searching tag, created date or word in description"
|
||||
if lang == "sgs" {
|
||||
searchStr = "Ivesk ėiškuoma žīma, sokūrėma data arba žuodi ėš aprašīma"
|
||||
}
|
||||
w.WriteHeader(gemini.StatusInput, searchStr)
|
||||
return
|
||||
}
|
||||
|
||||
langFilter := TreeRequest_Filter{Key: "lang", Value: lang}
|
||||
descFilter := TreeRequest_Filter{Key: "description", Value: lang}
|
||||
createdFilter := TreeRequest_Filter{Key: "created", Value: lang}
|
||||
tagFilter := TreeRequest_Filter{Key: "tag", Value: lang}
|
||||
filters := []*TreeRequest_Filter{&langFilter, &descFilter, &createdFilter, &tagFilter}
|
||||
|
||||
path := ""
|
||||
tr := TreeRequest{Path: &path, Filter: filters}
|
||||
|
||||
tree, err := client.GetSummery(context.Background(), &tr)
|
||||
if err != nil {
|
||||
log.Fatalf("client.GetSummery failed: %v", err)
|
||||
w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error")
|
||||
return
|
||||
}
|
||||
fmt.Println(tree)
|
||||
w.Write([]byte("Nieka narada dā\n" +
|
||||
"=> / ← back"))
|
||||
}
|
||||
|
||||
func renderAbout(lang string, w gemini.ResponseWriter) {
|
||||
w.SetMediaType("text/gemini")
|
||||
content, err := os.ReadFile(fmt.Sprintf("templates/%s/about.gmi", lang))
|
||||
if err != nil {
|
||||
w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error")
|
||||
return
|
||||
}
|
||||
|
||||
_, err = w.Write(content)
|
||||
if err != nil {
|
||||
w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func renderIndex(lang string, w gemini.ResponseWriter, client TreeManagerClient) {
|
||||
w.SetMediaType("text/gemini")
|
||||
content, err := os.ReadFile(fmt.Sprintf("templates/%s/index.gmi", lang))
|
||||
if err != nil {
|
||||
w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error")
|
||||
return
|
||||
}
|
||||
page := string(content)
|
||||
|
||||
langFilter := TreeRequest_Filter{Key: "lang", Value: lang}
|
||||
filters := []*TreeRequest_Filter{&langFilter}
|
||||
|
||||
path := ""
|
||||
tr := TreeRequest{Path: &path, Filter: filters}
|
||||
|
||||
tree, err := client.GetSummery(context.Background(), &tr)
|
||||
if err != nil {
|
||||
log.Fatalf("client.GetSummery failed: %v", err)
|
||||
w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error")
|
||||
return
|
||||
}
|
||||
|
||||
categories := ""
|
||||
for c, count := range tree.Categories {
|
||||
categories = categories + fmt.Sprintf("=> /%s/f/%s %s (%d)\n", lang, c, c, count)
|
||||
}
|
||||
page = strings.Replace(page, "{{categories}}", categories, 1)
|
||||
|
||||
tags := ""
|
||||
for t, count := range tree.Tags {
|
||||
tags = tags + fmt.Sprintf("=> /%s/t/%s %s (%d)\n", lang, t, t, count)
|
||||
}
|
||||
page = strings.Replace(page, "{{tags}}", tags, 1)
|
||||
|
||||
lastFiles := ""
|
||||
for _, f := range GetLastFiles(tree.Files) {
|
||||
lastFiles = lastFiles + fmt.Sprintf(
|
||||
func appendFileLink(lang string, content string, f *TreeFile) string {
|
||||
content = content + fmt.Sprintf(
|
||||
"=> /%s/f/%s/%s/%s %s (%s)\n",
|
||||
lang, strings.Join(f.Category, "/"), f.Id, strings.Replace(f.Name, ".md", ".gmi", 1), f.Description, f.Created,
|
||||
lang,
|
||||
strings.Join(f.Category, "/"),
|
||||
f.Id,
|
||||
strings.Replace(f.Name, ".md", ".gmi", 1),
|
||||
f.Description,
|
||||
f.Created,
|
||||
)
|
||||
}
|
||||
page = strings.Replace(page, "{{last_posts}}", lastFiles, 1)
|
||||
|
||||
_, err = w.Write([]byte(page))
|
||||
if err != nil {
|
||||
w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error")
|
||||
return
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
|
10
templates/category.gmi
Normal file
10
templates/category.gmi
Normal file
|
@ -0,0 +1,10 @@
|
|||
{% import "macros.tpl" category_url, text_url %}
|
||||
# Arna alkierios :: {{ path }}
|
||||
|
||||
{% if indexFile %}{{ indexFile.Content }}{% endif %}
|
||||
|
||||
{% for file in tree.RootFiles %}{{ text_url(lang, file) }} {% endfor %}
|
||||
{% if tree.Categories %}### {% if lang == "sgs" %}Kateguorėjės{% else %}Categories{% endif %}
|
||||
{% for cat, count in tree.Categories %}{{ category_url(lang, cat, cat, count) }}
|
||||
{% endfor %} {% endif %}
|
||||
=> {% if lang == "sgs" %}/sgs 🏠 nomėi{% else %}/en 🏠 home{% endif %}
|
|
@ -27,7 +27,7 @@ Articles I wrote simply in files and put them in my self-hosted cloud by created
|
|||
|
||||
From this service it is served to gemini service what is written in Go, too, or to http service (backend on Go, frontend on Vue.js). For communications I use gRPC.
|
||||
|
||||
=> /en/f/gemini/arns-lt more...
|
||||
=> /en/f/gemini/arns_lt more...
|
||||
=> https://g.arns.lt/zordsdavini/zord-tree zord-tree library to build tree from files
|
||||
=> https://g.arns.lt/zordsdavini/arns-lt-tree-push-service service written on top of zord-tree to serve files
|
||||
=> https://g.arns.lt/zordsdavini/arns-lt-gemini gemini service
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
{% import "../macros.tpl" category_url, tag_url, text_url %}
|
||||
# Arna alkierios
|
||||
|
||||
Welcome to my capsule in Gemini space.
|
||||
|
@ -12,16 +13,16 @@ Main themes are spreaded by categories. Each file may have tags what can filter
|
|||
=> /en/s search
|
||||
=> /en/a about
|
||||
|
||||
### Categories
|
||||
{% if tree.Categories %}### Categories
|
||||
{% for cat, count in tree.Categories %}
|
||||
{{ category_url(lang, cat, cat, count) }} {% endfor %} {% endif %}
|
||||
|
||||
{{categories}}
|
||||
{% if tree.Tags %}### Tags
|
||||
{% for tag, count in tree.Tags %}
|
||||
{{ tag_url(lang, tag, count) }} {% endfor %} {% endif %}
|
||||
|
||||
### Tags
|
||||
|
||||
{{tags}}
|
||||
|
||||
### Last posts
|
||||
|
||||
{{last_posts}}
|
||||
{% if lastFiles %}### Last texts
|
||||
{% for file in lastFiles %}
|
||||
{{ text_url(lang, file) }} {% endfor %} {% endif %}
|
||||
|
||||
=> /en/f all texts
|
||||
|
|
5
templates/macros.tpl
Normal file
5
templates/macros.tpl
Normal file
|
@ -0,0 +1,5 @@
|
|||
{% macro category_url(lang, path, category, count) export %}=> /{{ lang }}/f/{{ path }} {{ category }} ({{ count }}) {% endmacro %}
|
||||
|
||||
{% macro tag_url(lang, tag, count) export %}=> /{{ lang }}/t/{{ tag }} {{ tag }} ({{ count }}) {% endmacro %}
|
||||
|
||||
{% macro text_url(lang, file) export %}=> /{{ lang }}/f/{{ file.CategoriesAsUrl() }}/{{ file.Id }}/{{ file.GmiName() }} {{ file.Description }} ({{ file.Created }}) {% endmacro %}
|
9
templates/search.gmi
Normal file
9
templates/search.gmi
Normal file
|
@ -0,0 +1,9 @@
|
|||
{% import "macros.tpl" text_url %}
|
||||
# Arna alkierios :: {% if lang == "sgs" %}Paėiška{% else %}Search{% endif %}
|
||||
|
||||
{% if lang == "sgs" %}Ėiškuota vagol{% else %}Searched by{% endif %}: {{ q }}
|
||||
{% if tree.Files|length == 0 %}{% if lang == "sgs" %}Nieka narada{% else %}Nothing found{% endif %}...{% else %}
|
||||
{% for file in tree.Files %}
|
||||
{{ text_url(lang, file) }} {% endfor %} {% endif %}
|
||||
|
||||
=> {% if lang == "sgs" %}/sgs ← grīžtė{% else %}/en ← back{% endif %}
|
|
@ -27,7 +27,7 @@ Tekstus rašau i paprastiausius failiokus ėr anus talpėno sava „cloud'ė“
|
|||
|
||||
Jau ėš makliavuonės servėsa pajemo i gemini servėsa, katras parašīts ėrgė so Go, arba i http servėsa (backend's ont Go REST, frontend's ont Vue.js). Ruodā nauduojo gRPC pruotuokuola.
|
||||
|
||||
=> /en/f/gemini/arns-lt platiau (onglėškā)
|
||||
=> /en/f/gemini/arns_lt platiau (onglėškā)
|
||||
=> https://g.arns.lt/zordsdavini/zord-tree zord-tree bėbliuoteka sokortė miedi ėš failu
|
||||
=> https://g.arns.lt/zordsdavini/arns-lt-tree-push-service servėsos ont zord-tree somakliavuotė failus ėr anus padoutė tuoliau
|
||||
=> https://g.arns.lt/zordsdavini/arns-lt-gemini gemini servėsos
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
{% import "../macros.tpl" category_url, tag_url, text_url %}
|
||||
# Arna alkierios
|
||||
|
||||
Sveikė atvīkėn i mona kapsolė Gemini ertie.
|
||||
|
@ -12,16 +13,16 @@ Pagrindėnės temas paskėrstītas par kateguorėjės. Kuožnos fails gal turie
|
|||
=> /sgs/s ėiškuok
|
||||
=> /sgs/a aple
|
||||
|
||||
### Kateguorėjės
|
||||
{% if tree.Categories %}### Kateguorėjės
|
||||
{% for cat, count in tree.Categories %}
|
||||
{{ category_url(lang, cat, cat, count) }} {% endfor %} {% endif %}
|
||||
|
||||
{{categories}}
|
||||
{% if tree.Tags %}### Žīmas
|
||||
{% for tag, count in tree.Tags %}
|
||||
{{ tag_url(lang, tag, count) }} {% endfor %} {% endif %}
|
||||
|
||||
### Žīmas
|
||||
|
||||
{{tags}}
|
||||
|
||||
### Paskotėnē īrašā
|
||||
|
||||
{{last_posts}}
|
||||
{% if lastFiles %}### Paskotėnē īrašā
|
||||
{% for file in lastFiles %}
|
||||
{{ text_url(lang, file) }} {% endfor %} {% endif %}
|
||||
|
||||
=> /sgs/f vėsė straipsnē
|
||||
|
|
92
tree.pb.go
92
tree.pb.go
|
@ -231,6 +231,7 @@ type Tree struct {
|
|||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Files []*TreeFile `protobuf:"bytes,1,rep,name=files,proto3" json:"files,omitempty"`
|
||||
RootFiles []*TreeFile `protobuf:"bytes,4,rep,name=rootFiles,proto3" json:"rootFiles,omitempty"`
|
||||
Tags map[string]int32 `protobuf:"bytes,2,rep,name=tags,proto3" json:"tags,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
|
||||
Categories map[string]int32 `protobuf:"bytes,3,rep,name=categories,proto3" json:"categories,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
|
||||
}
|
||||
|
@ -274,6 +275,13 @@ func (x *Tree) GetFiles() []*TreeFile {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (x *Tree) GetRootFiles() []*TreeFile {
|
||||
if x != nil {
|
||||
return x.RootFiles
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Tree) GetTags() map[string]int32 {
|
||||
if x != nil {
|
||||
return x.Tags
|
||||
|
@ -426,36 +434,39 @@ var file_tree_proto_rawDesc = []byte{
|
|||
0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73,
|
||||
0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07,
|
||||
0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63,
|
||||
0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x22, 0x8a, 0x02, 0x0a, 0x04, 0x54, 0x72, 0x65, 0x65, 0x12,
|
||||
0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x22, 0xb8, 0x02, 0x0a, 0x04, 0x54, 0x72, 0x65, 0x65, 0x12,
|
||||
0x24, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e,
|
||||
0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x05,
|
||||
0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x02, 0x20,
|
||||
0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x2e,
|
||||
0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12,
|
||||
0x3a, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20,
|
||||
0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x2e,
|
||||
0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52,
|
||||
0x0a, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x1a, 0x37, 0x0a, 0x09, 0x54,
|
||||
0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61,
|
||||
0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
|
||||
0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3d, 0x0a, 0x0f, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69,
|
||||
0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a,
|
||||
0x02, 0x38, 0x01, 0x22, 0x4b, 0x0a, 0x0b, 0x46, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65,
|
||||
0x6e, 0x74, 0x12, 0x22, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x0e, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x46, 0x69, 0x6c, 0x65,
|
||||
0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
|
||||
0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
|
||||
0x32, 0x6f, 0x0a, 0x0b, 0x54, 0x72, 0x65, 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x12,
|
||||
0x2d, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x53, 0x75, 0x6d, 0x6d, 0x65, 0x72, 0x79, 0x12, 0x11, 0x2e,
|
||||
0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x1a, 0x0a, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x22, 0x00, 0x12, 0x31,
|
||||
0x0a, 0x07, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x69, 0x6e,
|
||||
0x2e, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x6d,
|
||||
0x61, 0x69, 0x6e, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22,
|
||||
0x00, 0x42, 0x03, 0x5a, 0x01, 0x2e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x09, 0x72, 0x6f, 0x6f, 0x74, 0x46, 0x69, 0x6c,
|
||||
0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e,
|
||||
0x54, 0x72, 0x65, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x09, 0x72, 0x6f, 0x6f, 0x74, 0x46, 0x69,
|
||||
0x6c, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,
|
||||
0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x2e, 0x54, 0x61,
|
||||
0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x3a, 0x0a,
|
||||
0x0a, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28,
|
||||
0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x2e, 0x43, 0x61,
|
||||
0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x63,
|
||||
0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x1a, 0x37, 0x0a, 0x09, 0x54, 0x61, 0x67,
|
||||
0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
|
||||
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
|
||||
0x38, 0x01, 0x1a, 0x3d, 0x0a, 0x0f, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73,
|
||||
0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
|
||||
0x01, 0x22, 0x4b, 0x0a, 0x0b, 0x46, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
|
||||
0x12, 0x22, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e,
|
||||
0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x04,
|
||||
0x66, 0x69, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x32, 0x6f,
|
||||
0x0a, 0x0b, 0x54, 0x72, 0x65, 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x12, 0x2d, 0x0a,
|
||||
0x0a, 0x47, 0x65, 0x74, 0x53, 0x75, 0x6d, 0x6d, 0x65, 0x72, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61,
|
||||
0x69, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0a,
|
||||
0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x07,
|
||||
0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x46,
|
||||
0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x69,
|
||||
0x6e, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, 0x00, 0x42,
|
||||
0x03, 0x5a, 0x01, 0x2e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -484,18 +495,19 @@ var file_tree_proto_goTypes = []interface{}{
|
|||
var file_tree_proto_depIdxs = []int32{
|
||||
5, // 0: main.TreeRequest.filter:type_name -> main.TreeRequest.Filter
|
||||
2, // 1: main.Tree.files:type_name -> main.TreeFile
|
||||
6, // 2: main.Tree.tags:type_name -> main.Tree.TagsEntry
|
||||
7, // 3: main.Tree.categories:type_name -> main.Tree.CategoriesEntry
|
||||
2, // 4: main.FileContent.file:type_name -> main.TreeFile
|
||||
1, // 5: main.TreeManager.GetSummery:input_type -> main.TreeRequest
|
||||
0, // 6: main.TreeManager.GetFile:input_type -> main.FileRequest
|
||||
3, // 7: main.TreeManager.GetSummery:output_type -> main.Tree
|
||||
4, // 8: main.TreeManager.GetFile:output_type -> main.FileContent
|
||||
7, // [7:9] is the sub-list for method output_type
|
||||
5, // [5:7] is the sub-list for method input_type
|
||||
5, // [5:5] is the sub-list for extension type_name
|
||||
5, // [5:5] is the sub-list for extension extendee
|
||||
0, // [0:5] is the sub-list for field type_name
|
||||
2, // 2: main.Tree.rootFiles:type_name -> main.TreeFile
|
||||
6, // 3: main.Tree.tags:type_name -> main.Tree.TagsEntry
|
||||
7, // 4: main.Tree.categories:type_name -> main.Tree.CategoriesEntry
|
||||
2, // 5: main.FileContent.file:type_name -> main.TreeFile
|
||||
1, // 6: main.TreeManager.GetSummery:input_type -> main.TreeRequest
|
||||
0, // 7: main.TreeManager.GetFile:input_type -> main.FileRequest
|
||||
3, // 8: main.TreeManager.GetSummery:output_type -> main.Tree
|
||||
4, // 9: main.TreeManager.GetFile:output_type -> main.FileContent
|
||||
8, // [8:10] is the sub-list for method output_type
|
||||
6, // [6:8] is the sub-list for method input_type
|
||||
6, // [6:6] is the sub-list for extension type_name
|
||||
6, // [6:6] is the sub-list for extension extendee
|
||||
0, // [0:6] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_tree_proto_init() }
|
||||
|
|
|
@ -39,6 +39,7 @@ message TreeFile {
|
|||
|
||||
message Tree {
|
||||
repeated TreeFile files = 1;
|
||||
repeated TreeFile rootFiles = 4;
|
||||
map<string, int32> tags = 2;
|
||||
map<string, int32> categories = 3;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue