mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-07 01:09:54 +08:00
feat: unsplash api proxy (#6572)
This commit is contained in:
@@ -16,6 +16,7 @@ const {
|
|||||||
CAPTCHA_TURNSTILE_SECRET,
|
CAPTCHA_TURNSTILE_SECRET,
|
||||||
COPILOT_OPENAI_API_KEY,
|
COPILOT_OPENAI_API_KEY,
|
||||||
COPILOT_FAL_API_KEY,
|
COPILOT_FAL_API_KEY,
|
||||||
|
COPILOT_UNSPLASH_API_KEY,
|
||||||
MAILER_SENDER,
|
MAILER_SENDER,
|
||||||
MAILER_USER,
|
MAILER_USER,
|
||||||
MAILER_PASSWORD,
|
MAILER_PASSWORD,
|
||||||
@@ -103,6 +104,7 @@ const createHelmCommand = ({ isDryRun }) => {
|
|||||||
`--set graphql.app.copilot.enabled=true`,
|
`--set graphql.app.copilot.enabled=true`,
|
||||||
`--set-string graphql.app.copilot.openai.key="${COPILOT_OPENAI_API_KEY}"`,
|
`--set-string graphql.app.copilot.openai.key="${COPILOT_OPENAI_API_KEY}"`,
|
||||||
`--set-string graphql.app.copilot.fal.key="${COPILOT_FAL_API_KEY}"`,
|
`--set-string graphql.app.copilot.fal.key="${COPILOT_FAL_API_KEY}"`,
|
||||||
|
`--set-string graphql.app.copilot.unsplash.key="${COPILOT_UNSPLASH_API_KEY}"`,
|
||||||
`--set graphql.app.objectStorage.r2.enabled=true`,
|
`--set graphql.app.objectStorage.r2.enabled=true`,
|
||||||
`--set-string graphql.app.objectStorage.r2.accountId="${R2_ACCOUNT_ID}"`,
|
`--set-string graphql.app.objectStorage.r2.accountId="${R2_ACCOUNT_ID}"`,
|
||||||
`--set-string graphql.app.objectStorage.r2.accessKeyId="${R2_ACCESS_KEY_ID}"`,
|
`--set-string graphql.app.objectStorage.r2.accessKeyId="${R2_ACCESS_KEY_ID}"`,
|
||||||
|
|||||||
@@ -7,4 +7,5 @@ type: Opaque
|
|||||||
data:
|
data:
|
||||||
openaiSecret: {{ .Values.app.copilot.openai.key | b64enc }}
|
openaiSecret: {{ .Values.app.copilot.openai.key | b64enc }}
|
||||||
falSecret: {{ .Values.app.copilot.fal.key | b64enc }}
|
falSecret: {{ .Values.app.copilot.fal.key | b64enc }}
|
||||||
|
unsplashSecret: {{ .Values.app.copilot.unsplash.key | b64enc }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|||||||
@@ -159,6 +159,11 @@ spec:
|
|||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: "{{ .Values.app.copilot.secretName }}"
|
name: "{{ .Values.app.copilot.secretName }}"
|
||||||
key: falSecret
|
key: falSecret
|
||||||
|
- name: COPILOT_UNSPLASH_API_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: "{{ .Values.app.copilot.secretName }}"
|
||||||
|
key: unsplashSecret
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ if .Values.app.oauth.google.enabled }}
|
{{ if .Values.app.oauth.google.enabled }}
|
||||||
- name: OAUTH_GOOGLE_ENABLED
|
- name: OAUTH_GOOGLE_ENABLED
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ jobs:
|
|||||||
CAPTCHA_TURNSTILE_SECRET: ${{ secrets.CAPTCHA_TURNSTILE_SECRET }}
|
CAPTCHA_TURNSTILE_SECRET: ${{ secrets.CAPTCHA_TURNSTILE_SECRET }}
|
||||||
COPILOT_OPENAI_API_KEY: ${{ secrets.COPILOT_OPENAI_API_KEY }}
|
COPILOT_OPENAI_API_KEY: ${{ secrets.COPILOT_OPENAI_API_KEY }}
|
||||||
COPILOT_FAL_API_KEY: ${{ secrets.COPILOT_FAL_API_KEY }}
|
COPILOT_FAL_API_KEY: ${{ secrets.COPILOT_FAL_API_KEY }}
|
||||||
|
COPILOT_UNSPLASH_API_KEY: ${{ secrets.COPILOT_UNSPLASH_API_KEY }}
|
||||||
MAILER_SENDER: ${{ secrets.OAUTH_EMAIL_SENDER }}
|
MAILER_SENDER: ${{ secrets.OAUTH_EMAIL_SENDER }}
|
||||||
MAILER_USER: ${{ secrets.OAUTH_EMAIL_LOGIN }}
|
MAILER_USER: ${{ secrets.OAUTH_EMAIL_LOGIN }}
|
||||||
MAILER_PASSWORD: ${{ secrets.OAUTH_EMAIL_PASSWORD }}
|
MAILER_PASSWORD: ${{ secrets.OAUTH_EMAIL_PASSWORD }}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ AFFiNE.ENV_MAP = {
|
|||||||
THROTTLE_LIMIT: ['rateLimiter.limit', 'int'],
|
THROTTLE_LIMIT: ['rateLimiter.limit', 'int'],
|
||||||
COPILOT_OPENAI_API_KEY: 'plugins.copilot.openai.apiKey',
|
COPILOT_OPENAI_API_KEY: 'plugins.copilot.openai.apiKey',
|
||||||
COPILOT_FAL_API_KEY: 'plugins.copilot.fal.apiKey',
|
COPILOT_FAL_API_KEY: 'plugins.copilot.fal.apiKey',
|
||||||
|
COPILOT_UNSPLASH_API_KEY: 'plugins.copilot.unsplashKey',
|
||||||
REDIS_SERVER_HOST: 'plugins.redis.host',
|
REDIS_SERVER_HOST: 'plugins.redis.host',
|
||||||
REDIS_SERVER_PORT: ['plugins.redis.port', 'int'],
|
REDIS_SERVER_PORT: ['plugins.redis.port', 'int'],
|
||||||
REDIS_SERVER_USER: 'plugins.redis.username',
|
REDIS_SERVER_USER: 'plugins.redis.username',
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ import {
|
|||||||
Param,
|
Param,
|
||||||
Query,
|
Query,
|
||||||
Req,
|
Req,
|
||||||
|
Res,
|
||||||
Sse,
|
Sse,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
|
import type { Request, Response } from 'express';
|
||||||
import {
|
import {
|
||||||
concatMap,
|
concatMap,
|
||||||
connect,
|
connect,
|
||||||
@@ -21,6 +23,7 @@ import {
|
|||||||
} from 'rxjs';
|
} from 'rxjs';
|
||||||
|
|
||||||
import { CurrentUser } from '../../core/auth/current-user';
|
import { CurrentUser } from '../../core/auth/current-user';
|
||||||
|
import { Config } from '../../fundamentals';
|
||||||
import { CopilotProviderService } from './providers';
|
import { CopilotProviderService } from './providers';
|
||||||
import { ChatSession, ChatSessionService } from './session';
|
import { ChatSession, ChatSessionService } from './session';
|
||||||
import { CopilotCapability } from './types';
|
import { CopilotCapability } from './types';
|
||||||
@@ -34,6 +37,7 @@ export interface ChatEvent {
|
|||||||
@Controller('/api/copilot')
|
@Controller('/api/copilot')
|
||||||
export class CopilotController {
|
export class CopilotController {
|
||||||
constructor(
|
constructor(
|
||||||
|
private readonly config: Config,
|
||||||
private readonly chatSession: ChatSessionService,
|
private readonly chatSession: ChatSessionService,
|
||||||
private readonly provider: CopilotProviderService
|
private readonly provider: CopilotProviderService
|
||||||
) {}
|
) {}
|
||||||
@@ -78,6 +82,12 @@ export class CopilotController {
|
|||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getSignal(req: Request) {
|
||||||
|
const controller = new AbortController();
|
||||||
|
req.on('close', () => controller.abort());
|
||||||
|
return controller.signal;
|
||||||
|
}
|
||||||
|
|
||||||
@Get('/chat/:sessionId')
|
@Get('/chat/:sessionId')
|
||||||
async chat(
|
async chat(
|
||||||
@CurrentUser() user: CurrentUser,
|
@CurrentUser() user: CurrentUser,
|
||||||
@@ -111,7 +121,7 @@ export class CopilotController {
|
|||||||
session.finish(params),
|
session.finish(params),
|
||||||
session.model,
|
session.model,
|
||||||
{
|
{
|
||||||
signal: req.signal,
|
signal: this.getSignal(req),
|
||||||
user: user.id,
|
user: user.id,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -161,7 +171,7 @@ export class CopilotController {
|
|||||||
delete params.messageId;
|
delete params.messageId;
|
||||||
return from(
|
return from(
|
||||||
provider.generateTextStream(session.finish(params), session.model, {
|
provider.generateTextStream(session.finish(params), session.model, {
|
||||||
signal: req.signal,
|
signal: this.getSignal(req),
|
||||||
user: user.id,
|
user: user.id,
|
||||||
})
|
})
|
||||||
).pipe(
|
).pipe(
|
||||||
@@ -222,7 +232,7 @@ export class CopilotController {
|
|||||||
delete params.messageId;
|
delete params.messageId;
|
||||||
return from(
|
return from(
|
||||||
provider.generateImagesStream(session.finish(params), session.model, {
|
provider.generateImagesStream(session.finish(params), session.model, {
|
||||||
signal: req.signal,
|
signal: this.getSignal(req),
|
||||||
user: user.id,
|
user: user.id,
|
||||||
})
|
})
|
||||||
).pipe(
|
).pipe(
|
||||||
@@ -254,4 +264,34 @@ export class CopilotController {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get('/unsplash/photos')
|
||||||
|
async unsplashPhotos(
|
||||||
|
@Req() req: Request,
|
||||||
|
@Res() res: Response,
|
||||||
|
@Query() params: Record<string, string>
|
||||||
|
) {
|
||||||
|
const { unsplashKey } = this.config.plugins.copilot || {};
|
||||||
|
if (!unsplashKey) {
|
||||||
|
throw new InternalServerErrorException('Unsplash key is not configured');
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = new URLSearchParams(params);
|
||||||
|
const response = await fetch(
|
||||||
|
`https://api.unsplash.com/search/photos?${query}`,
|
||||||
|
{
|
||||||
|
headers: { Authorization: `Client-ID ${unsplashKey}` },
|
||||||
|
signal: this.getSignal(req),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
res.set({
|
||||||
|
'Content-Type': response.headers.get('Content-Type'),
|
||||||
|
'Content-Length': response.headers.get('Content-Length'),
|
||||||
|
'X-Ratelimit-Limit': response.headers.get('X-Ratelimit-Limit'),
|
||||||
|
'X-Ratelimit-Remaining': response.headers.get('X-Ratelimit-Remaining'),
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(response.status).send(await response.json());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ import {
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import type { ChatPrompt } from './prompt';
|
import type { ChatPrompt } from './prompt';
|
||||||
|
import type { FalConfig } from './providers/fal';
|
||||||
|
|
||||||
export interface CopilotConfig {
|
export interface CopilotConfig {
|
||||||
openai: OpenAIClientOptions;
|
openai: OpenAIClientOptions;
|
||||||
fal: {
|
fal: FalConfig;
|
||||||
apiKey: string;
|
unsplashKey: string;
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum AvailableModels {
|
export enum AvailableModels {
|
||||||
|
|||||||
Reference in New Issue
Block a user