Recently Written · git

sbm-webpublish

Publish an sbm bookmark file as a list of links on a web site

git clone https://github.com/equwal/sbm-webpublish

Log | Files | Refs


README.md (7261 bytes)

1 # sbm-webpublish
2 
3 sbm-webpublish publishes an [sbm](https://github.com/equwal/sbm) bookmark
4 file as a list of links on a web site. The links page of
5 [recentlywritten.com](https://recentlywritten.com/links.html) uses it.
6 
7 It is one small Go program. At run time it needs only the Go standard
8 library. The tests use rapid for property tests. The program keeps the
9 links in one plain sbm file, one line for each link:
10 
11     URL<tab>description<tab>tag tag tag<tab>feed
12 
13 so `bm`, `cut`, `awk` and `grep` can read the same file. The feed is the
14 address of the RSS or Atom feed of the site. It is optional: a line without
15 a feed has the three fields of `bm`. `bm` reads only the first three
16 fields. `bm --merge` does not copy the feed.
17 
18 ## Get the links
19 
20 The program makes each output from the file at each request. The outputs
21 contain only http and https links.
22 
23 | Path | Format |
24 |---|---|
25 | `/links/links.txt` | The links as sbm lines, oldest first. The fourth field is the feed of the site, if there is one. |
26 | `/links/atom.xml` | An Atom feed, newest first. Each tag of a link is a category. |
27 | `/links/list.html` | The links as an HTML fragment, under each of their tags, with an "rss" link after each link that has a feed. A page of the site includes it with nginx SSI. |
28 
29 Each output sends `Last-Modified` and answers `If-Modified-Since` with
30 `304 Not Modified`.
31 
32 To follow the links in [sfeed](https://codemadness.org/sfeed.html), add
33 a line to `feeds()` in your sfeedrc:
34 
35     feed 'recentlywritten links' 'https://recentlywritten.com/links/atom.xml'
36 
37 The plain text works with the usual tools:
38 
39     curl -s https://recentlywritten.com/links/links.txt | cut -f1
40     curl -s https://recentlywritten.com/links/links.txt | bm --merge
41     curl -s https://recentlywritten.com/links/links.txt | dmenu -l 20 | cut -f1 | xargs xdg-open
42 
43 To follow the sites of the links, make a `feed` line for sfeedrc from each
44 link that has a feed. The name of each feed comes from its URL:
45 
46     curl -s https://recentlywritten.com/links/links.txt | awk -F'\t' '$4 {
47         n = $1; sub(/^[a-z]+:\/\//, "", n); gsub(/[^A-Za-z0-9._-]/, "-", n)
48         printf "\tfeed '\''%s'\'' '\''%s'\''\n", n, $4 }'
49 
50 ## Dates
51 
52 A feed needs the time of each link, and an sbm line has no time. So the
53 admin page writes a date line above the links that it adds:
54 
55     # 2026-09-25T08:00:00Z
56     https://suckless.org/	software that sucks less	code unix
57 
58 `bm` ignores each line that starts with `#`, so the file stays an sbm file.
59 In the feed, a link gets the time of the last date line above it. A link
60 below no date line gets the time of the file. If you add links to the file
61 with an editor, write a date line above them.
62 
63 ## The admin page
64 
65 `/links/admin` lets the owner add a link with its feed, set the feed of a
66 link, delete a link and import a file. It uses HTTP basic auth. Any user
67 name works. The password is the value of `ADMIN_PASSWORD`. The server
68 refuses a form POST that comes from another site.
69 
70 The import takes an sbm file or the Netscape HTML that browsers export.
71 The folders of a browser bookmark become its tags. The import skips a URL
72 that is a link already, but a line with a feed gives its feed to a link
73 that has none. Two URLs are the same link when they differ only in scheme,
74 a leading `www.`, trailing slashes or the case of the host, as in `bm`.
75 
76 ## Add links in a shell
77 
78 On the server, `sbm-webpublish merge` adds the links of its input, as the
79 import does:
80 
81     bm --list | ssh root@example.com 'LINKS_FILE=/var/lib/sbm-webpublish/links.sbm sbm-webpublish merge'
82 
83 `sbm-webpublish feed URL FEED` sets the feed of one link, as the admin
84 page does. An empty FEED (`''`) removes the feed.
85 
86 The commands do not lock the file against the service, so do not use the
87 admin page at the same time.
88 
89 ## Run
90 
91     ADMIN_PASSWORD=secret go run .
92 
93 Then open http://127.0.0.1:8082/links/admin.
94 
95 | Variable | Default | Meaning |
96 |---|---|---|
97 | `ADMIN_PASSWORD` | none, required | The password of the admin page. |
98 | `LINKS_FILE` | `links.sbm` | The sbm file of the links. |
99 | `ADDR` | `127.0.0.1:8082` | The address to listen on. |
100 
101 The feed has no settings. It gets its address and name from the request.
102 
103 ## Put it on a site
104 
105 1. Install the program on a server with systemd and nginx. The second
106    argument is the name of the site file in `/etc/nginx/sites-enabled`:
107 
108        sh deploy/deploy.sh root@example.com example
109 
110    The script builds the program, installs `deploy/sbm-webpublish.service`,
111    makes an admin password when there is none, and installs
112    `deploy/nginx-location.conf` as `/etc/nginx/snippets/sbm-webpublish.conf`.
113 2. The script adds `include snippets/sbm-webpublish.conf;` to each HTTPS
114    server block of the site file, runs `nginx -t` and reloads nginx. If
115    `nginx -t` fails, it puts the old site file back. The snippet sends
116    `/links/` to the program and turns on SSI for `/links.html`.
117 3. Put this line in the page of the links:
118 
119        <!--# include virtual="/links/list.html" -->
120 
121    Put `<link rel="alternate" type="application/atom+xml" href="links/atom.xml">`
122    in its head, so that feed readers and `sfeed_web` find the feed. See
123    `pages/links.md` in
124    [recentlywritten](https://github.com/equwal/recentlywritten) for an
125    example.
126 
127 ## Test
128 
129     go test ./...
130 
131 ## More projects
132 
133 - [SubRead](https://subread.space/): read along with an audiobook, in the browser.
134   Also [for Android](https://github.com/equwal/subread-android/releases/latest),
135   [for YouTube](https://github.com/equwal/subread-extension/releases/latest)
136   and [for KOReader](https://github.com/equwal/subread.koplugin).
137 - [SubRead Overlay](https://github.com/equwal/subread-overlay/releases/latest): subtitle lines over any Android media player.
138 - [SubRead Dictionary](https://github.com/equwal/subread-dictionary/releases/latest): a pop-up dictionary for Android that reads Yomitan dictionaries.
139 - [SubRead Anki](https://github.com/equwal/subread-anki): one tap makes an Anki card from any Android app.
140 - [Subrep](https://github.com/equwal/subrep-android/releases/latest): live captions of the sound of your phone.
141 - [Book Simulator](https://booksimulator.com/): a reading room for Aozora Bunko and Project Gutenberg books.
142 - [honjimaku.com](https://honjimaku.com/): subtitles for Japanese audiobooks.
143 - [sbm Sync](https://sbmsync.com/): your bookmarks, the same on every device,
144   with [sbm](https://github.com/equwal/sbm) for dmenu,
145   [sbm for Android](https://github.com/equwal/sbm-android/releases/latest)
146   and the [sbm add-on](https://github.com/equwal/sbm-extension/releases/latest) for Firefox and Chrome.
147 - [Rebind](https://github.com/equwal/rebind/releases): remap the hardware buttons of e-ink readers and Android,
148   with [Ink Recents](https://github.com/equwal/ink-recents/releases/latest),
149   [Ink Dim](https://github.com/equwal/ink-dim/releases/latest)
150   and [Ink Update](https://github.com/equwal/ink-update/releases/latest).
151 - [dickt.store](https://dickt.store/): language-learning tools, flashcards and web toys.
152 - [hentaibun.online](https://hentaibun.online/): learn kanbun and kobun.
153 - [Recently Written](https://recentlywritten.com/): the blog, and a list of [all projects](https://recentlywritten.com/projects.html).
154 
155 ## License
156 
157 AGPL-3.0, as sbm-sync.