alpha-gate 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/LICENSE +21 -0
- package/README.md +138 -0
- package/bin/alpha-gate.mjs +75 -0
- package/deploy/backup.sh +45 -0
- package/deploy/deploy.sh +21 -0
- package/deploy/dev.sh +56 -0
- package/deploy/lib/statedir.sh +11 -0
- package/deploy/teardown.sh +17 -0
- package/docs/ONBOARDING.md +16 -0
- package/docs/PRINCIPLES.md +195 -0
- package/docs/README.md +54 -0
- package/docs/UPLOADING.md +14 -0
- package/docs/integrate/activation.md +56 -0
- package/docs/integrate/sparkle-go.md +106 -0
- package/docs/integrate/sparkle-swift.md +63 -0
- package/docs/maintain/backup.md +52 -0
- package/docs/maintain/migrate-account.md +95 -0
- package/docs/maintain/teardown.md +46 -0
- package/docs/maintain/troubleshooting.md +102 -0
- package/docs/maintain/updating.md +92 -0
- package/docs/operate/add-users.md +55 -0
- package/docs/operate/channels.md +54 -0
- package/docs/operate/email.md +51 -0
- package/docs/operate/monitoring.md +56 -0
- package/docs/operate/publish.md +90 -0
- package/docs/operate/remove-users.md +47 -0
- package/docs/setup/cloudflare-account.md +41 -0
- package/docs/setup/deploy.md +84 -0
- package/docs/setup/install.md +65 -0
- package/migrations/0001_clients.sql +12 -0
- package/migrations/0002_builds_streams.sql +30 -0
- package/migrations/0003_access_log.sql +14 -0
- package/migrations/0004_meta.sql +5 -0
- package/migrations/0005_admin_audit.sql +14 -0
- package/migrations/0006_build_dmg.sql +6 -0
- package/migrations/0007_access_requests.sql +12 -0
- package/migrations/0008_build_rollback_target.sql +7 -0
- package/migrations/0009_hidden.sql +6 -0
- package/migrations/0010_purged_at.sql +5 -0
- package/package.json +65 -0
- package/publish.sh +302 -0
- package/release.json +7 -0
- package/src/auth/access-jwt.ts +210 -0
- package/src/auth/token-gate.ts +27 -0
- package/src/core/appcast.ts +107 -0
- package/src/core/audit-chain.ts +145 -0
- package/src/core/invite-template.ts +127 -0
- package/src/core/no-build.ts +160 -0
- package/src/core/resolver.ts +60 -0
- package/src/core/tokens.ts +51 -0
- package/src/core/types.ts +76 -0
- package/src/core/validation.ts +60 -0
- package/src/core/verdict.ts +195 -0
- package/src/core/version.ts +113 -0
- package/src/cron.ts +37 -0
- package/src/db/access-log.ts +168 -0
- package/src/db/access-requests.ts +90 -0
- package/src/db/admin-audit.ts +101 -0
- package/src/db/builds.ts +169 -0
- package/src/db/client.ts +80 -0
- package/src/db/clients.ts +103 -0
- package/src/db/meta.ts +30 -0
- package/src/db/streams.ts +85 -0
- package/src/deploy/cli.ts +181 -0
- package/src/deploy/commands/deploy.ts +392 -0
- package/src/deploy/commands/dev.ts +190 -0
- package/src/deploy/commands/teardown.ts +159 -0
- package/src/deploy/core/args.ts +205 -0
- package/src/deploy/core/colors.ts +49 -0
- package/src/deploy/core/config.ts +65 -0
- package/src/deploy/core/parse.ts +51 -0
- package/src/deploy/core/paths.ts +27 -0
- package/src/deploy/core/plan.ts +138 -0
- package/src/deploy/core/result.ts +13 -0
- package/src/deploy/core/state.ts +64 -0
- package/src/deploy/core/table.ts +49 -0
- package/src/deploy/core/types.ts +39 -0
- package/src/deploy/core/ui.ts +107 -0
- package/src/deploy/seams/clock.ts +10 -0
- package/src/deploy/seams/files.ts +52 -0
- package/src/deploy/seams/io.ts +56 -0
- package/src/deploy/seams/wrangler.ts +100 -0
- package/src/deploy/ui-preview.ts +112 -0
- package/src/deps.ts +41 -0
- package/src/dev/admin-entry.ts +104 -0
- package/src/env.ts +47 -0
- package/src/lib/clock.ts +17 -0
- package/src/lib/hosts.ts +27 -0
- package/src/lib/text.ts +6 -0
- package/src/r2/builds-bucket.ts +50 -0
- package/src/r2/keys.ts +27 -0
- package/src/routes/admin/admin-context.ts +9 -0
- package/src/routes/admin/audit-fields.ts +22 -0
- package/src/routes/admin/branding.tsx +161 -0
- package/src/routes/admin/builds.tsx +388 -0
- package/src/routes/admin/clients.tsx +396 -0
- package/src/routes/admin/confirm.tsx +80 -0
- package/src/routes/admin/flash.ts +72 -0
- package/src/routes/admin/form.ts +43 -0
- package/src/routes/admin/index.ts +146 -0
- package/src/routes/admin/invite.ts +57 -0
- package/src/routes/admin/middleware.ts +52 -0
- package/src/routes/admin/negotiate.ts +13 -0
- package/src/routes/admin/pending.tsx +73 -0
- package/src/routes/admin/read-model.ts +518 -0
- package/src/routes/admin/streams.tsx +182 -0
- package/src/routes/admin/theme.ts +34 -0
- package/src/routes/admin/upload.tsx +320 -0
- package/src/routes/admin/views.tsx +293 -0
- package/src/routes/app/access.tsx +38 -0
- package/src/routes/app/app-context.ts +8 -0
- package/src/routes/app/appcast.ts +68 -0
- package/src/routes/app/assets.ts +25 -0
- package/src/routes/app/download.ts +45 -0
- package/src/routes/app/get.tsx +35 -0
- package/src/routes/app/index.ts +31 -0
- package/src/routes/app/resolve.ts +16 -0
- package/src/services/anchor.ts +54 -0
- package/src/services/audit.ts +19 -0
- package/src/services/branding.ts +51 -0
- package/src/services/email-cloudflare.ts +80 -0
- package/src/services/email.ts +68 -0
- package/src/services/self-update.ts +62 -0
- package/src/views/access-page.tsx +39 -0
- package/src/views/admin/ci-page.tsx +76 -0
- package/src/views/admin/combobox.tsx +191 -0
- package/src/views/admin/forms.tsx +37 -0
- package/src/views/admin/layout.tsx +496 -0
- package/src/views/admin/manage-pages.tsx +223 -0
- package/src/views/admin/manage.tsx +1120 -0
- package/src/views/admin/plist-extract.ts +233 -0
- package/src/views/admin/read-pages.tsx +1098 -0
- package/src/views/admin/setup-page.tsx +108 -0
- package/src/views/admin/table-enhance.ts +176 -0
- package/src/views/admin/ui.tsx +159 -0
- package/src/views/get-page.tsx +39 -0
- package/src/views/layout.tsx +57 -0
- package/src/worker.ts +23 -0
- package/tsconfig.json +35 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import type { Child, FC } from "hono/jsx";
|
|
2
|
+
import { COPY_SCRIPT } from "./forms";
|
|
3
|
+
import { AdminLayout } from "./layout";
|
|
4
|
+
import { Tag } from "./ui";
|
|
5
|
+
|
|
6
|
+
// Result/confirmation pages for admin mutations. InvitePage surfaces the copy-paste link + message
|
|
7
|
+
// (§13 add user); ConfirmPage is the §11 "this would strand these users — proceed?" gate;
|
|
8
|
+
// ConfirmActionPage confirms destructive-but-not-stranding actions; ResultPage is the generic
|
|
9
|
+
// success/error landing for a browser form post (the HTML half of content negotiation).
|
|
10
|
+
|
|
11
|
+
/** Generic outcome page for a browser form submit: a status line, a message, and a way back. */
|
|
12
|
+
export const ResultPage: FC<{
|
|
13
|
+
title: string;
|
|
14
|
+
intent?: "success" | "error";
|
|
15
|
+
back?: { href: string; label: string };
|
|
16
|
+
children?: Child;
|
|
17
|
+
}> = ({ title, intent = "success", back, children }) => (
|
|
18
|
+
<AdminLayout title={title}>
|
|
19
|
+
{intent === "error" ? <Tag kind="warn" label="nothing was changed" /> : null}
|
|
20
|
+
{children}
|
|
21
|
+
{back ? (
|
|
22
|
+
<p class="actions">
|
|
23
|
+
<a class="btn" href={back.href}>
|
|
24
|
+
{back.label}
|
|
25
|
+
</a>
|
|
26
|
+
</p>
|
|
27
|
+
) : null}
|
|
28
|
+
</AdminLayout>
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
export const InvitePage: FC<{
|
|
32
|
+
email: string;
|
|
33
|
+
getUrl: string;
|
|
34
|
+
delivery?: { sent: boolean; error?: string } | undefined;
|
|
35
|
+
/** The rendered invite (template + link) — shown in copy-paste mode so the operator can send it. */
|
|
36
|
+
message?: { subject: string; body: string } | undefined;
|
|
37
|
+
/** True when this invite also reactivated a previously revoked user. */
|
|
38
|
+
restored?: boolean;
|
|
39
|
+
}> = ({ email, getUrl, delivery, message, restored }) => (
|
|
40
|
+
<AdminLayout title={`Invite ready — ${email}`}>
|
|
41
|
+
{restored ? (
|
|
42
|
+
<p class="callout ok">
|
|
43
|
+
{email} was revoked — their access is now restored with the fresh link below (the old link
|
|
44
|
+
stays dead).
|
|
45
|
+
</p>
|
|
46
|
+
) : null}
|
|
47
|
+
{delivery?.sent ? (
|
|
48
|
+
<p class="callout ok">Emailed to {email} — the link below is the same one they received.</p>
|
|
49
|
+
) : delivery && !delivery.sent ? (
|
|
50
|
+
<p class="callout warn">
|
|
51
|
+
The user was created, but the invite email to <strong>{email}</strong> didn't send:{" "}
|
|
52
|
+
{delivery.error || "delivery failed"}. Send the link below manually, or fix email under{" "}
|
|
53
|
+
<a href="/admin/settings">Settings</a>.
|
|
54
|
+
</p>
|
|
55
|
+
) : null}
|
|
56
|
+
|
|
57
|
+
<section aria-label="Link">
|
|
58
|
+
<div class="slab">
|
|
59
|
+
<h2>Private link</h2>
|
|
60
|
+
<span class="hint">durable while the token is active — they can revisit it</span>
|
|
61
|
+
</div>
|
|
62
|
+
<code class="token" id="invite-link">
|
|
63
|
+
{getUrl}
|
|
64
|
+
</code>
|
|
65
|
+
<p class="actions">
|
|
66
|
+
<button type="button" class="btn btn-primary" data-copy="#invite-link">
|
|
67
|
+
Copy link
|
|
68
|
+
</button>
|
|
69
|
+
</p>
|
|
70
|
+
</section>
|
|
71
|
+
|
|
72
|
+
{message && !delivery?.sent ? (
|
|
73
|
+
<section aria-label="Message">
|
|
74
|
+
<div class="slab">
|
|
75
|
+
<h2>Message to send</h2>
|
|
76
|
+
<span class="hint">your invite template, filled in — paste it anywhere</span>
|
|
77
|
+
</div>
|
|
78
|
+
<code class="token" id="invite-message">
|
|
79
|
+
{message.body}
|
|
80
|
+
</code>
|
|
81
|
+
<p class="actions">
|
|
82
|
+
<button type="button" class="btn" data-copy="#invite-message">
|
|
83
|
+
Copy message
|
|
84
|
+
</button>
|
|
85
|
+
</p>
|
|
86
|
+
</section>
|
|
87
|
+
) : null}
|
|
88
|
+
|
|
89
|
+
<p class="actions">
|
|
90
|
+
<a class="btn" href="/admin/users">
|
|
91
|
+
Back to users
|
|
92
|
+
</a>
|
|
93
|
+
</p>
|
|
94
|
+
{/* Progressive enhancement: the link is selectable without JS; this just adds one-click copy. */}
|
|
95
|
+
<script dangerouslySetInnerHTML={{ __html: COPY_SCRIPT }} />
|
|
96
|
+
</AdminLayout>
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* The §11 stranding confirm: names the exact action in operator language, lists who would be left
|
|
101
|
+
* with no available build (their apps report "up to date" and receive nothing), and returns to the
|
|
102
|
+
* page the operator came from on Cancel. `hidden` must carry everything the re-post needs.
|
|
103
|
+
*/
|
|
104
|
+
export const ConfirmPage: FC<{
|
|
105
|
+
/** The action in operator words, e.g. "Withdraw build 1400 (1.3.1-beta)". */
|
|
106
|
+
subject: string;
|
|
107
|
+
affected: string[];
|
|
108
|
+
postTo: string;
|
|
109
|
+
hidden: Record<string, string>;
|
|
110
|
+
cancelTo: string;
|
|
111
|
+
}> = ({ subject, affected, postTo, hidden, cancelTo }) => (
|
|
112
|
+
<AdminLayout title="Are you sure?">
|
|
113
|
+
<p>
|
|
114
|
+
<strong>{subject}</strong> would leave {affected.length === 1 ? "this user" : "these users"}{" "}
|
|
115
|
+
with no available build:
|
|
116
|
+
</p>
|
|
117
|
+
<ul>
|
|
118
|
+
{affected.map((email) => (
|
|
119
|
+
<li>{email}</li>
|
|
120
|
+
))}
|
|
121
|
+
</ul>
|
|
122
|
+
<p class="mut">
|
|
123
|
+
Their apps will report “up to date” and receive nothing until a higher build reaches them
|
|
124
|
+
(publish a newer build, reassign their channel, or adjust the pin).
|
|
125
|
+
</p>
|
|
126
|
+
<form method="post" action={postTo} class="actions">
|
|
127
|
+
{Object.entries(hidden).map(([name, value]) => (
|
|
128
|
+
<input type="hidden" name={name} value={value} />
|
|
129
|
+
))}
|
|
130
|
+
<button type="submit" class="btn-danger">
|
|
131
|
+
{subject} anyway
|
|
132
|
+
</button>
|
|
133
|
+
<a class="btn" href={cancelTo}>
|
|
134
|
+
Cancel
|
|
135
|
+
</a>
|
|
136
|
+
</form>
|
|
137
|
+
</AdminLayout>
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
/** The §11 confirm for a bulk operation (§13 #3): re-posts the op and every selected id on confirm. */
|
|
141
|
+
export const BulkConfirmPage: FC<{
|
|
142
|
+
op: string;
|
|
143
|
+
ids: number[];
|
|
144
|
+
affected: string[];
|
|
145
|
+
postTo: string;
|
|
146
|
+
}> = ({ op, ids, affected, postTo }) => (
|
|
147
|
+
<AdminLayout title="Are you sure?">
|
|
148
|
+
<p>
|
|
149
|
+
<strong>
|
|
150
|
+
{op === "withdraw" ? "Withdraw" : op} {ids.length} {ids.length === 1 ? "build" : "builds"}
|
|
151
|
+
</strong>{" "}
|
|
152
|
+
would leave these users with no available build:
|
|
153
|
+
</p>
|
|
154
|
+
<ul>
|
|
155
|
+
{affected.map((email) => (
|
|
156
|
+
<li>{email}</li>
|
|
157
|
+
))}
|
|
158
|
+
</ul>
|
|
159
|
+
<p class="mut">
|
|
160
|
+
Their apps will report “up to date” and receive nothing until a higher build reaches them.
|
|
161
|
+
</p>
|
|
162
|
+
<form method="post" action={postTo} class="actions">
|
|
163
|
+
<input type="hidden" name="op" value={op} />
|
|
164
|
+
<input type="hidden" name="confirm" value="true" />
|
|
165
|
+
{ids.map((id) => (
|
|
166
|
+
<input type="hidden" name="id" value={String(id)} />
|
|
167
|
+
))}
|
|
168
|
+
<button type="submit" class="btn-danger">
|
|
169
|
+
Confirm anyway
|
|
170
|
+
</button>
|
|
171
|
+
<a class="btn" href="/admin/builds">
|
|
172
|
+
Cancel
|
|
173
|
+
</a>
|
|
174
|
+
</form>
|
|
175
|
+
</AdminLayout>
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Confirmation for a destructive-but-not-stranding action (revoke, reissue, delete channel). States
|
|
180
|
+
* the action and its consequence in operator words; Cancel returns whence the operator came. The §11
|
|
181
|
+
* stranding list, when there is one, rides along via `affected`.
|
|
182
|
+
*/
|
|
183
|
+
export const ConfirmActionPage: FC<{
|
|
184
|
+
/** The action in operator words, e.g. "Revoke alice@corner.studio". */
|
|
185
|
+
subject: string;
|
|
186
|
+
confirmLabel: string;
|
|
187
|
+
postTo: string;
|
|
188
|
+
hidden: Record<string, string>;
|
|
189
|
+
cancelTo: string;
|
|
190
|
+
affected?: string[];
|
|
191
|
+
children?: Child;
|
|
192
|
+
}> = ({ subject, confirmLabel, postTo, hidden, cancelTo, affected = [], children }) => (
|
|
193
|
+
<AdminLayout title="Are you sure?">
|
|
194
|
+
<p>
|
|
195
|
+
<strong>{subject}</strong>
|
|
196
|
+
</p>
|
|
197
|
+
{children}
|
|
198
|
+
{affected.length > 0 ? (
|
|
199
|
+
<>
|
|
200
|
+
<p>
|
|
201
|
+
This would also leave {affected.length === 1 ? "this user" : "these users"} with no
|
|
202
|
+
available build:
|
|
203
|
+
</p>
|
|
204
|
+
<ul>
|
|
205
|
+
{affected.map((email) => (
|
|
206
|
+
<li>{email}</li>
|
|
207
|
+
))}
|
|
208
|
+
</ul>
|
|
209
|
+
</>
|
|
210
|
+
) : null}
|
|
211
|
+
<form method="post" action={postTo} class="actions">
|
|
212
|
+
{Object.entries(hidden).map(([name, value]) => (
|
|
213
|
+
<input type="hidden" name={name} value={value} />
|
|
214
|
+
))}
|
|
215
|
+
<button type="submit" class="btn-danger">
|
|
216
|
+
{confirmLabel}
|
|
217
|
+
</button>
|
|
218
|
+
<a class="btn" href={cancelTo}>
|
|
219
|
+
Cancel
|
|
220
|
+
</a>
|
|
221
|
+
</form>
|
|
222
|
+
</AdminLayout>
|
|
223
|
+
);
|