chore: add twt html formatting
This commit is contained in:
		
							parent
							
								
									ef65b115b7
								
							
						
					
					
						commit
						a7009dcb56
					
				
							
								
								
									
										16
									
								
								http-api.go
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								http-api.go
									
									
									
									
									
								
							@ -194,16 +194,24 @@ func add(preamble lextwt.Comments, text string, v ...any) lextwt.Comments {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return append(preamble, lextwt.NewComment("# "+text))
 | 
						return append(preamble, lextwt.NewComment("# "+text))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					func addKey(preamble lextwt.Comments, key, value string, v ...any) lextwt.Comments {
 | 
				
			||||||
 | 
						if len(v) > 0 {
 | 
				
			||||||
 | 
							value = fmt.Sprintf(value, v...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						comment := fmt.Sprintf("# %s = %s", key, value)
 | 
				
			||||||
 | 
						return append(preamble, lextwt.NewCommentValue(comment, key, value))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func mkPreamble(hostname, uri, path string, limit int, length, offset, end int64) lextwt.Comments {
 | 
					func mkPreamble(hostname, uri, path string, limit int, length, offset, end int64) lextwt.Comments {
 | 
				
			||||||
	uri += path
 | 
						uri += path
 | 
				
			||||||
	preamble := add(mkPreambleDocs(hostname), "twt range = 1  %d", end)
 | 
						preamble := addKey(mkPreambleDocs(hostname), "twt range", "1 %d", end)
 | 
				
			||||||
	preamble = add(preamble, "self = %s%s", hostname, mkqry(uri, limit, offset))
 | 
						preamble = addKey(preamble, "self", "%s%s", hostname, mkqry(uri, limit, offset))
 | 
				
			||||||
	if next := offset + length; next < end {
 | 
						if next := offset + length; next < end {
 | 
				
			||||||
		preamble = add(preamble, "next = %s%s", hostname, mkqry(uri, limit, next))
 | 
							preamble = addKey(preamble, "next", "%s%s", hostname, mkqry(uri, limit, next))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if prev := offset - int64(limit); prev > 0 {
 | 
						if prev := offset - int64(limit); prev > 0 {
 | 
				
			||||||
		preamble = add(preamble, "prev = %s%s", hostname, mkqry(uri, limit, prev))
 | 
							preamble = addKey(preamble, "prev", "%s%s", hostname, mkqry(uri, limit, prev))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return preamble
 | 
						return preamble
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										160
									
								
								http-html.go
									
									
									
									
									
								
							
							
						
						
									
										160
									
								
								http-html.go
									
									
									
									
									
								
							@ -1,9 +1,17 @@
 | 
				
			|||||||
package main
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"go.sour.is/xt/internal/otel"
 | 
						"go.sour.is/xt/internal/otel"
 | 
				
			||||||
 | 
						"go.yarn.social/lextwt"
 | 
				
			||||||
 | 
						"go.yarn.social/types"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type HTML struct {
 | 
					type HTML struct {
 | 
				
			||||||
@ -21,10 +29,156 @@ func (a *HTML) healthcheck(w http.ResponseWriter, r *http.Request) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (a *HTML) home(w http.ResponseWriter, r *http.Request) {
 | 
					func (a *HTML) home(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
	_, span := otel.Span(r.Context())
 | 
						ctx, span := otel.Span(r.Context())
 | 
				
			||||||
	defer span.End()
 | 
						defer span.End()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
 | 
						w.Header().Set("Content-Type", "text/html; charset=utf-8")
 | 
				
			||||||
	w.Write([]byte("ok"))
 | 
					
 | 
				
			||||||
 | 
						uri := r.URL.Query().Get("uri")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						limit := 100
 | 
				
			||||||
 | 
						if v, ok := strconv.Atoi(r.URL.Query().Get("limit")); ok == nil {
 | 
				
			||||||
 | 
							limit = v
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						limit = min(100, max(1, limit))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var offset int64 = 0
 | 
				
			||||||
 | 
						if v, ok := strconv.ParseInt(r.URL.Query().Get("offset"), 10, 64); ok == nil {
 | 
				
			||||||
 | 
							offset = v
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						twts, offset, end, err := fetchTwts(ctx, a.db, uri, limit, offset)
 | 
				
			||||||
 | 
						span.RecordError(err)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							http.Error(w, "ERR", 500)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						preamble := mkPreamble(a.hostname, uri, "", limit, int64(len(twts)), offset, end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reg := &HTWriter{
 | 
				
			||||||
 | 
							lextwt.NewTwtRegistry(preamble, twts),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reg.WriteTo(w)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type reg interface {
 | 
				
			||||||
 | 
						Preamble() lextwt.Comments
 | 
				
			||||||
 | 
						Twts() types.Twts
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type HTWriter struct {
 | 
				
			||||||
 | 
						reg
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *HTWriter) WriteTo(w io.Writer) (int64, error) {
 | 
				
			||||||
 | 
						var output int64
 | 
				
			||||||
 | 
						i, err := fmt.Fprintln(w, `<!DOCTYPE html>
 | 
				
			||||||
 | 
						<html>
 | 
				
			||||||
 | 
							<head>
 | 
				
			||||||
 | 
								<title>The Watcher</title>
 | 
				
			||||||
 | 
								<style>
 | 
				
			||||||
 | 
									body { width: 70%; margin: auto; background: black; color: white; }
 | 
				
			||||||
 | 
									a { color: cornflowerblue; text-decoration: none; }
 | 
				
			||||||
 | 
									main { }
 | 
				
			||||||
 | 
									pre.preamble { color: green; }
 | 
				
			||||||
 | 
									article { border: 1px solid green; border-radius: 4px; padding: 4px; margin: 2px; }
 | 
				
			||||||
 | 
									.h-card { columns: 2; }
 | 
				
			||||||
 | 
									.h-card .author { display: flex; }
 | 
				
			||||||
 | 
									.h-card .icon { width: 36px; margin: 4px; }
 | 
				
			||||||
 | 
									.h-card .u-photo { width: 32px; }
 | 
				
			||||||
 | 
									.h-card .date { text-align: right;}
 | 
				
			||||||
 | 
									section { overflow: scroll; }
 | 
				
			||||||
 | 
								</style
 | 
				
			||||||
 | 
							</head>
 | 
				
			||||||
 | 
						<body>
 | 
				
			||||||
 | 
						<pre class='preamble'>
 | 
				
			||||||
 | 
						`)
 | 
				
			||||||
 | 
						output += int64(i)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return output, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, c := range r.Preamble() {
 | 
				
			||||||
 | 
							if key := c.Key(); key != "" {
 | 
				
			||||||
 | 
								value := mkValue(c.Value())
 | 
				
			||||||
 | 
								i, err = fmt.Fprintf(w, "# %s = %s\n", key, value)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								i, err = fmt.Fprintln(w, c.Text())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							output += int64(i)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return output, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						i, err = fmt.Fprintln(w, "</pre><main>")
 | 
				
			||||||
 | 
						output += int64(i)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, twt := range r.Twts() {
 | 
				
			||||||
 | 
							twter := twt.Twter()
 | 
				
			||||||
 | 
							uri, err := url.Parse(twter.URI)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								uri = &url.URL{
 | 
				
			||||||
 | 
									Scheme: "HTTPS",
 | 
				
			||||||
 | 
									Host:   "unknown.txt",
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							i, err = fmt.Fprintf(w, `
 | 
				
			||||||
 | 
								<article>
 | 
				
			||||||
 | 
									<header class="u-author h-card">
 | 
				
			||||||
 | 
										<div class="author">
 | 
				
			||||||
 | 
											<div class="icon">
 | 
				
			||||||
 | 
												<a href="%s" class="u-url">
 | 
				
			||||||
 | 
													<img class="avatar u-photo" src="%s" alt="" loading="lazy">
 | 
				
			||||||
 | 
												</a>
 | 
				
			||||||
 | 
											</div>			  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											<div class="author-name">
 | 
				
			||||||
 | 
												<div class="p-name">				
 | 
				
			||||||
 | 
													<a href="%s">%s</a>
 | 
				
			||||||
 | 
												</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												<div class="p-org">
 | 
				
			||||||
 | 
													<a target="_blank" href="%s">%s</a>
 | 
				
			||||||
 | 
												</div>
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										
 | 
				
			||||||
 | 
										<div class="date">
 | 
				
			||||||
 | 
											<div><a class="u-url" href="%s">%s</a></div>
 | 
				
			||||||
 | 
											<div><small>%s</small></div>
 | 
				
			||||||
 | 
										</div>	
 | 
				
			||||||
 | 
									</header>	
 | 
				
			||||||
 | 
									<section>
 | 
				
			||||||
 | 
										<pre>%M</pre>
 | 
				
			||||||
 | 
									</section>
 | 
				
			||||||
 | 
								  </div>		  
 | 
				
			||||||
 | 
								</article>
 | 
				
			||||||
 | 
							`, "/?uri="+twter.URI, "https://txt.sour.is/user/xuu/avatar",
 | 
				
			||||||
 | 
								"/?uri="+twter.URI, twter.Nick,
 | 
				
			||||||
 | 
								twter.URI, uri.Host,
 | 
				
			||||||
 | 
								"/conv/"+twt.Hash(), fmt.Sprintf("<time datetime='%s'>%s</time>", twt.Created().Format(time.RFC3339), twt.Created().Format(time.RFC822)),
 | 
				
			||||||
 | 
								time.Since(twt.Created()).Round(time.Second).String(), twt,
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
							output += int64(i)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return output, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						i, err = fmt.Fprintln(w, "</main></body>")
 | 
				
			||||||
 | 
						output += int64(i)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return output, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func mkValue(v string) string {
 | 
				
			||||||
 | 
						if strings.HasPrefix(v, "http") {
 | 
				
			||||||
 | 
							return fmt.Sprintf(`<a href="%s">%s</a>`, v, v)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return v
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user