[static]: simple html framework
This commit is contained in:
parent
5d507d2810
commit
400e8e2562
1
go.sum
1
go.sum
@ -2,6 +2,7 @@ gitea.difrex.ru/Umbrella/fetcher v0.0.0-20200723122826-e8bbdd12256b h1:K0vLl90b8
|
|||||||
gitea.difrex.ru/Umbrella/fetcher v0.0.0-20200723122826-e8bbdd12256b/go.mod h1:rcNfqAtzWqj1MsvxDuqTuqTNiJ7r6f1reQvsuUaiHYY=
|
gitea.difrex.ru/Umbrella/fetcher v0.0.0-20200723122826-e8bbdd12256b/go.mod h1:rcNfqAtzWqj1MsvxDuqTuqTNiJ7r6f1reQvsuUaiHYY=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/elastic/go-elasticsearch/v6 v6.8.10/go.mod h1:UwaDJsD3rWLM5rKNFzv9hgox93HoX8utj1kxD9aFUcI=
|
||||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
||||||
|
@ -232,6 +232,7 @@ func Serve(listen string, es ESConf) {
|
|||||||
// Simple and clean SSR UI
|
// Simple and clean SSR UI
|
||||||
ssr := newSSR("./templates", es)
|
ssr := newSSR("./templates", es)
|
||||||
r.HandleFunc("/", ssr.ssrRootHandler)
|
r.HandleFunc("/", ssr.ssrRootHandler)
|
||||||
|
r.HandleFunc("/forum", ssr.ssrForumHandler)
|
||||||
|
|
||||||
http.Handle("/", r)
|
http.Handle("/", r)
|
||||||
|
|
||||||
|
@ -366,6 +366,42 @@ func (es ESConf) GetXC(echoes string) []string {
|
|||||||
return counts
|
return counts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ThreadBucket struct {
|
||||||
|
DocCount int64 `json:"doc_count"`
|
||||||
|
Key string `json:"key"`
|
||||||
|
Post Hits
|
||||||
|
}
|
||||||
|
|
||||||
|
func (es ESConf) GetThreads(echoes ...string) (posts []i2es.ESDoc) {
|
||||||
|
query := `{"sort":[{"date":{"order":"desc"}}],"aggs":{"topics":{"terms":{"field":"topicid.keyword","size":100},"aggs":{"post":{"top_hits":{"size":1,"sort":[{"date":{"order":"desc"}}],"_source":{"include":["subg","author","date","echo","topicid","address"]}}}}}},"query":{"bool":{"must":[{"range":{"date":{"from":"now-30d","to":"now-0d"}}},{"constant_score":{"filter":{"terms":{"echo.keyword":["idec.talks","pipe.2032","linux.14","develop.16","dynamic.local","std.club","std.hugeping","oldpc.51t.ru","difrex.blog","ii.test.14"]}}}}]}}}`
|
||||||
|
req, err := http.NewRequest("POST", es.searchURI(), bytes.NewReader([]byte(query)))
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
var data ESAggsResp
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, bucket := range data.Aggregations.Topics.Buckets {
|
||||||
|
for _, post := range bucket.Post.Hits.Hits {
|
||||||
|
posts = append(posts, post.Source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (es ESConf) GetLatestPosts(sum int) []i2es.ESDoc {
|
func (es ESConf) GetLatestPosts(sum int) []i2es.ESDoc {
|
||||||
query := fmt.Sprintf(`{"sort": [{"date": {"order": "desc"}}, {"_score": {"order": "desc" }}], "size": %d}`, sum)
|
query := fmt.Sprintf(`{"sort": [{"date": {"order": "desc"}}, {"_score": {"order": "desc" }}], "size": %d}`, sum)
|
||||||
req, err := http.NewRequest("POST", es.searchURI(), bytes.NewBuffer([]byte(query)))
|
req, err := http.NewRequest("POST", es.searchURI(), bytes.NewBuffer([]byte(query)))
|
||||||
|
@ -3,7 +3,6 @@ gitea.difrex.ru/Umbrella/fetcher v0.0.0-20200723122826-e8bbdd12256b/go.mod h1:rc
|
|||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||||
github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA=
|
|
||||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
||||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
@ -27,7 +26,6 @@ golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm
|
|||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210324051608-47abb6519492 h1:Paq34FxTluEPvVyayQqMPgHm+vTOrIifmcYxFBx9TLg=
|
golang.org/x/sys v0.0.0-20210324051608-47abb6519492 h1:Paq34FxTluEPvVyayQqMPgHm+vTOrIifmcYxFBx9TLg=
|
||||||
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
53
node/ssr.go
53
node/ssr.go
@ -1,9 +1,7 @@
|
|||||||
package node
|
package node
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"html/template"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
|
|
||||||
"gitea.difrex.ru/Umbrella/fetcher/i2es"
|
"gitea.difrex.ru/Umbrella/fetcher/i2es"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@ -21,47 +19,40 @@ func newSSR(templatesDir string, es ESConf) *ssr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ssr) templatePath(name string) string {
|
type PageData struct {
|
||||||
return s.templatesDir + string(os.PathSeparator) + name
|
Echoes []echo
|
||||||
|
CurrentPage string
|
||||||
|
Posts []i2es.ESDoc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ssr) getTemplate(name string) (*template.Template, error) {
|
func (s *ssr) newPageData(page string, posts []i2es.ESDoc) *PageData {
|
||||||
t := template.New("root.html")
|
return &PageData{
|
||||||
|
Echoes: s.es.GetEchoesList(),
|
||||||
tpl, err := t.ParseFiles(
|
Posts: posts,
|
||||||
s.templatePath(name+".html"),
|
CurrentPage: page,
|
||||||
s.templatePath("style.html"),
|
|
||||||
s.templatePath("echoes.html"),
|
|
||||||
s.templatePath("post.html"),
|
|
||||||
s.templatePath("latest_posts.html"),
|
|
||||||
s.templatePath("header.html"),
|
|
||||||
s.templatePath("footer.html"),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return tpl, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ssr) ssrRootHandler(w http.ResponseWriter, r *http.Request) {
|
func (s *ssr) ssrRootHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
LogRequest(r)
|
|
||||||
|
|
||||||
tpl, err := s.getTemplate("root")
|
tpl, err := s.getTemplate("root")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var root struct {
|
if err := tpl.Execute(w, s.newPageData("", s.es.GetLatestPosts(50))); err != nil {
|
||||||
Echoes []echo
|
log.Error(err)
|
||||||
CurrentPage string
|
}
|
||||||
Posts []i2es.ESDoc
|
}
|
||||||
}
|
|
||||||
root.Echoes = s.es.GetEchoesList()
|
func (s *ssr) ssrForumHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
root.Posts = s.es.GetLatestPosts(100)
|
tpl, err := s.getTemplate("forum")
|
||||||
|
if err != nil {
|
||||||
if err := tpl.Execute(w, root); err != nil {
|
log.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tpl.Execute(w, s.newPageData("", s.es.GetThreads())); err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,20 @@ type ESSearchResp struct {
|
|||||||
Hits `json:"hits"`
|
Hits `json:"hits"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ESAggsResp struct {
|
||||||
|
ESSearchResp
|
||||||
|
Aggregations struct {
|
||||||
|
Topics struct {
|
||||||
|
Buckets []struct {
|
||||||
|
Bucket
|
||||||
|
Post struct {
|
||||||
|
Hits Hits `json:"hits"`
|
||||||
|
} `json:"post"`
|
||||||
|
} `json:"buckets"`
|
||||||
|
} `json:"topics"`
|
||||||
|
} `json:"aggregations"`
|
||||||
|
}
|
||||||
|
|
||||||
type Hits struct {
|
type Hits struct {
|
||||||
Total int64 `json:"total"`
|
Total int64 `json:"total"`
|
||||||
MaxScore float64 `json:"max_score"`
|
MaxScore float64 `json:"max_score"`
|
||||||
|
98
node/templates.go
Normal file
98
node/templates.go
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
package node
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Common templates
|
||||||
|
CommonTplDir = "common"
|
||||||
|
|
||||||
|
// Reusable components
|
||||||
|
ComponentsTplDir = "components"
|
||||||
|
|
||||||
|
// Something like component of components
|
||||||
|
MetaTplDir = "meta"
|
||||||
|
|
||||||
|
// Main pages
|
||||||
|
ViewsTplDir = "views"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *ssr) tplDir(name string) string {
|
||||||
|
return strings.Join([]string{s.templatesDir, name}, string(os.PathSeparator))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ssr) readTplDir(name string) ([]string, error) {
|
||||||
|
var paths []string
|
||||||
|
|
||||||
|
dir, err := os.ReadDir(s.templatesDir + string(os.PathSeparator) + name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range dir {
|
||||||
|
if strings.HasSuffix(entry.Name(), ".html") {
|
||||||
|
paths = append(paths, strings.Join(
|
||||||
|
[]string{s.templatesDir, name, entry.Name()},
|
||||||
|
string(os.PathSeparator)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return paths, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ssr) tplsPaths() ([]string, error) {
|
||||||
|
var paths []string
|
||||||
|
// Read common dir
|
||||||
|
commonTpls, err := s.readTplDir(CommonTplDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
paths = append(paths, commonTpls...)
|
||||||
|
|
||||||
|
// Read components dir
|
||||||
|
compTpls, err := s.readTplDir(ComponentsTplDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
paths = append(paths, compTpls...)
|
||||||
|
|
||||||
|
// Read meta dir
|
||||||
|
metaTpls, err := s.readTplDir(MetaTplDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
paths = append(paths, metaTpls...)
|
||||||
|
|
||||||
|
return paths, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ssr) componentsForTemplate(name string) []string {
|
||||||
|
paths := []string{s.tplDir(ViewsTplDir) + string(os.PathSeparator) + name + ".html"}
|
||||||
|
tpls, err := s.tplsPaths()
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return paths
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(paths, tpls...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ssr) templatePath(name string) string {
|
||||||
|
return s.templatesDir + string(os.PathSeparator) + name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ssr) getTemplate(name string) (*template.Template, error) {
|
||||||
|
t := template.New(name + ".html")
|
||||||
|
|
||||||
|
tpl, err := t.ParseFiles(s.componentsForTemplate(name)...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tpl, nil
|
||||||
|
}
|
12
templates/common/footer.html
Normal file
12
templates/common/footer.html
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{{ define "footer" }}
|
||||||
|
|
||||||
|
<!-- <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/js/bootstrap.bundle.min.js" integrity="sha384-b5kHyXgcpbZJO/tY9Ul7kGkf1S0CWuKcCD38l8YkeH8z8QjE0GmW1gYU5S9FOnJ0" crossorigin="anonymous"></script> -->
|
||||||
|
<p class="text-center">
|
||||||
|
copyleft 2021 difrex at lessmore dot pw;
|
||||||
|
|
||||||
|
<a href="#">source code</a>
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
{{ end }}
|
@ -6,27 +6,37 @@
|
|||||||
<body class="dynamic-bg">
|
<body class="dynamic-bg">
|
||||||
|
|
||||||
<!-- Panel -->
|
<!-- Panel -->
|
||||||
<nav class="navbar navbar-expand-lg position-static navbar-dark dynamic-panel mb-2">
|
<nav class="navbar navbar-expand-lg position-static navbar-dark dynamic-panel mb-2 shadow">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<a class="navbar-brand" href="#">static | more</a>
|
<a class="navbar-brand" href="#">static | more</a>
|
||||||
|
|
||||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||||
<li class="nav-item dropdown">
|
<div class="nav-item">
|
||||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
<a class="nav-link"
|
||||||
menu
|
href="#"
|
||||||
|
role="button">
|
||||||
|
feed
|
||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu" aria-labelledby="navbarDropdown">
|
</div>
|
||||||
<li><a class="dropdown-item" href="#">Action</a></li>
|
<div class="nav-item">
|
||||||
<li><a class="dropdown-item" href="#">Another action</a></li>
|
<a class="nav-link"
|
||||||
<li><hr class="dropdown-divider"></li>
|
href="/forum"
|
||||||
<li><a class="dropdown-item" href="#">Something else here</a></li>
|
role="button">
|
||||||
</ul>
|
forum
|
||||||
</li>
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="nav-item">
|
||||||
|
<a class="nav-link"
|
||||||
|
href="#"
|
||||||
|
role="button">
|
||||||
|
new
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</ul>
|
</ul>
|
||||||
<form class="d-flex">
|
<form class="d-flex">
|
||||||
<input class="form-control me-2 bg-dark text-black-50 border-0" type="search" placeholder='search query' aria-label="Search">
|
<input class="form-control me-2 bg-dark text-black-50 border-0" type="search" placeholder='search query' aria-label="Search">
|
||||||
<button class="btn btn-outline-success" type="submit">Search</button>
|
<button class="btn btn-outline-light" type="submit">Search</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
@ -3,7 +3,7 @@
|
|||||||
<div class="echoList">
|
<div class="echoList">
|
||||||
{{ range .Echoes }}
|
{{ range .Echoes }}
|
||||||
|
|
||||||
<div class="card dynamic-echo mb-1">
|
<div class="card dynamic-echo dynamic-opacity-95 mb-1">
|
||||||
<div class="card-header text-info container-fluid">
|
<div class="card-header text-info container-fluid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-9">
|
<div class="col-9">
|
@ -6,7 +6,9 @@
|
|||||||
{ template "post" .Posts[0] }
|
{ template "post" .Posts[0] }
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<div class="card container dynamic-post dynamic-opacity-95 p-3 mb-3">
|
<div
|
||||||
|
id="{{ .MsgID }}"
|
||||||
|
class="card container dynamic-post dynamic-opacity-95 p-3 mb-3">
|
||||||
<div class="card-body p-3">
|
<div class="card-body p-3">
|
||||||
<h3 class="card-title">
|
<h3 class="card-title">
|
||||||
<a class="dynamic-post-title" href="/ssr#{{ .TopicID }}">{{ .Subg }}</a>
|
<a class="dynamic-post-title" href="/ssr#{{ .TopicID }}">{{ .Subg }}</a>
|
||||||
@ -18,7 +20,7 @@
|
|||||||
{{ .Date }}
|
{{ .Date }}
|
||||||
@<a
|
@<a
|
||||||
title="{{ .Author }} posts"
|
title="{{ .Author }} posts"
|
||||||
href="/ssr#/author/{{ .Author }}">{{ .Author }}</a> ->
|
href="#/author/{{ .Author }}">{{ .Author }}</a> ->
|
||||||
{{ if .Repto }}<a href="/ssr#{{.Repto}}">{{ .To }}</a>
|
{{ if .Repto }}<a href="/ssr#{{.Repto}}">{{ .To }}</a>
|
||||||
{{ else }}{{ .To }}{{ end }}
|
{{ else }}{{ .To }}{{ end }}
|
||||||
</p>
|
</p>
|
||||||
@ -28,7 +30,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer text-white-50">
|
<div class="card-footer text-white-50">
|
||||||
[<a href="/ssr#{{ .MsgID }}">#</a>] [<a href="/ssr#{{ .MsgID }}">reply</a>]
|
[<a href="#{{ .MsgID }}">#</a>] [<a href="/ssr#{{ .MsgID }}">reply</a>]
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1,8 +0,0 @@
|
|||||||
{{ define "footer" }}
|
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/js/bootstrap.bundle.min.js" integrity="sha384-b5kHyXgcpbZJO/tY9Ul7kGkf1S0CWuKcCD38l8YkeH8z8QjE0GmW1gYU5S9FOnJ0" crossorigin="anonymous"></script>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
{{ end }}
|
|
29
templates/meta/forum.html
Normal file
29
templates/meta/forum.html
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{{ define "forum" }}
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row container-fluid">
|
||||||
|
<table class="table table-sm border-0 dynamic-bg dynamic-post-title dynamic-opacity-95 shadow">
|
||||||
|
<caption>latest threads</caption>
|
||||||
|
<thead>
|
||||||
|
<th scope="col">echo</th>
|
||||||
|
<th scope="col">thread</th>
|
||||||
|
<th scope="col">latest</th>
|
||||||
|
<th scope="col">address</th>
|
||||||
|
<th scope="col">date</th>
|
||||||
|
<!-- <th scope="col">comments</th> -->
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{ range .Posts }}
|
||||||
|
<tr>
|
||||||
|
<td>{{ .Echo }}</td>
|
||||||
|
<td>{{ .Subg }}</td>
|
||||||
|
<td>{{ .Author }}</td>
|
||||||
|
<td>{{ .Address }}</td>
|
||||||
|
<td>{{ .Date }}</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
17
templates/views/forum.html
Normal file
17
templates/views/forum.html
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{{ template "header" . }}
|
||||||
|
|
||||||
|
{{ template "style" }}
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-2">
|
||||||
|
{{ template "echoes" . }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-9 text-primary">
|
||||||
|
{{ template "forum" . }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{ template "footer" . }}
|
Loading…
Reference in New Issue
Block a user