diff --git a/.docker/selfhost/.env.example b/.docker/selfhost/.env.example deleted file mode 100644 index 7ec7e2c82c..0000000000 --- a/.docker/selfhost/.env.example +++ /dev/null @@ -1,23 +0,0 @@ -# select a revision to deploy, available values: stable, beta, canary -AFFINE_REVISION=stable - -# set the port for the server container it will expose the server on -PORT=3010 - -# set the host for the server for outgoing links -# AFFINE_SERVER_HTTPS=true -# AFFINE_SERVER_HOST=affine.yourdomain.com -# or -# AFFINE_SERVER_EXTERNAL_URL=https://affine.yourdomain.com - -# position of the database data to persist -DB_DATA_LOCATION=~/.affine/self-host/postgres/pgdata -# position of the upload data(images, files, etc.) to persist -UPLOAD_LOCATION=~/.affine/self-host/storage -# position of the configuration files to persist -CONFIG_LOCATION=~/.affine/self-host/config - -# database credentials -DB_USERNAME=affine -DB_PASSWORD= -DB_DATABASE=affine diff --git a/.docker/selfhost/compose.yml b/.docker/selfhost/compose.yml index d4c4ea2e72..f9b8376e4e 100644 --- a/.docker/selfhost/compose.yml +++ b/.docker/selfhost/compose.yml @@ -1,10 +1,10 @@ name: affine services: affine: - image: ghcr.io/toeverything/affine:${AFFINE_REVISION:-stable} + image: ghcr.io/toeverything/affine:stable container_name: affine_server ports: - - '${PORT:-3010}:3010' + - '3010:3010' depends_on: redis: condition: service_healthy @@ -13,30 +13,24 @@ services: affine_migration: condition: service_completed_successfully volumes: - # custom configurations - - ${UPLOAD_LOCATION}:/root/.affine/storage - - ${CONFIG_LOCATION}:/root/.affine/config - env_file: - - .env + - ./data/storage:/root/.affine/storage + - ./config:/root/.affine/config environment: - REDIS_SERVER_HOST=redis - - DATABASE_URL=postgresql://${DB_USERNAME}:${DB_PASSWORD}@postgres:5432/${DB_DATABASE:-affine} + - DATABASE_URL=postgresql://affine@postgres:5432/affine - AFFINE_INDEXER_ENABLED=false restart: unless-stopped affine_migration: - image: ghcr.io/toeverything/affine:${AFFINE_REVISION:-stable} + image: ghcr.io/toeverything/affine:stable container_name: affine_migration_job volumes: - # custom configurations - - ${UPLOAD_LOCATION}:/root/.affine/storage - - ${CONFIG_LOCATION}:/root/.affine/config + - ./data/storage:/root/.affine/storage + - ./config:/root/.affine/config command: ['sh', '-c', 'node ./scripts/self-host-predeploy.js'] - env_file: - - .env environment: - REDIS_SERVER_HOST=redis - - DATABASE_URL=postgresql://${DB_USERNAME}:${DB_PASSWORD}@postgres:5432/${DB_DATABASE:-affine} + - DATABASE_URL=postgresql://affine@postgres:5432/affine - AFFINE_INDEXER_ENABLED=false depends_on: postgres: @@ -58,18 +52,14 @@ services: image: pgvector/pgvector:pg16 container_name: affine_postgres volumes: - - ${DB_DATA_LOCATION}:/var/lib/postgresql/data + - ./data/postgres:/var/lib/postgresql/data environment: - POSTGRES_USER: ${DB_USERNAME} - POSTGRES_PASSWORD: ${DB_PASSWORD} - POSTGRES_DB: ${DB_DATABASE:-affine} + POSTGRES_USER: affine + POSTGRES_DB: affine POSTGRES_INITDB_ARGS: '--data-checksums' - # you better set a password for you database - # or you may add 'POSTGRES_HOST_AUTH_METHOD=trust' to ignore postgres security policy POSTGRES_HOST_AUTH_METHOD: trust healthcheck: - test: - ['CMD', 'pg_isready', '-U', "${DB_USERNAME}", '-d', "${DB_DATABASE:-affine}"] + test: ['CMD', 'pg_isready', '-U', 'affine', '-d', 'affine'] interval: 10s timeout: 5s retries: 5 diff --git a/.docker/selfhost/config.json.example b/.docker/selfhost/config.json.example new file mode 100644 index 0000000000..e3ed7ac32b --- /dev/null +++ b/.docker/selfhost/config.json.example @@ -0,0 +1,14 @@ +{ + "$schema": "https://github.com/toeverything/affine/releases/latest/download/config.schema.json", + "server": { + "name": "AFFiNE Self-hosted", + "externalUrl": "http://localhost:3010" + }, + "copilot": { + "enabled": true, + "byok": { + "enabled": true, + "allowCustomEndpoint": true + } + } +} \ No newline at end of file diff --git a/.docker/selfhost/schema.json b/.docker/selfhost/schema.json index 9ac0395aad..8272b06953 100644 --- a/.docker/selfhost/schema.json +++ b/.docker/selfhost/schema.json @@ -1140,17 +1140,17 @@ "properties": { "enabled": { "type": "boolean", - "description": "Whether to enable the copilot plugin.
Document: https://docs.affine.pro/self-host-affine/administer/ai\n@default false", + "description": "Enable AI features. Workspace owners configure provider keys in Workspace Settings → Integrations → AI BYOK.\n@default false", "default": false }, "byok.enabled": { "type": "boolean", - "description": "Whether to enable workspace BYOK.\n@default true", + "description": "Allow workspace owners and admins to configure AI provider keys through AI BYOK.\n@default true", "default": true }, "byok.allowedProviders": { "type": "array", - "description": "The allowlist for workspace BYOK providers.\n@default [\"openai\",\"anthropic\",\"gemini\",\"fal\"]", + "description": "AI providers that workspace owners and admins may add through AI BYOK.\n@default [\"openai\",\"anthropic\",\"gemini\",\"fal\"]", "default": [ "openai", "anthropic", @@ -1160,7 +1160,7 @@ }, "byok.allowCustomEndpoint": { "type": "boolean", - "description": "Whether workspace BYOK custom endpoints are accepted.\n@default false", + "description": "Allow AI BYOK keys to use a custom provider endpoint.\n@default false", "default": false }, "byok.allowPrivateEndpoint": { diff --git a/.github/workflows/release-desktop.yml b/.github/workflows/release-desktop.yml index a6a05ee557..5c07e67650 100644 --- a/.github/workflows/release-desktop.yml +++ b/.github/workflows/release-desktop.yml @@ -413,7 +413,7 @@ jobs: - name: Copy Selfhost Release Files run: | cp ./.docker/selfhost/compose.yml ./release/docker-compose.yml - cp ./.docker/selfhost/.env.example ./release/.env.example + cp ./.docker/selfhost/config.json.example ./release/config.json.example cp ./.docker/selfhost/schema.json ./release/config.schema.json - name: Generate Release yml run: | diff --git a/packages/backend/server/src/plugins/copilot/config.ts b/packages/backend/server/src/plugins/copilot/config.ts index c71087467b..9bcce05aa8 100644 --- a/packages/backend/server/src/plugins/copilot/config.ts +++ b/packages/backend/server/src/plugins/copilot/config.ts @@ -217,21 +217,21 @@ declare global { defineModuleConfig('copilot', { enabled: { - desc: 'Whether to enable the copilot plugin.
Document: https://docs.affine.pro/self-host-affine/administer/ai', + desc: 'Enable AI features. Workspace owners configure provider keys in Workspace Settings → Integrations → AI BYOK.', default: false, }, 'byok.enabled': { - desc: 'Whether to enable workspace BYOK.', + desc: 'Allow workspace owners and admins to configure AI provider keys through AI BYOK.', default: true, shape: z.boolean(), }, 'byok.allowedProviders': { - desc: 'The allowlist for workspace BYOK providers.', + desc: 'AI providers that workspace owners and admins may add through AI BYOK.', default: ['openai', 'anthropic', 'gemini', 'fal'], shape: z.array(z.enum(['openai', 'anthropic', 'gemini', 'fal'])), }, 'byok.allowCustomEndpoint': { - desc: 'Whether workspace BYOK custom endpoints are accepted.', + desc: 'Allow AI BYOK keys to use a custom provider endpoint.', default: false, shape: z.boolean(), }, diff --git a/packages/frontend/admin/src/app.tsx b/packages/frontend/admin/src/app.tsx index 36a7ef7a68..6dd5ea2d91 100644 --- a/packages/frontend/admin/src/app.tsx +++ b/packages/frontend/admin/src/app.tsx @@ -33,9 +33,6 @@ export const Workspaces = lazy( export const Queue = lazy( () => import(/* webpackChunkName: "queue" */ './modules/queue') ); -export const AI = lazy( - () => import(/* webpackChunkName: "ai" */ './modules/ai') -); export const About = lazy( () => import(/* webpackChunkName: "about" */ './modules/about') ); @@ -131,7 +128,12 @@ export const App = () => { } /> } /> - } /> + + } + /> } /> Document: https://docs.affine.pro/self-host-affine/administer/ai" + "desc": "Enable AI features. Workspace owners configure provider keys in Workspace Settings → Integrations → AI BYOK." }, "byok.enabled": { "type": "Boolean", - "desc": "Whether to enable workspace BYOK." + "desc": "Allow workspace owners and admins to configure AI provider keys through AI BYOK." }, "byok.allowedProviders": { "type": "Array", - "desc": "The allowlist for workspace BYOK providers." + "desc": "AI providers that workspace owners and admins may add through AI BYOK." }, "byok.allowCustomEndpoint": { "type": "Boolean", - "desc": "Whether workspace BYOK custom endpoints are accepted." + "desc": "Allow AI BYOK keys to use a custom provider endpoint." }, "byok.allowPrivateEndpoint": { "type": "Boolean", diff --git a/packages/frontend/admin/src/modules/ai/index.tsx b/packages/frontend/admin/src/modules/ai/index.tsx deleted file mode 100644 index a1949ae3d1..0000000000 --- a/packages/frontend/admin/src/modules/ai/index.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { Switch } from '@affine/admin/components/ui/switch'; -import { cn } from '@affine/admin/utils'; -import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area'; -import { useState } from 'react'; - -import { Header } from '../header'; - -function AiPage() { - const [enableAi, setEnableAi] = useState(false); - - return ( -
-
- - -
-
AI
-
-
-

Enable AI

-

- AI functionality is not currently supported. Self-hosted AI - support is in progress. -

-
- -
-
-
- - - - -
-
- ); -} - -export { AiPage as Component }; diff --git a/packages/frontend/admin/src/modules/ai/keys.tsx b/packages/frontend/admin/src/modules/ai/keys.tsx deleted file mode 100644 index b52a4edace..0000000000 --- a/packages/frontend/admin/src/modules/ai/keys.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { Button } from '@affine/admin/components/ui/button'; -import { Input } from '@affine/admin/components/ui/input'; -import { Label } from '@affine/admin/components/ui/label'; -import { Separator } from '@affine/admin/components/ui/separator'; -import { useState } from 'react'; - -export function Keys() { - const [openAIKey, setOpenAIKey] = useState(''); - const [falAIKey, setFalAIKey] = useState(''); - const [unsplashKey, setUnsplashKey] = useState(''); - - return ( -
-
- Keys -
-
-
-
- -
- setOpenAIKey(e.target.value)} - /> - -
-
- -
- -
- setFalAIKey(e.target.value)} - /> - -
-
- -
- -
- setUnsplashKey(e.target.value)} - /> - -
-
- -
- Custom API keys may not perform as expected. AFFiNE does not - guarantee results when using custom API keys. -
-
-
-
- ); -} diff --git a/packages/frontend/admin/src/modules/nav/nav.tsx b/packages/frontend/admin/src/modules/nav/nav.tsx index 0cb8e719ea..d42dd45000 100644 --- a/packages/frontend/admin/src/modules/nav/nav.tsx +++ b/packages/frontend/admin/src/modules/nav/nav.tsx @@ -58,12 +58,6 @@ export function Nav({ isCollapsed = false }: NavProps) { label="Queue" isCollapsed={isCollapsed} /> - {/* } - label="AI" - isCollapsed={isCollapsed} - /> */} , { - name: 'AI', + name: 'AI BYOK', module: 'copilot', fields: [ - 'enabled', - 'providers.openai', - 'providers.gemini', - 'providers.anthropic', - 'providers.fal', - 'unsplash', - 'exa', { - key: 'storage', - desc: 'The storage provider for copilot blobs', - sub: 'provider', - type: 'Enum', - options: ['fs', 'aws-s3', 'cloudflare-r2'], + key: 'enabled', + desc: 'Enable AI features. Workspace owners configure provider keys in Workspace Settings → Integrations → AI BYOK.', }, + 'byok.enabled', + 'byok.allowedProviders', + 'byok.allowCustomEndpoint', { - key: 'storage', - sub: 'bucket', - type: 'String', - desc: 'The bucket name for copilot blobs storage', - }, - { - key: 'storage', - sub: 'config', - type: 'JSON', - desc: 'The S3 compatible config for the storage provider (endpoint/region/credentials).', + key: 'byok.allowPrivateEndpoint', + desc: 'Allow workspace owners and admins to connect BYOK providers on private network endpoints. Only enable this for trusted workspaces.', }, ], } as ConfigGroup<'copilot'>,