tags are how the marketplace is browsed rather than scrolled: a list of them on the left, the plugins that carry one on the right. a closed vocabulary (PLUGIN_TAGS in the platform) because free text becomes homelab, home-lab, Home Lab and selfhosted inside a month, and a filter list of synonyms is worse than no filter at all. declared here rather than curated in the catalogue, so a plugin can say what it is and nobody edits a table when one ships. the marketplace caches it on sync, exactly as it already does for label and summary.
54 lines
2.5 KiB
TypeScript
54 lines
2.5 KiB
TypeScript
import type { PluginManifest } from '@@/plugins/manifest';
|
|
|
|
// Nginx Proxy Manager, inside Officer.
|
|
//
|
|
// ── The name ──
|
|
//
|
|
// The container is called `npm` and the owner calls it NPM. This plugin is `proxy-manager` anyway: in a
|
|
// repository where every other sentence is about bun, lockfiles and `npm install`, a plugin directory
|
|
// called `npm` would collide with the package manager in every grep and every conversation. The label
|
|
// says the real name; the id says what it does. Same reasoning that renamed `officer-claude` to
|
|
// `officer-anthropic-proxy`.
|
|
//
|
|
// ── The permission, and why settings are carved out ──
|
|
//
|
|
// Reading which hosts exist, whether their certificates are healthy and whether one is offline is
|
|
// genuinely useful to somebody who is not the owner. The CREDENTIAL is not: it is an NPM admin login,
|
|
// and NPM has no read-only role, so anyone holding it can redirect any hostname on this machine.
|
|
//
|
|
// So the permission is grantable and `/_config` is owner-only — the split `ownerOnly` exists for, and
|
|
// the same shape Jellyfin uses for its admin token.
|
|
export const manifest: PluginManifest = {
|
|
publisher: 'officerdev',
|
|
version: '0.1.0',
|
|
platform: '>=1.0.0',
|
|
|
|
label: 'Proxy Manager',
|
|
summary: 'Nginx Proxy Manager — hosts, certificates and access lists',
|
|
/** what is exposed, and the certificates for it. */
|
|
tags: ['Server Management', 'Security'],
|
|
icon: 'Route',
|
|
color: '#f97316',
|
|
|
|
permissions: [
|
|
{
|
|
key: 'proxy-manager',
|
|
label: 'Proxy Manager',
|
|
description: 'Nginx Proxy Manager — proxy hosts, redirections, streams and certificates',
|
|
|
|
// The connection form and the probe. Everything under here reads or writes the admin credential,
|
|
// and the match is by PREFIX — so `/_config/test` is covered too, deliberately.
|
|
//
|
|
// There is no `readOnlyWrites` here, and the omission is the decision rather than an oversight.
|
|
// `POST /_config/test` is a genuine read-only write: it validates a credential and stores nothing.
|
|
// Declaring it would still change nothing, because `authorize.ts` asks `isOwnerOnlyRoute` before
|
|
// `isRequestAllowedAtLevel` — ownerOnly wins, so the entry would be inert.
|
|
//
|
|
// And it should be inert. The test request carries the admin secret in its BODY. A member who can
|
|
// call it can offer this server a credential and be told whether it works, which is an oracle
|
|
// pointed at somebody else's password. Testing a connection belongs to whoever owns it.
|
|
ownerOnly: ['/_config'],
|
|
},
|
|
],
|
|
};
|