feat: add local model gateway and console tabs
This commit is contained in:
@@ -4,7 +4,8 @@ This repository contains the first runnable ShareServer project. It is an
|
||||
interactive control-console prototype based on the
|
||||
[ShareServer PRD](docs/features/share-server/prd.md), not a production
|
||||
ShareServer implementation. It also includes a persistent local sharing metadata
|
||||
service connected to GoodBuddy's Device Sharing application.
|
||||
service connected to GoodBuddy's Device Sharing application and a local
|
||||
OpenAI-compatible model gateway that forwards requests to configured upstreams.
|
||||
|
||||
## Repository and documentation
|
||||
|
||||
@@ -36,20 +37,17 @@ Source and feature documentation were extracted from GoodBuddy commit
|
||||
action endpoints.
|
||||
- Same-origin React, Vite, TypeScript, Tailwind CSS, and shadcn/ui-style Web
|
||||
console.
|
||||
- Administrator views for overview, members, devices, capabilities, policies,
|
||||
approvals, releases, federation, tasks, audit, and settings.
|
||||
- Member self-service views for personal devices, grants, and requests.
|
||||
- Sharing center at the preserved `/capabilities` route: capability and knowledge
|
||||
catalogs, publisher/device views, URL filters and linked metadata details.
|
||||
Knowledge fixtures distinguish synced copies from remote retrieval and display
|
||||
separate search/read/download permissions; no content operations are connected.
|
||||
- GoodBuddy service demos: `/model-gateway`, `/documents`, and `/distribution`.
|
||||
They simulate upstream routing policies and call audit, document ownership and
|
||||
recycle-bin permissions, OnlyOffice instance checks, and per-group configuration
|
||||
releases and rollback. State survives navigation between these routes but resets
|
||||
on refresh; these actions use browser memory only, with no network requests or
|
||||
local storage. Offline devices retain their applied version and remain pending.
|
||||
- Responsive navigation, dark and light themes, search, filters, details,
|
||||
- Overview with service links; planned modules show pending-service notices.
|
||||
- Sharing center at `/capabilities`: real device and publication metadata,
|
||||
filters, source declarations and separate search/read/download flags; no content
|
||||
operations or organization ownership checks are connected.
|
||||
- Local model gateway at `/model-gateway`: persistent upstreams and model aliases,
|
||||
encrypted upstream credentials, scoped client access keys, real model discovery,
|
||||
request testing, JSON/SSE forwarding, and metadata audits.
|
||||
- `/documents` and `/distribution` are placeholders without document operations,
|
||||
OnlyOffice checks, drafts, releases or rollback; saving gateway models does not
|
||||
deliver configuration to GoodBuddy.
|
||||
- Responsive navigation, dark and light themes, filters, details,
|
||||
forms, confirmations, and keyboard-accessible dialogs.
|
||||
- Production build and a non-root container image.
|
||||
|
||||
@@ -61,17 +59,18 @@ business pages.
|
||||
|
||||
## Not included yet
|
||||
|
||||
The existing snapshot records are read-only fixtures; the service demos mutate
|
||||
only in-memory examples. The action endpoint returns an acknowledgement but does
|
||||
not persist changes. PostgreSQL,
|
||||
initialization and login, organization isolation, real authorization, device
|
||||
Legacy snapshot and acknowledgement endpoints remain in the backend but are no
|
||||
longer used by the console. PostgreSQL,
|
||||
initialization and login, organization isolation, organization authorization, device
|
||||
WebSocket connections, task routing, relay, package storage, federation, and
|
||||
auditable mutations remain production implementation work. The planned Office
|
||||
editing integration also has no current implementation: ONLYOFFICE Docs,
|
||||
object storage, document sessions, save callbacks, checkpoints, and Desktop
|
||||
document tabs remain design work. Model inference, actual gateway forwarding,
|
||||
document saving, and device delivery are not implemented. The OnlyOffice checks
|
||||
use preset results and do not establish an editing session. See the
|
||||
document tabs remain design work. Model hosting and inference, document saving,
|
||||
and device delivery are not implemented. B1 gateway code is implemented with
|
||||
Bearer key authentication and model scopes, but acceptance is partial; GoodBuddy
|
||||
Desktop, browser UI and a full tool-result roundtrip remain unverified for B1.
|
||||
Organization authentication and quotas remain unimplemented. See the
|
||||
[ShareServer feature documentation](docs/features/share-server/README.md).
|
||||
|
||||
Daily work belongs in GoodBuddy; ShareServer provides the organization backend.
|
||||
@@ -95,32 +94,113 @@ The readiness endpoint reports this explicitly:
|
||||
|
||||
## Run locally
|
||||
|
||||
```text
|
||||
cd goodbuddy-shareserver
|
||||
Run from the repository root. The development launcher creates and reuses a
|
||||
persistent master key under the current user's home directory. For first-time
|
||||
Windows PowerShell setup:
|
||||
|
||||
```powershell
|
||||
npm ci
|
||||
$env:SHARESERVER_GATEWAY_BASE_URL = 'http://127.0.0.1:8787/api/v1/gateway/v1'
|
||||
npm run dev
|
||||
```
|
||||
|
||||
The automatic key is outside `SHARESERVER_DATA_DIR` (default `./data`) and is
|
||||
reused on later starts. To manage the key explicitly, create one with
|
||||
`npm run gateway:init-key -- <file>` and set `SHARESERVER_MASTER_KEY_FILE`
|
||||
before starting the service. Back up the key separately from `data/`; restoring
|
||||
encrypted credentials requires the same master key.
|
||||
|
||||
Open `http://127.0.0.1:5174`. Vite proxies `/api` to the Hono service on port
|
||||
`8787`.
|
||||
`8787` by default. When the launcher loads `SHARESERVER_PORT`, it passes the
|
||||
derived API port to Vite so the proxy follows the API child. Vite accepts `/api`
|
||||
requests only from loopback sockets, even if its listen address is changed.
|
||||
|
||||
## Local GoodBuddy integration
|
||||
|
||||
With `npm run dev` running here, run `npm run dev` in the adjacent GoodBuddy
|
||||
checkout. Open Device Sharing from its application center, save the server URL
|
||||
(`http://127.0.0.1:8787`), register the device, and publish a metadata entry.
|
||||
Open `http://127.0.0.1:5174/capabilities?view=local` to view actual records separately
|
||||
from the organization fixtures. Revoke in GoodBuddy and refresh the Web directory.
|
||||
Open `http://127.0.0.1:5174/capabilities?view=local` to view actual records.
|
||||
Organization fixture tabs have been removed. Revoke in GoodBuddy and refresh the Web directory.
|
||||
|
||||
Records persist in `data/sharing.json`; override the directory with
|
||||
`SHARESERVER_DATA_DIR`. Use one process per data directory. This API is a local,
|
||||
unauthenticated development service: device IDs identify records, not owners.
|
||||
`SHARESERVER_DATA_DIR`. Use one process per data directory. This API is a local
|
||||
development service without device authentication, restricted to direct loopback
|
||||
administration: device IDs identify records, not owners.
|
||||
Source modes and permission flags are declarations only; no knowledge content,
|
||||
file synchronization, remote execution, or organization authorization is provided.
|
||||
The [technical design](docs/features/share-server/technical-design.md#41-本地共享目录契约已实现)
|
||||
defines the actual HTTP contract. The readiness response additionally reports this
|
||||
local-service boundary and keeps `productionReady: false`.
|
||||
|
||||
## Local model gateway
|
||||
|
||||
The gateway runs independently of GoodBuddy and has no Desktop source dependency.
|
||||
Open `/model-gateway` in the Web console. A new store starts with no upstreams,
|
||||
models or audits; mutations persist in `data/gateway.json` under
|
||||
`SHARESERVER_DATA_DIR` (default `./data`). Use one process and one store per data
|
||||
directory. It retains the newest 200 chat audits, containing metadata only, never
|
||||
prompts, response content or credentials. Missing token usage remains unknown.
|
||||
|
||||
Add an upstream with its name, HTTP/HTTPS Base URL, OpenAI Chat Completions
|
||||
protocol and API Key (or no authentication). Credentials are encrypted with
|
||||
AES-256-GCM and never returned by the API. Editing defaults to `keep`; `replace`
|
||||
sets a new secret and `clear` removes it. Selecting no authentication does not
|
||||
delete a saved credential. `credentialConfigured` reports stored configuration,
|
||||
not connectivity or successful decryption.
|
||||
|
||||
The master key is supplied by `SHARESERVER_MASTER_KEY_FILE`, or alternatively
|
||||
`SHARESERVER_MASTER_KEY` containing 64 hexadecimal characters; do not set both.
|
||||
Missing or incorrect keys report credential unavailability and never regenerate
|
||||
the key over existing ciphertext.
|
||||
|
||||
Persisted legacy `apiKeyEnv` references remain readable and callable. Replace
|
||||
them explicitly through the upstream editor to use managed credentials. The
|
||||
variable-name/prefix UI and `/import-env` endpoint have been removed. For legacy
|
||||
references or authorized development testing only, an env file can be loaded:
|
||||
|
||||
```text
|
||||
npm run dev -- --env-file=../goodbuddy/.env.deepseek.local
|
||||
```
|
||||
|
||||
`scripts/dev.mjs` passes values loaded from this file only to the API child, not
|
||||
Vite; existing process variables take precedence. Restart the API after environment
|
||||
changes. The adjacent path is optional test input, not a repository dependency.
|
||||
Use preexisting personal configurations only for explicitly authorized environment
|
||||
testing; the gateway does not automatically read or migrate GoodBuddy settings.
|
||||
|
||||
Use **Discover models** to request the upstream model list, add or edit an alias
|
||||
mapping, and explicitly send a real test request. Discovery and testing contact the upstream; chat may incur
|
||||
charges. Manual mapping is also available.
|
||||
|
||||
For an OpenAI-compatible client, use Base URL
|
||||
`http://127.0.0.1:8787/api/v1/gateway/v1`, protocol `openai-chat-completions`,
|
||||
a configured model alias and a newly created gateway access key as the client's
|
||||
API Key. The console shows `SHARESERVER_GATEWAY_BASE_URL`; it does not infer a
|
||||
public address from Vite or request Host headers. Supported
|
||||
client endpoints are `/models` and `/chat/completions`; chat supports JSON or SSE,
|
||||
tool-call data and upstream usage. Responses and Anthropic APIs are not supported, and the
|
||||
gateway does not execute tools. Requests are limited to 1 MiB, upstream responses
|
||||
to 16 MiB, and the default request timeout is 120 seconds. Exact stream limits and
|
||||
API contracts are in [technical design section 4.2](docs/features/share-server/technical-design.md#42-本地模型网关契约已实现).
|
||||
|
||||
Both client endpoints require a Bearer access key. Create a key for selected
|
||||
model record IDs; renaming an alias preserves its scope. The full key is shown
|
||||
only at creation, and only its SHA-256 digest is stored. Missing, invalid or
|
||||
revoked keys return 401; unauthorized models return 403. Model lists include only
|
||||
authorized, enabled models with enabled upstreams and usable credentials. Audits
|
||||
link the access key ID, which identifies a calling configuration, not a user.
|
||||
|
||||
Management and the legacy sharing API require a direct loopback socket. For these
|
||||
requests, the API rejects `Forwarded`, `X-Forwarded-*` and `X-Real-IP` headers and checks Origin;
|
||||
Origin alone is not authorization. Web tests use local-only `/api/v1/gateway/test`,
|
||||
not a Bearer bypass on client endpoints. Reverse-proxy and container administration
|
||||
are unsupported until B2 login exists; a proxy's loopback connection must not be
|
||||
treated as an administrator. Nonlocal clients must reach the API inference
|
||||
endpoints directly with a key, not through Vite. Use HTTPS for external deployment;
|
||||
HTTP carries keys without transport encryption. Organization isolation and quotas
|
||||
remain pending. Dated verification is in [progress](docs/features/share-server/progress.md).
|
||||
|
||||
## Run the production build
|
||||
|
||||
```text
|
||||
@@ -131,20 +211,26 @@ npm start
|
||||
|
||||
Open `http://127.0.0.1:8787`.
|
||||
|
||||
Use the same master-key and gateway Base URL environment settings as development
|
||||
when starting the build. A successful build does not imply production readiness.
|
||||
|
||||
Build output is `dist/web/` for the console and `dist/server/` for the backend.
|
||||
`npm start` runs `dist/server/server/src/index.js` from the repository root and
|
||||
serves `dist/web/`. The backend preserves the source directory structure under
|
||||
its output directory so compiled imports can resolve root `shared/` modules.
|
||||
|
||||
Set `SHARESERVER_PORT` to use another listen port. Local runs bind to
|
||||
`127.0.0.1` by default; set `SHARESERVER_HOST` explicitly when a trusted reverse
|
||||
proxy or container needs another interface. Do not expose the prototype as a
|
||||
production service.
|
||||
`127.0.0.1` by default; `SHARESERVER_HOST` changes the listen interface, not the
|
||||
management boundary. Do not expose the prototype as a production service.
|
||||
|
||||
## Container
|
||||
|
||||
Container configuration lives in `docker/`. Run these commands from the repository
|
||||
root to build and start the Web console and API together:
|
||||
Container configuration lives in `docker/`. The existing commands below build and
|
||||
start the image, but B1 does not support container/proxy administration: published
|
||||
ports usually reach the API from a non-loopback socket and management returns 403.
|
||||
The supplied Compose file does not configure the master-key file. Managed
|
||||
credentials require a separate secret mount outside `/app/data` and its environment
|
||||
setting; container management must wait for B2. Run from the repository root:
|
||||
|
||||
```text
|
||||
mkdir -p data
|
||||
@@ -155,11 +241,12 @@ docker compose -f docker/compose.yaml logs -f shareserver
|
||||
```
|
||||
|
||||
Open `http://127.0.0.1:8787`. The container runs as the non-root `node` user
|
||||
and stores sharing metadata in `/app/data`, bind-mounted from the project's
|
||||
and stores sharing and gateway metadata in `/app/data`, bind-mounted from the project's
|
||||
`./data` directory. No named volume or additional database container is required.
|
||||
Existing local `data/sharing.json` is used directly and must be readable by UID 1000.
|
||||
Only sharing metadata is
|
||||
persisted; the console's other demos retain their prototype behavior.
|
||||
Gateway configuration and its bounded audit metadata use `data/gateway.json` in
|
||||
the same mount; upstream credentials are encrypted there and access keys are
|
||||
stored as digests. The master key must be supplied and backed up separately.
|
||||
|
||||
Stop with `docker compose -f docker/compose.yaml down`; the host directory survives
|
||||
container recreation and removal with `down -v`. Run only one process per data directory, including
|
||||
@@ -176,7 +263,7 @@ docker run --rm -p 127.0.0.1:8787:8787 --mount type=bind,source="$(pwd)/data",ta
|
||||
|
||||
Compose publishes port 8787 on all host interfaces; the standalone example
|
||||
publishes only on the host loopback interface. Containerization
|
||||
does not add authentication or authorization; this remains a local prototype.
|
||||
does not add organization login or relax B1's socket restrictions.
|
||||
The image health check verifies HTTP availability, not storage writability or
|
||||
production readiness.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user