all left pages
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Arnas Udovicius 2022-08-21 01:33:27 +03:00
parent 37cb244ffb
commit c946b9daba
10 changed files with 126 additions and 89 deletions

View file

@ -5,36 +5,16 @@
My capsule in Gemini space. Built with zord-tree support My capsule in Gemini space. Built with zord-tree support
## URL plan
* / - redirect to /sgs
* /sgs or /en - index template by language. Links to other language, about, search, root categories, all tags, last 10 post, link to all posts
* /[sgs|en]/a - about page
* /[sgs|en]/s - search
* /[sgs|en]/f/<category/path> - display files, subcategories, subtags. If exists index.gmi - display its text
* /[sgs|en]/f/<category/path>/<id>/<name.gmi> - display category path as:
```
=> /sgs/f/c1 c1
=> /sgs/f/c1/c2 └─ c2
=> /sgs/f/c1/c2/c3 , └─ c3
---
> c1
> └─ c2
> , └─ c3
```
display tags, date, copyright
## TODO ## TODO
* add category view
* add tag view
* add file view
* add image or other file not `gmi` download * add image or other file not `gmi` download
* feed * feed
* security * security
* citata from some file * citata from some file
__DONE__ __DONE__
* add category view
* add tag view
* add file view
* about * about
* migrate to gRPC * migrate to gRPC
* routing, not found * routing, not found

View file

@ -20,6 +20,10 @@ func (file *TreeFile) GmiName() string {
return strings.Replace(file.Name, ".md", ".gmi", 1) return strings.Replace(file.Name, ".md", ".gmi", 1)
} }
func (file *TreeFile) CategoryPath() string {
return "/" + strings.Join(file.Category, "/")
}
func (tree *Tree) GetIndexFile() (*TreeFile, error) { func (tree *Tree) GetIndexFile() (*TreeFile, error) {
for _, file := range tree.RootFiles { for _, file := range tree.RootFiles {
if file.Name == "index.md" { if file.Name == "index.md" {
@ -29,14 +33,6 @@ func (tree *Tree) GetIndexFile() (*TreeFile, error) {
return nil, errors.New("index file not found") 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 { func GetLastFiles(files []*TreeFile) []*TreeFile {
sortingFiles := make(map[string]*TreeFile) sortingFiles := make(map[string]*TreeFile)

101
main.go
View file

@ -100,6 +100,9 @@ func process(_ context.Context, w gemini.ResponseWriter, r *gemini.Request) {
renderFile(lang, w, r, client) renderFile(lang, w, r, client)
case regexp.MustCompile(`^/(sgs|en)/f/[\p{L}\d_+.]+(/[\p{L}\d_+.]+)*/?$`).MatchString(r.URL.Path): case regexp.MustCompile(`^/(sgs|en)/f/[\p{L}\d_+.]+(/[\p{L}\d_+.]+)*/?$`).MatchString(r.URL.Path):
renderCategory(lang, w, r, client) renderCategory(lang, w, r, client)
case regexp.MustCompile(`^/(sgs|en)/t/[\p{L}\d_+.]+/?$`).MatchString(r.URL.Path):
renderTag(lang, w, r, client)
// case abruozdelee
default: default:
w.WriteHeader(gemini.StatusNotFound, "Out of space") w.WriteHeader(gemini.StatusNotFound, "Out of space")
} }
@ -214,7 +217,6 @@ func renderCategory(lang string, w gemini.ResponseWriter, r *gemini.Request, cli
w.SetMediaType("text/gemini") w.SetMediaType("text/gemini")
tpl := pongo2.Must(pongo2.FromFile("templates/category.gmi")) 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}) page, err := tpl.Execute(pongo2.Context{"lang": lang, "tree": tree, "path": path, "indexFile": file})
if err != nil { if err != nil {
log.Fatalf("template failed: %v", err) log.Fatalf("template failed: %v", err)
@ -240,29 +242,19 @@ func renderFile(lang string, w gemini.ResponseWriter, r *gemini.Request, client
return return
} }
category := "" w.SetMediaType("text/gemini")
for i, cat := range file.File.Category { tpl := pongo2.Must(pongo2.FromFile("templates/page.gmi"))
category = category + fmt.Sprintf("=> /%s/f/%s .%s└─ %s\n", page, err := tpl.Execute(pongo2.Context{"lang": lang, "file": file})
lang, if err != nil {
strings.Join(file.File.Category[:i+1], "/"), log.Fatalf("template failed: %v", err)
strings.Repeat(" ", i), return
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)) _, err = w.Write([]byte(page))
if err != nil {
w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error")
return
}
} }
func renderAllFiles(lang string, w gemini.ResponseWriter, client TreeManagerClient) { func renderAllFiles(lang string, w gemini.ResponseWriter, client TreeManagerClient) {
@ -274,40 +266,51 @@ func renderAllFiles(lang string, w gemini.ResponseWriter, client TreeManagerClie
tree, err := client.GetSummery(context.Background(), &tr) tree, err := client.GetSummery(context.Background(), &tr)
if err != nil { if err != nil {
log.Fatalf("client.GetSummery failed: %v", err)
w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error") w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error")
return return
} }
content := "# Arnas alkierios :: " w.SetMediaType("text/gemini")
if lang == "sgs" { tpl := pongo2.Must(pongo2.FromFile("templates/all_texts.gmi"))
content = content + "vėsė tekstā\n\n" page, err := tpl.Execute(pongo2.Context{"lang": lang, "tree": tree})
} else { if err != nil {
content = content + "all texts\n\n" log.Fatalf("template failed: %v", err)
return
} }
for _, f := range tree.Files { _, err = w.Write([]byte(page))
content = appendFileLink(lang, content, f) if err != nil {
w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error")
return
} }
if lang == "sgs" {
content = content + "\n=> /sgs ← grīžtė"
} else {
content = content + "\n=> /en ← back"
}
w.Write([]byte(content))
} }
func appendFileLink(lang string, content string, f *TreeFile) string { func renderTag(lang string, w gemini.ResponseWriter, r *gemini.Request, client TreeManagerClient) {
content = content + fmt.Sprintf( urlParts := strings.Split(strings.Trim(r.URL.Path, "/"), "/")
"=> /%s/f/%s/%s/%s %s (%s)\n", tag := urlParts[len(urlParts)-1]
lang, tagFilter := TreeRequest_Filter{Key: "tag", Value: tag}
strings.Join(f.Category, "/"), filters := []*TreeRequest_Filter{&tagFilter}
f.Id,
strings.Replace(f.Name, ".md", ".gmi", 1), path := ""
f.Description, tr := TreeRequest{Path: &path, Filter: filters}
f.Created,
) tree, err := client.GetSummery(context.Background(), &tr)
return content if err != nil {
w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error")
return
}
w.SetMediaType("text/gemini")
tpl := pongo2.Must(pongo2.FromFile("templates/tag.gmi"))
page, err := tpl.Execute(pongo2.Context{"lang": lang, "tree": tree, "tag": tag})
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
}
} }

7
templates/all_texts.gmi Normal file
View file

@ -0,0 +1,7 @@
{% import "macros.tpl" category_url, tag_url, text_url %}
# Arna alkierios :: {% if lang == "sgs" %}Vėsė tekstā{% else %}All texts{% endif %}
{% for file in tree.Files %}{{ text_url(lang, file) }}
{% endfor %}
=> {% if lang == "sgs" %}/sgs 🏠 grīžtė{% else %}/en ← back{% endif %}

View file

@ -1,10 +1,18 @@
{% import "macros.tpl" category_url, text_url %} {% import "macros.tpl" category_url, tag_url, text_url %}
# Arna alkierios :: {{ path }} # Arna alkierios :: {{ path }}
{% if indexFile %}{{ indexFile.Content }}{% endif %} {% if indexFile %}{{ indexFile.Content }}
{% for file in tree.RootFiles %}{{ text_url(lang, file) }} {% endfor %} 🌙 {{ indexFile.File.Created}}
{{ indexFile.File.Copyright }}{% endif %}
{% for file in tree.RootFiles %}{{ text_url(lang, file) }}
{% endfor %}
{% if tree.Categories %}### {% if lang == "sgs" %}Kateguorėjės{% else %}Categories{% endif %} {% if tree.Categories %}### {% if lang == "sgs" %}Kateguorėjės{% else %}Categories{% endif %}
{% for cat, count in tree.Categories %}{{ category_url(lang, cat, cat, count) }} {% for cat, count in tree.Categories %}{{ category_url(lang, tree.Path|add:"/"|add:cat, cat, count) }}
{% endfor %} {% endif %} {% endfor %} {% endif %}
{% if tree.Tags %}### {% if lang == "sgs" %}Žīmas{% else %}Tags{% endif %}
{% for tag, count in tree.Tags %}{{ tag_url(lang, tag, count) }}
{% endfor %} {% endif %}
{% if tree.RootPath %}{{ category_url(lang, tree.RootPath, tree.RootPath, 0) }}{% endif %}
=> {% if lang == "sgs" %}/sgs 🏠 nomėi{% else %}/en 🏠 home{% endif %} => {% if lang == "sgs" %}/sgs 🏠 nomėi{% else %}/en 🏠 home{% endif %}

View file

@ -1,5 +1,5 @@
{% macro category_url(lang, path, category, count) export %}=> /{{ lang }}/f/{{ path }} {{ category }} ({{ count }}) {% endmacro %} {% macro category_url(lang, path, category, count) export %}=> /{{ lang }}/f/{{ path }} 🌿 {{ category }}{% if count %} ({{ count }}){% endif %} {% endmacro %}
{% macro tag_url(lang, tag, count) export %}=> /{{ lang }}/t/{{ tag }} {{ tag }} ({{ 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 %} {% macro text_url(lang, file) export %}=> /{{ lang }}/f/{{ file.CategoriesAsUrl() }}/{{ file.Id }}/{{ file.GmiName() }} {{ file.Description }} ({{ file.Created }}) {% endmacro %}

13
templates/page.gmi Normal file
View file

@ -0,0 +1,13 @@
{% import "macros.tpl" category_url, tag_url, text_url %}
# Arna alkierios :: {{ file.File.Description }}
{{ file.Content }}
🌙 {{ file.File.Created}}
{{ file.File.Copyright }}
{% if file.File.Tags %}### {% if lang == "sgs" %}Žīmas{% else %}Tags{% endif %}
{% for tag in file.File.Tags %}{{ tag_url(lang, tag, 0) }}
{% endfor %} {% endif %}
{{ category_url(lang, file.File.CategoryPath, file.File.Category|last, 0) }}
=> {% if lang == "sgs" %}/sgs 🏠 nomėi{% else %}/en 🏠 home{% endif %}

9
templates/tag.gmi Normal file
View file

@ -0,0 +1,9 @@
{% import "macros.tpl" category_url, tag_url, text_url %}
# Arna alkierios :: [{{ tag }}]
{% for file in tree.Files %}{{ text_url(lang, file) }}
{% endfor %}
{% if tree.Tags %}### {% if lang == "sgs" %}Žīmas{% else %}Tags{% endif %}
{% for tag, count in tree.Tags %}{{ tag_url(lang, tag, count) }}
{% endfor %} {% endif %}
=> {% if lang == "sgs" %}/sgs 🏠 nomėi{% else %}/en 🏠 home{% endif %}

View file

@ -234,6 +234,8 @@ type Tree struct {
RootFiles []*TreeFile `protobuf:"bytes,4,rep,name=rootFiles,proto3" json:"rootFiles,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"` 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"` 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"`
Path string `protobuf:"bytes,5,opt,name=path,proto3" json:"path,omitempty"`
RootPath string `protobuf:"bytes,6,opt,name=rootPath,proto3" json:"rootPath,omitempty"`
} }
func (x *Tree) Reset() { func (x *Tree) Reset() {
@ -296,6 +298,20 @@ func (x *Tree) GetCategories() map[string]int32 {
return nil return nil
} }
func (x *Tree) GetPath() string {
if x != nil {
return x.Path
}
return ""
}
func (x *Tree) GetRootPath() string {
if x != nil {
return x.RootPath
}
return ""
}
type FileContent struct { type FileContent struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@ -434,7 +450,7 @@ var file_tree_proto_rawDesc = []byte{
0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 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, 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, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63,
0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x22, 0xb8, 0x02, 0x0a, 0x04, 0x54, 0x72, 0x65, 0x65, 0x12, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x22, 0xe8, 0x02, 0x0a, 0x04, 0x54, 0x72, 0x65, 0x65, 0x12,
0x24, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 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, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x05,
0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x09, 0x72, 0x6f, 0x6f, 0x74, 0x46, 0x69, 0x6c, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x09, 0x72, 0x6f, 0x6f, 0x74, 0x46, 0x69, 0x6c,
@ -446,7 +462,10 @@ var file_tree_proto_rawDesc = []byte{
0x0a, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 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, 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, 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, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74,
0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1a, 0x0a,
0x08, 0x72, 0x6f, 0x6f, 0x74, 0x50, 0x61, 0x74, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52,
0x08, 0x72, 0x6f, 0x6f, 0x74, 0x50, 0x61, 0x74, 0x68, 0x1a, 0x37, 0x0a, 0x09, 0x54, 0x61, 0x67,
0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 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, 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, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,

View file

@ -42,6 +42,8 @@ message Tree {
repeated TreeFile rootFiles = 4; repeated TreeFile rootFiles = 4;
map<string, int32> tags = 2; map<string, int32> tags = 2;
map<string, int32> categories = 3; map<string, int32> categories = 3;
string path = 5;
string rootPath = 6;
} }
message FileContent { message FileContent {