Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 9 additions & 59 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,67 +9,17 @@ Participation is governed by [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md).
Project decisions and contribution licensing are described in
[GOVERNANCE.md](../docs/project/GOVERNANCE.md).

## Development Setup
## Development And Verification

The CI baseline is Rust stable and Node.js 24. Install Rust with `rustfmt` and
`clippy`, Node.js/npm, `curl`, and a native compiler toolchain. Docker Compose is
needed for the complete stack.
Follow [Development](../docs/DEVELOPMENT.md) for the pinned toolchain,
PostgreSQL setup, and daily commands. Run `scripts/dev.sh check` before a pull
request; `scripts/dev.sh check --backend` is the Rust-only subset.

Confirm that the current Linux shell uses the pinned toolchain and does not
resolve Windows-mounted Node/npm binaries:

```bash
scripts/doctor.sh --development
```

`scripts/install-deps-ubuntu.sh` installs only native helper packages; it does
not install Rust, Node.js, npm, Docker, or Playwright browsers.

```bash
git clone git@github.com:tiammomo/ModelPort.git
cd ModelPort
cp .env.example .env
cp config.example.toml config.toml
# replace required placeholders; never commit this file
scripts/config-validate.sh
scripts/check.sh

cd dashboard
npm ci
npm run lint
npm run build
```

Full setup and the change-to-test matrix are in
[docs/DEVELOPMENT.md](../docs/DEVELOPMENT.md).

## Before A Pull Request

Run the aggregate check:

```bash
scripts/check-all.sh
```

If working incrementally, `scripts/check.sh` is the backend-only subset and
`cd dashboard && npm run check` is the dashboard subset.
Dependency updates must also pass `cargo audit --deny warnings --file Cargo.lock`,
`cargo deny check`, and `node scripts/audit-dashboard.mjs`; CI runs these
gates. Never add a broad or non-expiring audit exception.

Then choose checks by risk:

- dashboard behavior: affected Playwright specs or `npm run e2e`;
- auth, API keys, teams, quota, or backup: `scripts/acceptance.sh`;
- protocol, SSE, or Tool Use: relevant Rust tests and
`scripts/tool-use-acceptance.sh`;
- provider behavior: `scripts/provider-matrix.sh --model provider:model` and
real Tool Use acceptance when certification is intended;
- Docker/systemd/reverse proxy: build/install the deployment and run smoke
through the deployed origin.

Real upstream checks can incur cost and must use your own local secrets. CI and
ordinary pull requests should prefer mock-backed checks.
Use the [change-to-test matrix](../docs/DEVELOPMENT.md#change-to-test-matrix)
for additional checks. Dependency changes must also pass the
[dependency audits](../docs/DEVELOPMENT.md#dependency-audits). Never add a broad
or non-expiring audit exception. Ordinary verification uses synthetic data;
real Provider certification requires explicitly intended, potentially paid calls.

## Code And Security Conventions

Expand Down
26 changes: 4 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,28 +89,10 @@ project provides no paid edition, hosted service, or community-support SLA.

## Development

The source-development path requires a reachable PostgreSQL instance; the
development scripts do not start one. See [Development](docs/DEVELOPMENT.md)
for a loopback-only disposable database command and the complete prerequisites.
The [repository map](docs/DEVELOPMENT.md#repository-map) explains where code,
resources, tests, and project policies belong.

```bash
cp .env.example .env
cp config.example.toml config.toml
# replace required placeholders
scripts/start.sh

cd dashboard
npm ci
npm run dev
```

Before submitting a change:

```bash
scripts/check-all.sh
```
Use the [development guide](docs/DEVELOPMENT.md) for the repository map,
pinned tools, PostgreSQL setup, and frontend workflow. Native gateway commands
share `scripts/dev.sh`; use `scripts/dev.sh help` to see the available actions.
Before submitting a change, run `scripts/dev.sh check`.

## License

Expand Down
22 changes: 3 additions & 19 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,25 +80,9 @@ CIDR、安全 Cookie、CSRF 防护和控制台 API Key。不要提交 `.env`、P

## 本地开发

参见[开发指南与目录职责](docs/DEVELOPMENT.md#repository-map)。本地开发需要
可连接的 PostgreSQL;开发脚本不会自动启动数据库。

```bash
cp .env.example .env
cp config.example.toml config.toml
# 替换必填 placeholder
scripts/start.sh

cd dashboard
npm ci
npm run dev
```

提交变更前:

```bash
scripts/check-all.sh
```
[开发指南](docs/DEVELOPMENT.md)统一说明目录职责、工具版本、PostgreSQL 准备和
前端开发流程。本地网关统一使用 `scripts/dev.sh`,运行 `scripts/dev.sh help`
查看启动、停止、状态与排障命令;提交前运行 `scripts/dev.sh check`。

## 许可证

Expand Down
19 changes: 18 additions & 1 deletion dashboard/e2e/provider-management.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ test.describe('provider management', () => {
await expect(card.getByText('已禁用')).toHaveCount(0)
})

test('exposes credential pool controls on provider cards', async ({ page }) => {
test('validates, creates, edits and deletes credentials on provider cards', async ({ page }) => {
const suffix = Date.now().toString(36)
const providerId = 'deepseek'
const credentialId = `e2e_pool_${suffix}`
Expand All @@ -70,6 +70,9 @@ test.describe('provider management', () => {
try {
await card.getByRole('button', { name: '新增' }).click()
const credentialDialog = page.getByRole('dialog')
await credentialDialog.getByRole('button', { name: '新增账号' }).click()
await expect(credentialDialog.locator('#credential-id')).toHaveAttribute('aria-invalid', 'true')
await expect(credentialDialog.locator('#credential-id')).toBeFocused()
await credentialDialog.getByPlaceholder('例如: account-a').fill(credentialId)
await credentialDialog.getByPlaceholder('例如: Mimo 主账号').fill('Pool Account A')
await credentialDialog.getByPlaceholder('例如: MIMO_OPENAI_API_KEY_ALT').fill(`E2E_POOL_KEY_${suffix.toUpperCase()}`)
Expand All @@ -83,6 +86,20 @@ test.describe('provider management', () => {
await card.getByRole('combobox').first().click()
await page.getByRole('option', { name: '轮询' }).click()
await expect(card).toContainText('轮询')

await card.getByRole('button', { name: '编辑上游账号 Pool Account A' }).click()
const editDialog = page.getByRole('dialog')
await expect(editDialog.locator('#credential-id')).toHaveCount(0)
await editDialog.locator('#credential-name').fill('Pool Account Updated')
await editDialog.getByRole('button', { name: '保存账号' }).click()
await expect(editDialog).not.toBeVisible()
await expect(card).toContainText('Pool Account Updated')

await card.getByRole('button', { name: '删除上游账号 Pool Account Updated' }).click()
await page.getByRole('dialog').getByRole('button', { name: '删除账号' }).click()
await expect(page.getByRole('dialog')).not.toBeVisible()
await expect(card).not.toContainText('Pool Account Updated')
await expect(card).toContainText('默认凭证')
} finally {
await page.request.delete(
`/admin/providers/${providerId}/credentials/${encodeURIComponent(credentialId)}`,
Expand Down
19 changes: 19 additions & 0 deletions dashboard/src/features/models/ModelFormField.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Switch } from '@/components/ui/switch'
import type { ReactNode } from 'react'
import { CircleAlert } from 'lucide-react'
import { Label } from '@/components/ui/label'
Expand Down Expand Up @@ -39,3 +40,21 @@ export function Field({
)
}

export function SwitchRow({
label,
checked,
disabled,
onCheckedChange,
}: {
label: string
checked: boolean
disabled?: boolean
onCheckedChange: (checked: boolean) => void
}) {
return (
<div className="flex items-center justify-between gap-3">
<Label className={cn('text-sm font-normal', disabled && 'text-muted-foreground')}>{label}</Label>
<Switch checked={checked} disabled={disabled} onCheckedChange={onCheckedChange} aria-label={label} />
</div>
)
}
Loading
Loading