refactor(ai-generation): 将预设提示词提取到单独文件并移除旧加密逻辑
将AI图像和视频生成的预设提示词提取到单独的constants/prompts.ts文件 移除不再需要的旧版加密逻辑和迁移代码 简化加密工具类,仅保留当前版本的加密实现
This commit is contained in:
1 parent
9642a54af0
commit
445a214b8d
5 files changed
+100
-138
No files matched your search
@@ -4,6 +4,7 @@ import './ai-image-generation.scss';
|
||||
import { useDrawnix } from '../../hooks/use-drawnix';
|
||||
import { useI18n } from '../../i18n';
|
||||
import { useBoard } from '@plait-board/react-board';
|
||||
import { getImagePrompts, type Language } from '../../constants/prompts';
|
||||
import { getSelectedElements, PlaitElement, getRectangleByElements, Point } from '@plait/core';
|
||||
import { defaultGeminiClient, promptForApiKey } from '../../utils/gemini-api';
|
||||
import { geminiSettings } from '../../utils/settings-manager';
|
||||
@@ -387,26 +388,8 @@ const AIImageGeneration = ({ initialPrompt = '', initialImages = [], selectedEle
|
||||
|
||||
// 获取合并的预设提示词(用户历史 + 默认预设)
|
||||
const getMergedPresetPrompts = () => {
|
||||
// 默认预设提示词
|
||||
const defaultPrompts = language === 'zh' ? [
|
||||
'一只可爱的小猫坐在窗台上,阳光透过窗户洒在它的毛发上',
|
||||
'美丽的山水风景,青山绿水,云雾缭绕',
|
||||
'现代简约风格的室内设计,明亮宽敞',
|
||||
'夜晚的城市天际线,霓虹灯闪烁',
|
||||
'春天的樱花盛开,粉色花瓣飘落',
|
||||
'科幻风格的太空站,星空背景',
|
||||
'温馨的咖啡厅,暖色调灯光',
|
||||
'抽象艺术风格,色彩丰富的几何图形'
|
||||
] : [
|
||||
'A cute kitten sitting on a windowsill with sunlight streaming through',
|
||||
'Beautiful mountain landscape with green hills and misty clouds',
|
||||
'Modern minimalist interior design, bright and spacious',
|
||||
'City skyline at night with neon lights glowing',
|
||||
'Cherry blossoms in spring with pink petals falling',
|
||||
'Sci-fi space station with starry background',
|
||||
'Cozy coffee shop with warm ambient lighting',
|
||||
'Abstract art with colorful geometric shapes'
|
||||
];
|
||||
// 获取默认预设提示词
|
||||
const defaultPrompts = getImagePrompts(language as Language);
|
||||
|
||||
// 使用工具函数提取用户历史提示词
|
||||
const userPrompts = extractUserPromptsFromHistory(historyItems).slice(0, 8);
|
||||
|
||||
@@ -4,6 +4,7 @@ import './ai-video-generation.scss';
|
||||
import { useDrawnix } from '../../hooks/use-drawnix';
|
||||
import { useI18n } from '../../i18n';
|
||||
import { useBoard } from '@plait-board/react-board';
|
||||
import { getVideoPrompts, type Language } from '../../constants/prompts';
|
||||
import { getSelectedElements, PlaitElement, getRectangleByElements, Point } from '@plait/core';
|
||||
import { defaultGeminiClient, videoGeminiClient, promptForApiKey } from '../../utils/gemini-api';
|
||||
import { geminiSettings } from '../../utils/settings-manager';
|
||||
@@ -439,26 +440,8 @@ const AIVideoGeneration = ({ initialPrompt = '', initialImage }: AIVideoGenerati
|
||||
|
||||
// 获取合并的预设提示词(用户历史 + 默认预设)
|
||||
const getMergedPresetPrompts = () => {
|
||||
// 默认预设提示词(适用于无源图片场景)
|
||||
const defaultPrompts = language === 'zh' ? [
|
||||
'生成一个美丽的日出场景,阳光从山峰后缓缓升起,云朵轻柔地飘动',
|
||||
'创造一个森林中的场景,树叶在微风中轻轻摇摆,阳光斑驳',
|
||||
'生成一个海边场景,海浪轻拍岸边,海鸟在空中盘旋',
|
||||
'创建一个花园场景,花朵在微风中轻摆,蝴蝶翩翩起舞',
|
||||
'生成一个雨后场景,水滴从树叶上缓缓滴落,彩虹出现在天空',
|
||||
'创造一个雪花飘落的冬日场景,雪花轻柔地降落',
|
||||
'生成一个星空场景,星星闪烁,云朵缓缓飘过月亮',
|
||||
'创建一个溪流场景,清水在石头间潺潺流淌,鱼儿游过'
|
||||
] : [
|
||||
'Generate a beautiful sunrise scene where the sun slowly rises from behind mountains with clouds gently floating',
|
||||
'Create a forest scene with leaves swaying gently in the breeze and dappled sunlight',
|
||||
'Generate a beach scene with waves gently lapping the shore and seagulls soaring overhead',
|
||||
'Create a garden scene with flowers swaying in the breeze and butterflies dancing',
|
||||
'Generate a post-rain scene with water drops slowly falling from leaves and a rainbow in the sky',
|
||||
'Create a winter scene with snowflakes gently falling from the sky',
|
||||
'Generate a starry night scene with twinkling stars and clouds slowly drifting past the moon',
|
||||
'Create a stream scene with clear water flowing gently between rocks and fish swimming by'
|
||||
];
|
||||
// 获取默认预设提示词
|
||||
const defaultPrompts = getVideoPrompts(language as Language);
|
||||
|
||||
// 使用工具函数提取用户历史提示词
|
||||
const userPrompts = extractUserPromptsFromHistory(historyItems).slice(0, 8);
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* AI 生成的默认提示词常量
|
||||
*/
|
||||
|
||||
// AI 图像生成默认提示词
|
||||
export const AI_IMAGE_PROMPTS = {
|
||||
zh: [
|
||||
`一张写实的半身人像,一位身穿水蓝色连身裙的年轻韩国亚洲女人走进旅馆大厅,女人脸上带着温暖的微笑,左手拿着白色棒球帽,右手拉着黄色行李箱。
|
||||
场景在济州岛度假感的旅馆大厅里,柔和的光线从窗外洒入室内柔和的打在女人的身上,凸显了女人的脸部表情。
|
||||
使用 50mm 人像镜头拍摄,女人在画面中央,背景呈现柔和的模糊(散景)。
|
||||
氛围是日系杂志的色调并充满放松度假感。直式人像构图,比例1:1。`,
|
||||
'一只可爱的小猫坐在窗台上,阳光透过窗户洒在它的毛发上',
|
||||
'美丽的山水风景,青山绿水,云雾缭绕',
|
||||
'现代简约风格的室内设计,明亮宽敞',
|
||||
'夜晚的城市天际线,霓虹灯闪烁',
|
||||
'春天的樱花盛开,粉色花瓣飘落',
|
||||
'科幻风格的太空站,星空背景',
|
||||
'温馨的咖啡厅,暖色调灯光',
|
||||
'抽象艺术风格,色彩丰富的几何图形'
|
||||
],
|
||||
en: [
|
||||
`young Korean woman in a light blue dress holding a white baseball cap and pulling a yellow suitcase, photography,
|
||||
stylish modern hotel lobby, soft sunlight streaming through the window, pastel tones, relaxed vacation mood,
|
||||
centered vertical portrait with bokeh background, medium shot.`,
|
||||
'A cute kitten sitting on a windowsill with sunlight streaming through',
|
||||
'Beautiful mountain landscape with green hills and misty clouds',
|
||||
'Modern minimalist interior design, bright and spacious',
|
||||
'City skyline at night with neon lights glowing',
|
||||
'Cherry blossoms in spring with pink petals falling',
|
||||
'Sci-fi space station with starry background',
|
||||
'Cozy coffee shop with warm ambient lighting',
|
||||
'Abstract art with colorful geometric shapes'
|
||||
]
|
||||
} as const;
|
||||
|
||||
// AI 视频生成默认提示词
|
||||
export const AI_VIDEO_PROMPTS = {
|
||||
zh: [
|
||||
`场景:日落时分,一座宏伟的城堡庭院,金色的光线透过彩色玻璃窗,营造出温暖而充满希望的氛围。
|
||||
相机:对两位公主进行中特写跟踪拍摄,然后进行广角拉出,露出整个庭院,最后缓慢向上倾斜到天空。
|
||||
动作:公主们互相微笑,然后开始和谐地唱歌,然后在灯光亮起时向天空举手。
|
||||
音频:柔和的管弦乐,伴随着令人振奋的弦乐和合唱,沙沙作响的树叶和远处鸟儿的环绕声,对话:“我们相信明天会更加光明。”
|
||||
风格:迪士尼风格的动画,鲜艳的色彩,梦幻般的灯光。`,
|
||||
'生成一个美丽的日出场景,阳光从山峰后缓缓升起,云朵轻柔地飘动',
|
||||
'创造一个森林中的场景,树叶在微风中轻轻摇摆,阳光斑驳',
|
||||
'生成一个海边场景,海浪轻拍岸边,海鸟在空中盘旋',
|
||||
'创建一个花园场景,花朵在微风中轻摆,蝴蝶翩翩起舞',
|
||||
'生成一个雨后场景,水滴从树叶上缓缓滴落,彩虹出现在天空',
|
||||
'创造一个雪花飘落的冬日场景,雪花轻柔地降落',
|
||||
'生成一个星空场景,星星闪烁,云朵缓缓飘过月亮',
|
||||
'创建一个溪流场景,清水在石头间潺潺流淌,鱼儿游过'
|
||||
],
|
||||
en: [
|
||||
`Scene: A grand castle courtyard at sunset, golden light filtering through stained glass windows, creating a warm and hopeful atmosphere.
|
||||
Camera: Medium close-up tracking shot of the two princesses, then a wide-angle pull-out to reveal the entire courtyard, ending with a slow upward tilt to the sky.
|
||||
Action: The princesses smile at each other, then begin singing in harmony, then raise their hands toward the sky as the light brightens.
|
||||
Audio: Soft orchestral music with uplifting strings and choir, ambient sounds of rustling leaves and distant birds, dialogue: "Together, we believe tomorrow will be brighter."
|
||||
Style: Disney-style animation, vibrant colors, dreamy lighting.`,
|
||||
'Generate a beautiful sunrise scene where the sun slowly rises from behind mountains with clouds gently floating',
|
||||
'Create a forest scene with leaves gently swaying in the breeze and dappled sunlight',
|
||||
'Generate a seaside scene with waves gently lapping the shore and seagulls circling overhead',
|
||||
'Create a garden scene with flowers swaying in the breeze and butterflies dancing',
|
||||
'Generate a post-rain scene with water drops slowly dripping from leaves and a rainbow appearing in the sky',
|
||||
'Create a winter scene with snowflakes gently falling',
|
||||
'Generate a starry night scene with twinkling stars and clouds slowly drifting across the moon',
|
||||
'Create a stream scene with clear water flowing gently between stones and fish swimming by'
|
||||
]
|
||||
} as const;
|
||||
|
||||
// 类型定义
|
||||
export type Language = 'zh' | 'en';
|
||||
|
||||
// 获取图像生成提示词的辅助函数
|
||||
export const getImagePrompts = (language: Language): readonly string[] => {
|
||||
return AI_IMAGE_PROMPTS[language];
|
||||
};
|
||||
|
||||
// 获取视频生成提示词的辅助函数
|
||||
export const getVideoPrompts = (language: Language): readonly string[] => {
|
||||
return AI_VIDEO_PROMPTS[language];
|
||||
};
|
||||
@@ -84,21 +84,6 @@ export class CryptoUtils {
|
||||
return `drawnix-v2-${stableInfo}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成旧版密码种子 (v1) - 用于向后兼容
|
||||
*/
|
||||
private static generateLegacyPasswordSeed(): string {
|
||||
const deviceId = this.getDeviceId();
|
||||
const stableInfo = [
|
||||
deviceId,
|
||||
screen.width,
|
||||
screen.height,
|
||||
navigator.language || 'en-US',
|
||||
Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'
|
||||
].join('-');
|
||||
|
||||
return `drawnix-v1-${stableInfo}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密数据
|
||||
@@ -149,38 +134,19 @@ export class CryptoUtils {
|
||||
const iv = this.base64ToArrayBuffer(parsed.iv);
|
||||
const salt = this.base64ToArrayBuffer(parsed.salt);
|
||||
|
||||
// 先尝试新版密钥
|
||||
try {
|
||||
const password = this.generatePasswordSeed();
|
||||
const key = await this.deriveKey(password, new Uint8Array(salt));
|
||||
|
||||
const decrypted = await crypto.subtle.decrypt(
|
||||
{ name: ALGORITHM, iv: iv as BufferSource },
|
||||
key,
|
||||
data
|
||||
);
|
||||
|
||||
const decoder = new TextDecoder();
|
||||
return decoder.decode(decrypted);
|
||||
} catch (newKeyError) {
|
||||
// 新版密钥失败,尝试旧版密钥
|
||||
console.warn('New key failed, trying legacy key:', newKeyError);
|
||||
|
||||
const legacyPassword = this.generateLegacyPasswordSeed();
|
||||
const legacyKey = await this.deriveKey(legacyPassword, new Uint8Array(salt));
|
||||
|
||||
const decrypted = await crypto.subtle.decrypt(
|
||||
{ name: ALGORITHM, iv: iv as BufferSource },
|
||||
legacyKey,
|
||||
data
|
||||
);
|
||||
|
||||
const decoder = new TextDecoder();
|
||||
const result = decoder.decode(decrypted);
|
||||
|
||||
console.log('Successfully decrypted with legacy key, data needs migration');
|
||||
return result;
|
||||
}
|
||||
// 生成解密密钥
|
||||
const password = this.generatePasswordSeed();
|
||||
const key = await this.deriveKey(password, new Uint8Array(salt));
|
||||
|
||||
// 解密数据
|
||||
const decrypted = await crypto.subtle.decrypt(
|
||||
{ name: ALGORITHM, iv: iv as BufferSource },
|
||||
key,
|
||||
data
|
||||
);
|
||||
|
||||
const decoder = new TextDecoder();
|
||||
return decoder.decode(decrypted);
|
||||
} catch (error) {
|
||||
console.error('Decryption failed:', error);
|
||||
throw new Error('Failed to decrypt data');
|
||||
|
||||
@@ -68,7 +68,6 @@ class SettingsManager {
|
||||
private async initializeAsync(): Promise<void> {
|
||||
try {
|
||||
await this.initializeCrypto();
|
||||
await this.migrateStoredData();
|
||||
this.initializeFromUrl();
|
||||
console.log('SettingsManager initialization completed');
|
||||
} catch (error) {
|
||||
@@ -133,26 +132,6 @@ class SettingsManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密敏感数据
|
||||
*/
|
||||
private async decryptSensitiveData(path: string, value: string): Promise<string> {
|
||||
if (!this.isSensitiveField(path) || !this.cryptoAvailable) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// 检查数据是否已加密
|
||||
if (!CryptoUtils.isEncrypted(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
try {
|
||||
return await CryptoUtils.decrypt(value);
|
||||
} catch (error) {
|
||||
console.warn(`Failed to decrypt sensitive data for ${path}:`, error);
|
||||
return value; // 解密失败时返回原值
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从本地存储加载设置
|
||||
@@ -177,36 +156,6 @@ class SettingsManager {
|
||||
return settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* 迁移已存储的数据(处理加密数据)
|
||||
*/
|
||||
private async migrateStoredData(): Promise<void> {
|
||||
let hasChanges = false;
|
||||
|
||||
for (const fieldPath of SENSITIVE_FIELDS) {
|
||||
const value = this.getSetting(fieldPath);
|
||||
if (value && typeof value === 'string') {
|
||||
try {
|
||||
// 如果是已加密的数据,解密到内存中
|
||||
if (CryptoUtils.isEncrypted(value)) {
|
||||
const decryptedValue = await this.decryptSensitiveData(fieldPath, value);
|
||||
if (decryptedValue !== value) {
|
||||
this.setNestedValue(this.settings, fieldPath, decryptedValue);
|
||||
console.log(`Decrypted stored sensitive field: ${fieldPath}`);
|
||||
hasChanges = true;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`Failed to decrypt stored field ${fieldPath}:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果有解密操作,重新保存以确保数据一致性
|
||||
if (hasChanges) {
|
||||
await this.saveToStorage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置嵌套对象的值
|
||||
|
||||
Reference in new issue
Block a user