diff --git a/main.go b/main.go index 965a098..312814e 100644 --- a/main.go +++ b/main.go @@ -39,26 +39,43 @@ type suggestResponse struct { Domain string `json:"domain"` } -type link struct { +type linkBase struct { Short string `json:"short"` Long string `json:"long"` Domain string `json:"domain"` - URL string `json:"url"` Generated bool `json:"generated"` + URL string `json:"url"` +} + +type link struct { + linkBase + + History []linkHistory `json:"history"` +} + +type linkHistory struct { + linkBase + + Until time.Time `json:"until"` } func NewShortLinks(db *sql.DB, domainAliases map[string]string, writableDomains map[string]bool) (*ShortLinks, error) { - tmpl, err := template.New("index.html").ParseFiles("static/index.html") + funcMap := template.FuncMap{ + "lower": strings.ToLower, + "join": strings.Join, + } + + tmpl, err := template.New("index.html").Funcs(funcMap).ParseFiles("static/index.html") if err != nil { return nil, fmt.Errorf("static/index.html: %w", err) } - help, err := template.New("help.html").ParseFiles("static/help.html") + help, err := template.New("help.html").Funcs(funcMap).ParseFiles("static/help.html") if err != nil { return nil, fmt.Errorf("static/help.html: %w", err) } - list, err := template.New("list.html").ParseFiles("static/list.html") + list, err := template.New("list.html").Funcs(funcMap).ParseFiles("static/list.html") if err != nil { return nil, fmt.Errorf("static/list.html: %w", err) } @@ -329,7 +346,34 @@ func (sl *ShortLinks) serveList(w http.ResponseWriter, r *http.Request) { return } - rows, err := sl.db.Query("SELECT short, long, domain, generated FROM links WHERE domain = $1 ORDER BY short ASC", sl.getDomain(r.Host)) + rows, err := sl.db.Query(` + SELECT + short, + long, + domain, + generated, + CURRENT_TIMESTAMP as until, + 0 as is_history + FROM links + WHERE domain = $1 + + UNION ALL + + SELECT + short, + long, + domain, + generated, + until, + 1 as is_history + FROM links_history + WHERE domain = $1 + + ORDER BY + short ASC, + is_history, + until DESC + `, sl.getDomain(r.Host)) if err != nil { sendError(w, http.StatusInternalServerError, "select links: %s", err) return @@ -338,17 +382,25 @@ func (sl *ShortLinks) serveList(w http.ResponseWriter, r *http.Request) { defer rows.Close() links := []link{} + for rows.Next() { link := link{} - err := rows.Scan(&link.Short, &link.Long, &link.Domain, &link.Generated) + hist := linkHistory{} + isHistory := false + + err := rows.Scan(&link.Short, &link.Long, &link.Domain, &link.Generated, &hist.Until, &isHistory) if err != nil { sendError(w, http.StatusInternalServerError, "scan link: %s", err) return } - link.URL = fmt.Sprintf("https://%s/%s", link.Domain, link.Short) - - links = append(links, link) + if !isHistory { + link.URL = fmt.Sprintf("https://%s/%s", link.Domain, link.Short) + links = append(links, link) + } else { + hist.linkBase = link.linkBase + links[len(links)-1].History = append(links[len(links)-1].History, hist) + } } err = sl.list.Execute(w, map[string]any{ diff --git a/static/index.html b/static/index.html index 258a920..1184a9e 100644 --- a/static/index.html +++ b/static/index.html @@ -303,8 +303,12 @@ document.addEventListener('DOMContentLoaded', async () => { await setFromInputs(); }); + if (document.getElementById('long').value == '') { + document.getElementById('long').focus(); + } else { + document.getElementById('short').focus(); + } - document.getElementById('long').focus(); setInputIcons(); }); diff --git a/static/list.html b/static/list.html index ac575da..899d03e 100644 --- a/static/list.html +++ b/static/list.html @@ -13,43 +13,18 @@ body { align-items: center; } -table { - border-spacing: 0; - width: 100%; - table-layout: fixed; -} - -.bg0 { - background-color: var(--sl-color-neutral-50); -} - -.bg1 { - background-color: var(--sl-color-neutral-100); -} - -td div { - display: flex; - flex-direction: row; - align-items: center; - padding: 5px 5px 5px 0; - overflow: clip; - text-wrap: nowrap; -} - -td:first-child { - padding-right: 10px; -} - -tr:hover { - background-color: var(--sl-color-neutral-200); -} - a { text-decoration: none; color: var(--sl-color-primary-700); } -.long, .short { +sl-tree-item { + display: flex; + flex-direction: row; + align-items: center; +} + +.short, .long { margin-left: 7px; } @@ -69,31 +44,29 @@ a { /> @@ -103,36 +76,43 @@ document.addEventListener('DOMContentLoaded', () => {

- - - {{ range .links }} - - - - + + {{ range .links }} + + + + + + + + + {{ .Short }} + + + + + + + + + {{ .Long }} + + + {{ range .History }} + + + + + + + + {{ .Long }} + {{ end }} - -
-
- - - - - - - {{ .Short }} -
-
-
- - - - - - - {{ .Long }} -
-
+ + + {{ end }} + \ No newline at end of file