@acms-subir/core 0.3.41 → 0.3.42

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.
@@ -0,0 +1,20 @@
1
+ import type { APIRoute } from 'astro';
2
+ import { getRepo } from '../../../../../lib/repo';
3
+ import { linkDevice, unlinkDevice } from '../../../../../lib/push/api';
4
+ import { failure, limitRequest, methodNotAllowed, response } from '../../../../../lib/mobile/http';
5
+ export { preflight as OPTIONS } from '../../../../../lib/mobile/http';
6
+ // POST: link this phone to a staff member (a code from the admin's Test phones) for test pushes. DELETE: unlink (idempotent).
7
+ function handler(run: (repo: Awaited<ReturnType<typeof getRepo>>, request: Request, token: string, address: string, locals: unknown) => Promise<unknown>): APIRoute {
8
+ return async context => {
9
+ try {
10
+ const repo = await getRepo(context.locals);
11
+ let address = 'unknown';
12
+ try { address = context.clientAddress || address; } catch { /* shared fallback bucket */ }
13
+ await limitRequest(repo, address, true);
14
+ return response(await run(repo, context.request, context.params.token ?? '', address, context.locals));
15
+ } catch (error) { return failure(error); }
16
+ };
17
+ }
18
+ export const POST = handler((repo, request, token, address, locals) => linkDevice(repo, request, token, address, locals));
19
+ export const DELETE = handler((repo, _request, token) => unlinkDevice(repo, token));
20
+ export const ALL = () => methodNotAllowed('POST, DELETE, OPTIONS');
@@ -0,0 +1,30 @@
1
+ /* Test phones panel and "Send test to my phone" (Admin → Announcements and
2
+ Admin → Mobile app). Scoped under .tp; admin.css tokens only. */
3
+ .tp { display: flex; flex-direction: column; align-items: flex-start; gap: 12px; scroll-margin-top: 72px; }
4
+ .tp > * { align-self: stretch; }
5
+ .tp > .btn { align-self: flex-start; }
6
+ .tp-head h2 { font-size: 14.5px; margin: 0; letter-spacing: -0.01em; }
7
+ .tp-head p { margin: 2px 0 0; color: var(--muted); font-size: 13px; max-width: 64ch; }
8
+ .tp-note { margin: 0; max-width: 64ch; }
9
+ .tp-intro { margin: 0 0 14px; max-width: 64ch; }
10
+ .tp-empty { margin: 0; color: var(--text-2); font-size: 13px; }
11
+ .tp-list { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--line); }
12
+ .tp-list li { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 9px 0; border-bottom: 1px solid var(--line); }
13
+ .tp-phone { display: flex; flex-direction: column; min-width: 0; }
14
+ .tp-phone b { font-size: 13.5px; }
15
+ .tp-phone small { color: var(--muted); font-size: 12px; }
16
+ .tp-code { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: 4px 16px; background: var(--panel-2); border-radius: var(--r-md); padding: 12px 14px; }
17
+ .tp-code p { margin: 0; color: var(--text-2); font-size: 13px; max-width: 52ch; }
18
+ .tp-code .btn { grid-column: 2; justify-self: start; margin-top: 4px; }
19
+ .tp-digits { grid-row: span 2; font: 700 30px/1.1 ui-monospace, 'SF Mono', Menlo, monospace; letter-spacing: 0.18em; font-variant-numeric: tabular-nums; }
20
+ .tp-flash .flash { margin: 0; }
21
+ /* an always-present live region; no gap while empty */
22
+ .tp > .tp-flash:empty { margin-top: -12px; }
23
+ .tp-send > .tp-flash:empty { margin-top: -8px; }
24
+ .tp-send { display: flex; flex-direction: column; align-items: center; gap: 8px; align-self: stretch; }
25
+ .tp-send .tp-flash { align-self: stretch; text-align: center; }
26
+ @media (max-width: 560px) {
27
+ .tp-code { grid-template-columns: minmax(0, 1fr); }
28
+ .tp-digits { grid-row: auto; }
29
+ .tp-code .btn { grid-column: 1; }
30
+ }