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', () => {
-
| - - | -- - | -