clone

a self-hosted multitenant nostr relay with sites, file storage, git hosting, and inbox and outbox delivery

feat/standalone-relay
tinyrelay / tinyclient / public.html
public.html 9.1K
1{{define "home"}}<section id="identity">2<header>{{if .Policy.Icon}}<img alt="" src="{{.Policy.Icon}}">{{end}}<h1>{{or .Policy.Name .Slug}}</h1><small><code>{{.URL}}</code> | {{template "nostrKey" .Identity}}</small></header>3<table id="facts">4{{if .Policy.Contact}}<tr><th>contact</th><td>{{.Policy.Contact}}</td></tr>{{end}}5{{if .Policy.Tags}}<tr><th>tags</th><td id="tags">{{range .Policy.Tags}}<a href="/search?q={{urlquery .}}">#{{.}}</a>{{end}}</td></tr>{{end}}6{{if .Policy.LanguageTags}}<tr><th>languages</th><td>{{join .Policy.LanguageTags ", "}}</td></tr>{{end}}7{{if .Policy.RelayCountries}}<tr><th>countries</th><td>{{join .Policy.RelayCountries ", "}}</td></tr>{{end}}8<tr><th>policies</th><td>{{if .Policy.PostingPolicy}}<a href="{{.Policy.PostingPolicy}}">posting</a> | {{end}}{{if .Policy.PrivacyPolicy}}<a href="{{.Policy.PrivacyPolicy}}">privacy</a> | {{end}}<a href="/terms">terms</a></td></tr>9<tr><th>reads</th><td>{{.Policy.Reads}}</td></tr>10<tr><th>writes</th><td>{{.Policy.Writes}}</td></tr>11</table>12{{if .Policy.Description}}<h2>About</h2>13{{markdown .Policy.Description}}14{{end}}{{if .Connections}}<h2>Connect</h2>15<p>Open this relay in an app. Each card is a way in; sign in to see the ones made for members.</p>16{{range .Connections}}{{$c := asMap .}}<connect-card>17<h3>{{str (index $c "title")}} <small>{{str (index $c "app")}}{{with str (index $c "where")}}, {{.}}{{end}}{{if ne (str (index $c "visibility")) "public"}} | {{str (index $c "visibility")}}{{end}}</small></h3>18<p>{{str (index $c "about")}}</p>19{{range index $c "inputs"}}{{$i := asMap .}}<label>{{str (index $i "Label")}} <input name="{{str (index $i "Name")}}" placeholder="{{str (index $i "Placeholder")}}" pattern="{{str (index $i "Pattern")}}"></label>20{{end}}<p>{{range index $c "links"}}{{$l := asMap .}}{{if str (index $l "href")}}<a href="{{str (index $l "href")}}">{{str (index $l "label")}}</a> {{else}}<button type="button" data-copy="{{str (index $l "copy")}}">{{str (index $l "label")}}</button> {{end}}{{end}}{{if index $c "hidden"}}<small>sign in for {{str (index $c "hidden")}} more</small>{{end}}<output role="status"></output></p>21{{with str (index $c "qr")}}<details><summary>QR code</summary><img src="/qr.svg?text={{urlquery .}}" alt="QR code" width="200" height="200"></details>{{end}}22</connect-card>23{{end}}{{end}}24</section>25{{end}}2627{{define "signin"}}<section id="signin">28<h1>{{if and .Actor (.Query.Get "connect")}}Connect your signer{{else if .Actor}}Your account{{else}}Sign in{{end}}</h1>29{{if .Notice}}<p role="status">{{.Notice}}</p>{{end}}30{{if .Actor}}<p>Signed in as {{template "nostrKey" .Actor}}.</p>31<p><a href="/account">Open your account</a> | <a href="/chat">Chat</a></p>32{{if not (.Query.Get "connect")}}<p><a href="/signin?connect=1{{with .Query.Get "next"}}&amp;next={{urlquery .}}{{end}}">Connect a signer</a> to restore signing without signing out.</p>{{end}}33{{if .Query.Get "connect"}}<p>Connect the signer for this account. Your sign-in session stays active.</p>{{template "signerControls" .}}{{else}}<p>Open Menu on a phone, or use the desktop footer, to sign out.</p>{{end}}34{{else}}<p>Use a browser extension, or connect Amber on your phone. The relay never sees a private key.</p>35{{template "signerControls" .}}36{{end}}</section>37{{end}}3839{{define "account"}}<section id="account">40<h1>Account</h1>41<p>Manage your profile, chat preferences and relay lists.</p>42<table id="account-identity"><tbody><tr><th>signed in as</th><td>{{template "nostrName" .Actor}}</td></tr><tr><th>profile</th><td><a href="/profile">Edit your profile</a></td></tr></tbody></table>43<h2>Chat preferences</h2>44<chat-preferences actor="{{.Actor}}"><label><input type="checkbox" name="share_presence"{{if .SharePresence}} checked{{end}} disabled> Share presence and typing</label><p>Show when you are online or typing in chat.</p><output role="status"></output></chat-preferences><noscript><p>Changing preferences requires JavaScript and a connected signer.</p></noscript>45<h2>Your relay lists</h2>46<p>Choose where other Nostr clients find your notes, messages and files. Existing entries stay.</p>47<relay-lists relay="{{wsURL .URL}}" server="{{.URL}}" pubkey="{{.Actor}}">48<table id="rows"><tbody>49<tr data-kind="10002" data-tag="r"><td>Relay list <small>NIP-65</small></td><td>where clients read your notes and send you mentions</td><td><button type="button" name="check">check</button> <button type="button" name="add">add this relay</button></td></tr>50<tr data-kind="10050" data-tag="relay"><td>DM inbox <small>NIP-17</small></td><td>where people send you private messages</td><td><button type="button" name="check">check</button> <button type="button" name="add">add this relay</button></td></tr>51<tr data-kind="10007" data-tag="relay"><td>Search relays <small>NIP-51</small></td><td>where clients run your searches</td><td><button type="button" name="check">check</button> <button type="button" name="add">add this relay</button></td></tr>52<tr data-kind="10063" data-tag="server" data-value="server"><td>Blossom servers <small>BUD-03</small></td><td>where clients upload and look for your files</td><td><button type="button" name="check">check</button> <button type="button" name="add">add this relay</button></td></tr>53</tbody></table><output role="status"></output></relay-lists>54</section>{{end}}5556{{define "signerControls"}}<p><button id="session-login" type="button">{{if .Actor}}Use connected signer{{else}}Sign in with connected signer{{end}}</button></p>57<h2>Phone signer</h2>58<p>Scan the QR code with Amber and approve the connection and sign-in request. You will return to the page you opened.</p>59<p><label><input type="checkbox" id="remember-signer"> Remember this device</label> <small>Keeps the phone signer connected after the app closes. Sign out to forget it.</small></p>60<p><button id="nostrconnect" type="button">Generate QR code</button> <a id="nostrconnect-open" hidden>Open in signer</a></p>61<img id="signer-qr" alt="Nostr Connect QR code" hidden width="240" height="240">62<p id="signer-status" role="status"></p>63<h2>Bunker URL</h2>64<form id="bunker">65<label>Paste from your signer <input id="bunker-url" type="url" placeholder="bunker://…" required></label>66<button>Connect remote signer</button>67</form>68{{end}}6970{{define "inbox"}}<section>71<h1>Inbox</h1>72<p>Events addressed to this signed-in key, subject to the same private-event gates as websocket queries.</p>73{{if .Error}}{{template "alert" .Error}}{{else}}{{template "eventList" .}}{{end}}74</section>75{{end}}7677{{define "outbox"}}<section>78<h1>Outbox</h1>79<p>Events authored by this signed-in key.</p>80{{if .Error}}{{template "alert" .Error}}{{else}}{{template "eventList" .}}{{end}}81</section>82{{end}}8384{{define "search"}}<section>85<h1>Search</h1>86<form id="search" action="/search" method="get">87<label>Terms <input name="q" type="search" value="{{.Query.Get "q"}}" required></label>88<input type="hidden" name="kinds" value="{{.Query.Get "kinds"}}"><input type="hidden" name="author" value="{{.Query.Get "author"}}"><input type="hidden" name="since" value="{{.Query.Get "since"}}">89<button>Search</button>90</form>91{{if .Error}}{{template "alert" .Error}}92{{else if .Feed}}<h2>Results</h2>93{{template "eventList" .}}94{{else if .Query.Get "q"}}<p>No matching events.</p>95{{end}}</section>96{{end}}9798{{define "articles"}}<section>99<h1>Articles</h1>100<p>Long-form NIP-23 events published on this relay.</p>101{{if .Error}}{{template "alert" .Error}}{{else}}{{template "eventList" .}}{{end}}102<p><a href="/feed.xml">Atom feed</a> | <a href="/articles.json">JSON feed</a></p>103</section>104{{end}}105106{{define "event"}}<section>107<h1>Event</h1>108{{if .Feed}}{{template "eventList" .}}{{else}}{{template "nostrEvent" .Event}}<share-link></share-link>{{end}}109<p><a href="/feed.xml">Atom feed</a> | <a href="/articles.json">JSON feed</a></p>110</section>111{{end}}112113{{define "view"}}<section>114<h1>View: {{.View}}</h1>115{{template "jsonView" .Event}}116</section>117{{end}}118119{{define "invite"}}<section>120<h1>Join this relay</h1>121<p>This invite is claimed with one signature from your Nostr signer. The relay never receives a private key.</p>122{{if .Policy.JoinTerms}}<h2>Before you join</h2>123<pre>{{.Policy.JoinTerms}}</pre>124{{end}}<rpc-form method="claiminvite" action="/api/invites/claim" terms="{{.Policy.JoinTerms}}">125<input type="hidden" name="param" value="{{.Invite}}">126{{if .Policy.JoinTerms}}<label><input type="checkbox" name="termsAgreement" required> I agree to these relay terms.</label>127{{end}}<button>Join relay</button>128</rpc-form>129</section>130{{end}}131132{{define "terms"}}<section>133<h1>Terms</h1>134{{if .Policy.JoinTerms}}{{markdown .Policy.JoinTerms}}{{else}}<p>This relay has not published terms.</p>{{end}}135</section>136{{end}}137138{{define "private"}}<section>139<h1>Private messages</h1>140<p>Sign in with a Nostr client to view addressed private events.</p>141</section>142{{end}}143144{{define "media"}}<section>145<h1>Media</h1>146<p>Blossom uploads and NIP-96 metadata are available from compatible clients.</p>147</section>148{{end}}149150{{define "marmot"}}<section>151<h1>Marmot</h1>152<p>Marmot group state is served when the feature is enabled.</p>153</section>154{{end}}155156{{define "grasp"}}<section>157<h1>GRASP</h1>158<p>Repository state and relay capabilities are served through the GRASP endpoints.</p>159</section>160{{end}}