chore: auto-fix lint & formatting [skip ci]

This commit is contained in:
github-actions[bot] 2026-08-16 14:47:01 +00:00
parent df0edbb581
commit 3bcc98efc8
9 changed files with 152 additions and 77 deletions

View file

@ -58,11 +58,7 @@
</p> </p>
</div> </div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<button <button v-if="view === 'overview'" class="btn btn-ghost btn-sm" @click="refreshOverview">
v-if="view === 'overview'"
class="btn btn-ghost btn-sm"
@click="refreshOverview"
>
{{ t("metrics.refresh") }} {{ t("metrics.refresh") }}
</button> </button>
<button class="btn btn-ghost btn-sm" @click="toggle">{{ t("app.langToggle") }}</button> <button class="btn btn-ghost btn-sm" @click="toggle">{{ t("app.langToggle") }}</button>
@ -126,7 +122,9 @@
<button class="btn btn-ghost btn-sm" @click="exitGroup"> <button class="btn btn-ghost btn-sm" @click="exitGroup">
{{ t("group.back") }} {{ t("group.back") }}
</button> </button>
<span class="kpi-label">{{ t("group.routesIn", { name: selectedGroup.name }) }}</span> <span class="kpi-label">{{
t("group.routesIn", { name: selectedGroup.name })
}}</span>
<span class="kpi">{{ groupRoutes.length }}</span> <span class="kpi">{{ groupRoutes.length }}</span>
</div> </div>
<div class="status"> <div class="status">
@ -155,7 +153,11 @@
<section v-if="!groupRoutesLoading && !groupRoutes.length" class="empty"> <section v-if="!groupRoutesLoading && !groupRoutes.length" class="empty">
<p>{{ t("routes.emptyGroup") }}</p> <p>{{ t("routes.emptyGroup") }}</p>
<button v-if="canEditRoutes(selectedGroup.id)" class="btn btn-accent" @click="openNew"> <button
v-if="canEditRoutes(selectedGroup.id)"
class="btn btn-accent"
@click="openNew"
>
{{ t("routes.createFirst") }} {{ t("routes.createFirst") }}
</button> </button>
</section> </section>
@ -331,7 +333,13 @@ type View = "overview" | "groups" | "logs" | "audit" | "metrics";
const view = computed<View | null>(() => { const view = computed<View | null>(() => {
const seg = route.path.split("/").filter(Boolean)[1]; const seg = route.path.split("/").filter(Boolean)[1];
if (!seg) return "overview"; if (!seg) return "overview";
if (seg === "overview" || seg === "groups" || seg === "logs" || seg === "audit" || seg === "metrics") if (
seg === "overview" ||
seg === "groups" ||
seg === "logs" ||
seg === "audit" ||
seg === "metrics"
)
return seg; return seg;
return null; return null;
}); });

View file

@ -208,7 +208,10 @@ function cycleSort(): void {
v-for="s in metrics.byStatus" v-for="s in metrics.byStatus"
:key="s.status" :key="s.status"
class="metric-status" class="metric-status"
:class="{ ok: !s.status.startsWith('5') && s.status !== '0', bad: s.status.startsWith('5') || s.status === '0' }" :class="{
ok: !s.status.startsWith('5') && s.status !== '0',
bad: s.status.startsWith('5') || s.status === '0',
}"
> >
{{ s.status }} · {{ s.count }} {{ s.status }} · {{ s.count }}
</span> </span>

View file

@ -18,12 +18,19 @@
class="route-badge" class="route-badge"
:class="tg.platform === 'telegram' ? 'route-badge-tg' : 'route-badge-dc'" :class="tg.platform === 'telegram' ? 'route-badge-tg' : 'route-badge-dc'"
> >
<span class="route-badge-dot" :class="tg.platform === 'telegram' ? 'bg-info' : 'bg-accent'"></span> <span
class="route-badge-dot"
:class="tg.platform === 'telegram' ? 'bg-info' : 'bg-accent'"
></span>
{{ tg.platform === "telegram" ? "Telegram" : "Discord" }} {{ tg.platform === "telegram" ? "Telegram" : "Discord" }}
</span> </span>
<span v-if="route.fallback" class="route-badge route-badge-fallback">{{ t("route.fallback") }}</span> <span v-if="route.fallback" class="route-badge route-badge-fallback">{{
t("route.fallback")
}}</span>
<span v-if="route.stop" class="route-badge route-badge-stop">{{ t("route.stop") }}</span> <span v-if="route.stop" class="route-badge route-badge-stop">{{ t("route.stop") }}</span>
<span v-if="route.discordRoleIds?.length" class="route-badge route-badge-role">@roles</span> <span v-if="route.discordRoleIds?.length" class="route-badge route-badge-role"
>@roles</span
>
</div> </div>
</div> </div>
@ -34,7 +41,9 @@
class="route-chip" class="route-chip"
:class="{ exclude: f.exclude }" :class="{ exclude: f.exclude }"
> >
<span class="route-chip-type">{{ f.exclude ? t("routeEditor.not") + " " : "" }}{{ t("filter." + f.type) }}</span> <span class="route-chip-type"
>{{ f.exclude ? t("routeEditor.not") + " " : "" }}{{ t("filter." + f.type) }}</span
>
<span class="route-chip-val">{{ fmtMatch(f.match) }}</span> <span class="route-chip-val">{{ fmtMatch(f.match) }}</span>
</span> </span>
<span v-if="!route.filters.length" class="route-chip route-chip-empty"> <span v-if="!route.filters.length" class="route-chip route-chip-empty">
@ -67,7 +76,18 @@
:title="t('route.moveUp')" :title="t('route.moveUp')"
@click="$emit('move', route, -1)" @click="$emit('move', route, -1)"
> >
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m18 15-6-6-6 6"/></svg> <svg
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m18 15-6-6-6 6" />
</svg>
</button> </button>
<button <button
class="route-action-btn" class="route-action-btn"
@ -75,21 +95,56 @@
:title="t('route.moveDown')" :title="t('route.moveDown')"
@click="$emit('move', route, 1)" @click="$emit('move', route, 1)"
> >
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg> <svg
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m6 9 6 6 6-6" />
</svg>
</button> </button>
<button <button
class="route-action-btn" class="route-action-btn"
:title="t('routeEditor.editTitle')" :title="t('routeEditor.editTitle')"
@click="$emit('edit', route)" @click="$emit('edit', route)"
> >
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/><path d="m15 5 4 4"/></svg> <svg
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" />
<path d="m15 5 4 4" />
</svg>
</button> </button>
<button <button
class="route-action-btn route-action-btn-danger" class="route-action-btn route-action-btn-danger"
:title="t('routeEditor.close')" :title="t('routeEditor.close')"
@click="$emit('delete', route)" @click="$emit('delete', route)"
> >
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg> <svg
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M18 6 6 18" />
<path d="m6 6 12 12" />
</svg>
</button> </button>
</div> </div>
</article> </article>

View file

@ -102,9 +102,9 @@
:placeholder="t('routeEditor.matchPlaceholder')" :placeholder="t('routeEditor.matchPlaceholder')"
/> />
<label class="inline"> <label class="inline">
<input v-model="f.exclude" type="checkbox" /><span <input v-model="f.exclude" type="checkbox" /><span>{{
>{{ t("routeEditor.not") }}</span t("routeEditor.not")
> }}</span>
</label> </label>
<button type="button" class="icon-btn danger" @click="form.filters.splice(i, 1)"> <button type="button" class="icon-btn danger" @click="form.filters.splice(i, 1)">

View file

@ -7,7 +7,7 @@ The console itself is served at `/admin`; its tabs are deep-linkable via the URL
## Endpoints ## Endpoints
| Endpoint | Description | | Endpoint | Description |
| ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `GET /admin` | Config console UI | | `GET /admin` | Config console UI |
| `GET /admin/login` | Start admin sign-in (GitHub OAuth) | | `GET /admin/login` | Start admin sign-in (GitHub OAuth) |
| `GET /admin/logout` | Sign out and destroy the session | | `GET /admin/logout` | Sign out and destroy the session |

View file

@ -7,7 +7,7 @@
## 端点 ## 端点
| 端点 | 说明 | | 端点 | 说明 |
| ----------------------------------------------- | ------------------------------------------------------------------------------------ | | ----------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `GET /admin` | 配置控制台页面 | | `GET /admin` | 配置控制台页面 |
| `GET /admin/login` | 开始管理员登录GitHub OAuth | | `GET /admin/login` | 开始管理员登录GitHub OAuth |
| `GET /admin/logout` | 退出登录并销毁会话 | | `GET /admin/logout` | 退出登录并销毁会话 |

View file

@ -159,7 +159,9 @@ export async function getFailedSendLog(
`SELECT ${COLUMNS} FROM send_logs WHERE ok = 0 AND group_id = ? ORDER BY ts DESC LIMIT ?`, `SELECT ${COLUMNS} FROM send_logs WHERE ok = 0 AND group_id = ? ORDER BY ts DESC LIMIT ?`,
) )
.bind(groupId, limit) .bind(groupId, limit)
: db.prepare(`SELECT ${COLUMNS} FROM send_logs WHERE ok = 0 ORDER BY ts DESC LIMIT ?`).bind(limit); : db
.prepare(`SELECT ${COLUMNS} FROM send_logs WHERE ok = 0 ORDER BY ts DESC LIMIT ?`)
.bind(limit);
const { results } = await stmt.all<LogRow>(); const { results } = await stmt.all<LogRow>();
return results.map(toRecord); return results.map(toRecord);
} catch (err) { } catch (err) {

View file

@ -54,7 +54,10 @@ const EMPTY: DeliveryMetrics = {
recentFailures: [], recentFailures: [],
}; };
export async function getDeliveryMetrics(db: D1Database, groupId?: string): Promise<DeliveryMetrics> { export async function getDeliveryMetrics(
db: D1Database,
groupId?: string,
): Promise<DeliveryMetrics> {
const where = groupId ? " WHERE group_id = ?" : ""; const where = groupId ? " WHERE group_id = ?" : "";
const and = groupId ? " AND group_id = ?" : ""; const and = groupId ? " AND group_id = ?" : "";
const bindGroup = (stmt: D1PreparedStatement): D1PreparedStatement => const bindGroup = (stmt: D1PreparedStatement): D1PreparedStatement =>
@ -86,7 +89,9 @@ export async function getDeliveryMetrics(db: D1Database, groupId?: string): Prom
), ),
).all<StatusRow>(); ).all<StatusRow>();
const duration = await bindGroup( const duration = await bindGroup(
db.prepare(`SELECT AVG(duration_ms) AS avg FROM send_logs WHERE duration_ms IS NOT NULL${and}`), db.prepare(
`SELECT AVG(duration_ms) AS avg FROM send_logs WHERE duration_ms IS NOT NULL${and}`,
),
).all<AvgRow>(); ).all<AvgRow>();
const attempts = await bindGroup( const attempts = await bindGroup(
db.prepare( db.prepare(

View file

@ -32,14 +32,16 @@ describe("message title spec", () => {
sender, sender,
}), }),
); );
expect(msg.title).toBe( expect(msg.title).toBe("acme/widget: Pushed 1 commit to main");
"acme/widget: Pushed 1 commit to main",
);
expect(msg.description).toBe( expect(msg.description).toBe(
"[`abcd123`](https://github.com/acme/widget/commit/abcd1234ef) fix stuff", "[`abcd123`](https://github.com/acme/widget/commit/abcd1234ef) fix stuff",
); );
expect(msg.fields).toEqual([ expect(msg.fields).toEqual([
{ name: "Changes", value: "[View comparison](https://github.com/acme/widget/compare/abc...def):", inline: false }, {
name: "Changes",
value: "[View comparison](https://github.com/acme/widget/compare/abc...def):",
inline: false,
},
]); ]);
}); });