Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
B
bit-pm
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
燕伟桐
bit-pm
Commits
658d4e0b
提交
658d4e0b
authored
4月 15, 2025
作者:
Will Chen
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
proper secret encrpytion
上级
1c325ecc
显示空白字符变更
内嵌
并排
正在显示
7 个修改的文件
包含
77 行增加
和
53 行删除
+77
-53
GitHubConnector.tsx
src/components/GitHubConnector.tsx
+1
-1
ProviderSettingsPage.tsx
src/components/settings/ProviderSettingsPage.tsx
+5
-3
github_handlers.ts
src/ipc/handlers/github_handlers.ts
+10
-9
settings_handlers.ts
src/ipc/handlers/settings_handlers.ts
+1
-5
get_model_client.ts
src/ipc/utils/get_model_client.ts
+1
-1
schemas.ts
src/lib/schemas.ts
+9
-8
settings.ts
src/main/settings.ts
+50
-26
没有找到文件。
src/components/GitHubConnector.tsx
浏览文件 @
658d4e0b
...
...
@@ -171,7 +171,7 @@ export function GitHubConnector({ appId, folderName }: GitHubConnectorProps) {
}
};
if
(
!
settings
?.
github
Settings
.
secrets
?.
a
ccessToken
)
{
if
(
!
settings
?.
github
A
ccessToken
)
{
return
(
<
div
className=
"mt-4 w-full"
>
{
" "
}
...
...
src/components/settings/ProviderSettingsPage.tsx
浏览文件 @
658d4e0b
...
...
@@ -59,7 +59,7 @@ export function ProviderSettingsPage({ provider }: ProviderSettingsPageProps) {
const
envVarName
=
PROVIDER_TO_ENV_VAR
[
provider
];
const
envApiKey
=
envVars
[
envVarName
];
const
userApiKey
=
settings
?.
providerSettings
?.[
provider
]?.
apiKey
;
const
userApiKey
=
settings
?.
providerSettings
?.[
provider
]?.
apiKey
?.
value
;
// --- Configuration Logic --- Updated Priority ---
const
isValidUserKey
=
...
...
@@ -100,7 +100,9 @@ export function ProviderSettingsPage({ provider }: ProviderSettingsPageProps) {
...
settings
?.
providerSettings
,
[
provider
]:
{
...(
settings
?.
providerSettings
?.[
provider
]
||
{}),
apiKey
:
apiKeyInput
,
apiKey
:
{
value
:
apiKeyInput
,
},
},
},
});
...
...
@@ -124,7 +126,7 @@ export function ProviderSettingsPage({ provider }: ProviderSettingsPageProps) {
...
settings
?.
providerSettings
,
[
provider
]:
{
...(
settings
?.
providerSettings
?.[
provider
]
||
{}),
apiKey
:
null
,
apiKey
:
undefined
,
},
},
});
...
...
src/ipc/handlers/github_handlers.ts
浏览文件 @
658d4e0b
...
...
@@ -48,7 +48,7 @@ export async function getGithubUser(): Promise<GithubUser | null> {
const
email
=
settings
.
githubUser
?.
email
;
if
(
email
)
return
{
email
};
try
{
const
accessToken
=
settings
.
github
Settings
?.
secrets
?.
accessToken
;
const
accessToken
=
settings
.
github
AccessToken
?.
value
;
if
(
!
accessToken
)
return
null
;
const
res
=
await
fetch
(
"https://api.github.com/user/emails"
,
{
headers
:
{
Authorization
:
`Bearer
${
accessToken
}
`
},
...
...
@@ -116,10 +116,8 @@ async function pollForAccessToken(event: IpcMainInvokeEvent) {
message
:
"Successfully connected!"
,
});
writeSettings
({
githubSettings
:
{
secrets
:
{
accessToken
:
data
.
access_token
,
},
githubAccessToken
:
{
value
:
data
.
access_token
,
},
});
// TODO: Associate token with appId if provided
...
...
@@ -324,7 +322,7 @@ async function handleIsRepoAvailable(
try
{
// Get access token from settings
const
settings
=
readSettings
();
const
accessToken
=
settings
.
github
Settings
?.
secrets
?.
accessToken
;
const
accessToken
=
settings
.
github
AccessToken
?.
value
;
if
(
!
accessToken
)
{
return
{
available
:
false
,
error
:
"Not authenticated with GitHub."
};
}
...
...
@@ -362,7 +360,7 @@ async function handleCreateRepo(
try
{
// Get access token from settings
const
settings
=
readSettings
();
const
accessToken
=
settings
.
github
Settings
?.
secrets
?.
accessToken
;
const
accessToken
=
settings
.
github
AccessToken
?.
value
;
if
(
!
accessToken
)
{
return
{
success
:
false
,
error
:
"Not authenticated with GitHub."
};
}
...
...
@@ -411,7 +409,7 @@ async function handlePushToGithub(
try
{
// Get access token from settings
const
settings
=
readSettings
();
const
accessToken
=
settings
.
github
Settings
?.
secrets
?.
accessToken
;
const
accessToken
=
settings
.
github
AccessToken
?.
value
;
if
(
!
accessToken
)
{
return
{
success
:
false
,
error
:
"Not authenticated with GitHub."
};
}
...
...
@@ -437,7 +435,10 @@ async function handlePushToGithub(
dir
:
appPath
,
remote
:
"origin"
,
ref
:
"main"
,
onAuth
:
()
=>
({
username
:
accessToken
,
password
:
"x-oauth-basic"
}),
onAuth
:
()
=>
({
username
:
accessToken
,
password
:
"x-oauth-basic"
,
}),
force
:
false
,
});
return
{
success
:
true
};
...
...
src/ipc/handlers/settings_handlers.ts
浏览文件 @
658d4e0b
...
...
@@ -20,11 +20,7 @@ export function registerSettingsHandlers() {
)
{
const
providerSetting
=
settings
.
providerSettings
[
providerKey
];
// Check if apiKey exists and is a non-empty string before masking
if
(
providerSetting
?.
apiKey
&&
typeof
providerSetting
.
apiKey
===
"string"
&&
providerSetting
.
apiKey
.
length
>
0
)
{
if
(
providerSetting
?.
apiKey
?.
value
)
{
providerSetting
.
apiKey
=
providerSetting
.
apiKey
;
}
}
...
...
src/ipc/utils/get_model_client.ts
浏览文件 @
658d4e0b
...
...
@@ -37,7 +37,7 @@ export function getModelClient(
}
const
apiKey
=
settings
.
providerSettings
?.[
model
.
provider
]?.
apiKey
||
settings
.
providerSettings
?.[
model
.
provider
]?.
apiKey
?.
value
||
getEnvVar
(
PROVIDER_TO_ENV_VAR
[
model
.
provider
]);
switch
(
model
.
provider
)
{
case
"openai"
:
{
...
...
src/lib/schemas.ts
浏览文件 @
658d4e0b
import
{
z
}
from
"zod"
;
export
const
SecretSchema
=
z
.
object
({
value
:
z
.
string
(),
encryptionType
:
z
.
enum
([
"electron-safe-storage"
,
"plaintext"
]).
optional
(),
});
export
type
Secret
=
z
.
infer
<
typeof
SecretSchema
>
;
/**
* Zod schema for chat summary objects returned by the get-chats IPC
*/
...
...
@@ -53,7 +59,7 @@ export type LargeLanguageModel = z.infer<typeof LargeLanguageModelSchema>;
* Zod schema for provider settings
*/
export
const
ProviderSettingSchema
=
z
.
object
({
apiKey
:
z
.
string
().
nullable
(),
apiKey
:
SecretSchema
.
optional
(),
});
/**
...
...
@@ -65,15 +71,10 @@ export const RuntimeModeSchema = z.enum(["web-sandbox", "local-node", "unset"]);
export
type
RuntimeMode
=
z
.
infer
<
typeof
RuntimeModeSchema
>
;
export
const
GitHubSecretsSchema
=
z
.
object
({
accessToken
:
z
.
string
()
.
nullable
(),
accessToken
:
SecretSchema
.
nullable
(),
});
export
type
GitHubSecrets
=
z
.
infer
<
typeof
GitHubSecretsSchema
>
;
export
const
GitHubSettingsSchema
=
z
.
object
({
secrets
:
GitHubSecretsSchema
.
nullable
(),
});
export
type
GitHubSettings
=
z
.
infer
<
typeof
GitHubSettingsSchema
>
;
export
const
GithubUserSchema
=
z
.
object
({
email
:
z
.
string
(),
});
...
...
@@ -86,8 +87,8 @@ export const UserSettingsSchema = z.object({
selectedModel
:
LargeLanguageModelSchema
,
providerSettings
:
z
.
record
(
z
.
string
(),
ProviderSettingSchema
),
runtimeMode
:
RuntimeModeSchema
,
githubSettings
:
GitHubSettingsSchema
,
githubUser
:
GithubUserSchema
.
optional
(),
githubAccessToken
:
SecretSchema
.
optional
(),
});
/**
...
...
src/main/settings.ts
浏览文件 @
658d4e0b
import
fs
from
"node:fs"
;
import
path
from
"node:path"
;
import
{
getUserDataPath
}
from
"../paths/paths"
;
import
{
UserSettingsSchema
,
type
UserSettings
}
from
"../lib/schemas"
;
import
{
UserSettingsSchema
,
type
UserSettings
,
Secret
}
from
"../lib/schemas"
;
import
{
safeStorage
}
from
"electron"
;
// IF YOU NEED TO UPDATE THIS, YOU'RE PROBABLY DOING SOMETHING WRONG!
// Need to maintain backwards compatibility!
const
DEFAULT_SETTINGS
:
UserSettings
=
{
selectedModel
:
{
name
:
"auto"
,
...
...
@@ -11,9 +13,6 @@ const DEFAULT_SETTINGS: UserSettings = {
},
providerSettings
:
{},
runtimeMode
:
"unset"
,
githubSettings
:
{
secrets
:
null
,
},
};
const
SETTINGS_FILE
=
"user-settings.json"
;
...
...
@@ -30,18 +29,31 @@ export function readSettings(): UserSettings {
return
DEFAULT_SETTINGS
;
}
const
rawSettings
=
JSON
.
parse
(
fs
.
readFileSync
(
filePath
,
"utf-8"
));
// Validate and merge with defaults
const
validatedSettings
=
UserSettingsSchema
.
parse
({
const
combinedSettings
:
UserSettings
=
{
...
DEFAULT_SETTINGS
,
...
rawSettings
,
});
if
(
validatedSettings
.
githubSettings
?.
secrets
)
{
const
accessToken
=
validatedSettings
.
githubSettings
.
secrets
.
accessToken
;
validatedSettings
.
githubSettings
.
secrets
=
{
accessToken
:
accessToken
?
decrypt
(
accessToken
)
:
null
,
};
if
(
combinedSettings
.
githubAccessToken
)
{
const
encryptionType
=
combinedSettings
.
githubAccessToken
.
encryptionType
;
combinedSettings
.
githubAccessToken
=
{
value
:
decrypt
(
combinedSettings
.
githubAccessToken
),
encryptionType
,
};
}
for
(
const
provider
in
combinedSettings
.
providerSettings
)
{
if
(
combinedSettings
.
providerSettings
[
provider
].
apiKey
)
{
const
encryptionType
=
combinedSettings
.
providerSettings
[
provider
].
apiKey
.
encryptionType
;
combinedSettings
.
providerSettings
[
provider
].
apiKey
=
{
value
:
decrypt
(
combinedSettings
.
providerSettings
[
provider
].
apiKey
),
encryptionType
,
};
}
}
// Validate and merge with defaults
const
validatedSettings
=
UserSettingsSchema
.
parse
(
combinedSettings
);
return
validatedSettings
;
}
catch
(
error
)
{
console
.
error
(
"Error reading settings:"
,
error
);
...
...
@@ -54,30 +66,42 @@ export function writeSettings(settings: Partial<UserSettings>): void {
const
filePath
=
getSettingsFilePath
();
const
currentSettings
=
readSettings
();
const
newSettings
=
{
...
currentSettings
,
...
settings
};
// Validate before writing
const
validatedSettings
=
UserSettingsSchema
.
parse
(
newSettings
);
if
(
validatedSettings
.
githubSettings
?.
secrets
)
{
const
accessToken
=
validatedSettings
.
githubSettings
.
secrets
.
accessToken
;
validatedSettings
.
githubSettings
.
secrets
=
{
accessToken
:
accessToken
?
encrypt
(
accessToken
)
:
null
,
};
if
(
newSettings
.
githubAccessToken
)
{
newSettings
.
githubAccessToken
=
encrypt
(
newSettings
.
githubAccessToken
.
value
);
}
for
(
const
provider
in
newSettings
.
providerSettings
)
{
if
(
newSettings
.
providerSettings
[
provider
].
apiKey
)
{
newSettings
.
providerSettings
[
provider
].
apiKey
=
encrypt
(
newSettings
.
providerSettings
[
provider
].
apiKey
.
value
);
}
}
const
validatedSettings
=
UserSettingsSchema
.
parse
(
newSettings
);
fs
.
writeFileSync
(
filePath
,
JSON
.
stringify
(
validatedSettings
,
null
,
2
));
}
catch
(
error
)
{
console
.
error
(
"Error writing settings:"
,
error
);
}
}
export
function
encrypt
(
data
:
string
):
string
{
export
function
encrypt
(
data
:
string
):
Secret
{
if
(
safeStorage
.
isEncryptionAvailable
())
{
return
safeStorage
.
encryptString
(
data
).
toString
(
"base64"
);
return
{
value
:
safeStorage
.
encryptString
(
data
).
toString
(
"base64"
),
encryptionType
:
"electron-safe-storage"
,
};
}
return
data
;
return
{
value
:
data
,
encryptionType
:
"plaintext"
,
};
}
export
function
decrypt
(
data
:
string
):
string
{
if
(
safeStorage
.
isEncryptionAvailable
()
)
{
return
safeStorage
.
decryptString
(
Buffer
.
from
(
data
,
"base64"
));
export
function
decrypt
(
data
:
Secret
):
string
{
if
(
data
.
encryptionType
===
"electron-safe-storage"
)
{
return
safeStorage
.
decryptString
(
Buffer
.
from
(
data
.
value
,
"base64"
));
}
return
data
;
return
data
.
value
;
}
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论