| 1 | |
| 2 | {{/* The status page refreshes itself ONLY while a run is active, and it |
| 3 | is a meta refresh because every affordance here works scriptless. |
| 4 | This is also the honest mechanism: the import is a goroutine that |
| 5 | lives as long as the process, and the platform sleeps an idle app, |
| 6 | so an open page is what keeps it awake. The copy below says so |
| 7 | rather than implying a background worker memoria does not have. */}} |
| 8 | {{define "head"}}{{if .Run}}{{if .Run.Running}}<meta http-equiv="refresh" content="5">{{end}}{{end}}{{end}} |
| 9 | |
| 10 | {{define "content"}} |
| 11 | |
| 12 | <h1>Notion</h1> |
| 13 | |
| 14 | {{if .Flash}}<div rst-callout rst-tone="{{.FlashKind | tone}}"{{if eq .FlashKind "danger"}} role="alert"{{end}}><div rst-callout-body><p>{{.Flash}}</p></div></div>{{end}} |
| 15 | |
| 16 | {{if not .Configured}} |
| 17 | |
| 18 | <p rst-lede> |
| 19 | Memoria imports the Notion pages you share with an integration, and |
| 20 | everything nested underneath them. |
| 21 | </p> |
| 22 | |
| 23 | {{/* Info: the untoned callout (runTone says why there is no "info"). */}} |
| 24 | <div rst-callout> |
| 25 | <div rst-callout-body><p> |
| 26 | <strong>What you connect, Memoria keeps.</strong> Once a page is |
| 27 | imported, it stays in Memoria and stays readable by everyone here — |
| 28 | even if you later archive it in Notion, unshare it, or revoke this |
| 29 | token. Removing it is something you do in Memoria. |
| 30 | </p></div> |
| 31 | </div> |
| 32 | |
| 33 | <section rst-section> |
| 34 | <h2>Connect</h2> |
| 35 | <ol rst-steps> |
| 36 | <li>In Notion, create an <strong>internal integration</strong> with |
| 37 | read-only capabilities and no user information.</li> |
| 38 | <li>Share the pages you want Memoria to hold with that integration. |
| 39 | Sharing a page is what publishes it. Memoria imports what the |
| 40 | integration can see and nothing else.</li> |
| 41 | <li>Paste the integration token below.</li> |
| 42 | </ol> |
| 43 | |
| 44 | <form method="post" action="/connectors/notion" rst-stack> |
| 45 | <label for="token">Integration token</label> |
| 46 | <input type="password" id="token" name="token" autocomplete="off" required> |
| 47 | <p rst-muted>Checked with Notion before it is saved. It is stored sealed and is never shown again.</p> |
| 48 | <button rst-btn="primary">Connect</button> |
| 49 | </form> |
| 50 | </section> |
| 51 | |
| 52 | {{else}} |
| 53 | |
| 54 | <section rst-section> |
| 55 | <h2>Connected</h2> |
| 56 | <p rst-muted> |
| 57 | {{if .Verified}}Token last checked {{.Verified}}.{{else}}Token stored.{{end}} |
| 58 | </p> |
| 59 | |
| 60 | {{if .CountsKnown}} |
| 61 | <p>{{.Imported}} of {{.Pages}} known page{{if ne .Pages 1}}s{{end}} imported.</p> |
| 62 | {{else}} |
| 63 | <p>The page counts could not be read, so this says nothing about how much has been imported.</p> |
| 64 | {{end}} |
| 65 | |
| 66 | <form method="post" action="/connectors/notion/sync" rst-inline> |
| 67 | <button rst-btn="primary"{{if .Run}}{{if .Run.Running}} disabled{{end}}{{end}}> |
| 68 | {{if .Run}}{{if .Run.Running}}Importing…{{else}}Import now{{end}}{{else}}Import now{{end}} |
| 69 | </button> |
| 70 | </form> |
| 71 | </section> |
| 72 | |
| 73 | {{if .Run}} |
| 74 | <section rst-section> |
| 75 | <h2>Last import</h2> |
| 76 | {{/* A callout toned by the run's state (spec §4, runTone): a run in |
| 77 | progress or finished cleanly is info (untoned), one that finished |
| 78 | with pages it could not read, stopped, or failed is a warning. It used to be a |
| 79 | plain paragraph, so "some pages could not be read" read like |
| 80 | "finished". */}} |
| 81 | <div rst-callout{{with .Run.Tone}} rst-tone="{{.}}"{{end}}><div rst-callout-body><p>{{.Run.Message}}</p></div></div> |
| 82 | |
| 83 | <dl rst-facts> |
| 84 | <dt>Started</dt><dd>{{.Run.Started}}</dd> |
| 85 | {{if .Run.Finished}}<dt>Finished</dt><dd>{{.Run.Finished}}</dd>{{end}} |
| 86 | <dt>Top-level pages shared with the integration</dt><dd>{{.Run.Roots}}</dd> |
| 87 | <dt>Pages seen</dt> |
| 88 | <dd> |
| 89 | {{if .Run.TotalKnown}} |
| 90 | {{.Run.Total}} |
| 91 | {{else}} |
| 92 | {{.Run.Seen}} so far — the total is not known until the import finishes |
| 93 | {{end}} |
| 94 | </dd> |
| 95 | <dt>Imported</dt><dd>{{.Run.Fetched}}</dd> |
| 96 | <dt>Already up to date</dt><dd>{{.Run.Skipped}}</dd> |
| 97 | <dt>Empty pages</dt><dd>{{.Run.Empty}}</dd> |
| 98 | <dt>Could not be read</dt> |
| 99 | <dd> |
| 100 | {{.Run.Failed}} |
| 101 | {{if .Run.Failed}}<span rst-muted>— counted here rather than left out of the totals</span>{{end}} |
| 102 | </dd> |
| 103 | {{if .Run.Unread}} |
| 104 | <dt>Blocks Memoria does not read</dt> |
| 105 | <dd>{{.Run.Unread}} <span rst-muted>— a page that came in thin may be one of these</span></dd> |
| 106 | {{end}} |
| 107 | </dl> |
| 108 | </section> |
| 109 | {{end}} |
| 110 | |
| 111 | <section rst-section> |
| 112 | <h2>Replace the token</h2> |
| 113 | <p rst-muted> |
| 114 | Rotating a token replaces the stored one. The old token is not kept. |
| 115 | Nothing already imported is affected. |
| 116 | </p> |
| 117 | <form method="post" action="/connectors/notion" rst-stack> |
| 118 | <label for="token">New integration token</label> |
| 119 | {{/* Never carries a value. The stored token is not rendered back |
| 120 | here or anywhere — not even masked, because a masked secret is |
| 121 | still a secret in the DOM. TestTokenIsNeverRendered checks |
| 122 | this branch specifically: an earlier version of that gate only |
| 123 | looked at the not-yet-connected page, which has no token field |
| 124 | at all, so it passed while proving nothing. */}} |
| 125 | <input type="password" id="token" name="token" autocomplete="off" required> |
| 126 | <button rst-btn="quiet">Replace</button> |
| 127 | </form> |
| 128 | </section> |
| 129 | |
| 130 | <section rst-section> |
| 131 | <h2>Disconnect</h2> |
| 132 | <p> |
| 133 | This removes the token and stops future imports. <strong>It deletes |
| 134 | nothing Memoria has already imported</strong> — those pages stay |
| 135 | here until someone deletes them in Memoria. |
| 136 | </p> |
| 137 | <form method="post" action="/connectors/notion/disconnect" rst-inline> |
| 138 | <button rst-btn="danger">Disconnect Notion</button> |
| 139 | </form> |
| 140 | </section> |
| 141 | |
| 142 | {{end}} |
| 143 | |
| 144 | {{end}} |
| 145 | |