Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
B
bit-pm
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
燕伟桐
bit-pm
Commits
922ee7d9
提交
922ee7d9
authored
4月 23, 2025
作者:
Will Chen
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Handle token refresh for supabase
上级
67e7e730
隐藏空白字符变更
内嵌
并排
正在显示
4 个修改的文件
包含
108 行增加
和
12 行删除
+108
-12
lock_utils.ts
src/ipc/utils/lock_utils.ts
+10
-11
schemas.ts
src/lib/schemas.ts
+1
-0
supabase_management_client.ts
src/supabase_admin/supabase_management_client.ts
+96
-1
supabase_return_handler.ts
src/supabase_admin/supabase_return_handler.ts
+1
-0
没有找到文件。
src/ipc/utils/lock_utils.ts
浏览文件 @
922ee7d9
// Track app operations that are in progress
const
locks
=
new
Map
<
number
|
string
,
Promise
<
void
>>
();
const
appOperationLocks
=
new
Map
<
number
,
Promise
<
void
>>
();
/**
/**
* Acquires a lock for an app operation
* Acquires a lock for an app operation
* @param
app
Id The app ID to lock
* @param
lock
Id The app ID to lock
* @returns An object with release function and promise
* @returns An object with release function and promise
*/
*/
export
function
acquireLock
(
appId
:
number
):
{
export
function
acquireLock
(
lockId
:
number
|
string
):
{
release
:
()
=>
void
;
release
:
()
=>
void
;
promise
:
Promise
<
void
>
;
promise
:
Promise
<
void
>
;
}
{
}
{
...
@@ -14,33 +13,33 @@ export function acquireLock(appId: number): {
...
@@ -14,33 +13,33 @@ export function acquireLock(appId: number): {
const
promise
=
new
Promise
<
void
>
((
resolve
)
=>
{
const
promise
=
new
Promise
<
void
>
((
resolve
)
=>
{
release
=
()
=>
{
release
=
()
=>
{
appOperationLocks
.
delete
(
app
Id
);
locks
.
delete
(
lock
Id
);
resolve
();
resolve
();
};
};
});
});
appOperationLocks
.
set
(
app
Id
,
promise
);
locks
.
set
(
lock
Id
,
promise
);
return
{
release
,
promise
};
return
{
release
,
promise
};
}
}
/**
/**
* Executes a function with a lock on the
app
ID
* Executes a function with a lock on the
lock
ID
* @param
appId The app
ID to lock
* @param
lockId The lock
ID to lock
* @param fn The function to execute with the lock
* @param fn The function to execute with the lock
* @returns Result of the function
* @returns Result of the function
*/
*/
export
async
function
withLock
<
T
>
(
export
async
function
withLock
<
T
>
(
appId
:
number
,
lockId
:
number
|
string
,
fn
:
()
=>
Promise
<
T
>
fn
:
()
=>
Promise
<
T
>
):
Promise
<
T
>
{
):
Promise
<
T
>
{
// Wait for any existing operation to complete
// Wait for any existing operation to complete
const
existingLock
=
appOperationLocks
.
get
(
app
Id
);
const
existingLock
=
locks
.
get
(
lock
Id
);
if
(
existingLock
)
{
if
(
existingLock
)
{
await
existingLock
;
await
existingLock
;
}
}
// Acquire a new lock
// Acquire a new lock
const
{
release
,
promise
}
=
acquireLock
(
app
Id
);
const
{
release
,
promise
}
=
acquireLock
(
lock
Id
);
try
{
try
{
const
result
=
await
fn
();
const
result
=
await
fn
();
...
...
src/lib/schemas.ts
浏览文件 @
922ee7d9
...
@@ -84,6 +84,7 @@ export const SupabaseSchema = z.object({
...
@@ -84,6 +84,7 @@ export const SupabaseSchema = z.object({
accessToken
:
SecretSchema
.
optional
(),
accessToken
:
SecretSchema
.
optional
(),
refreshToken
:
SecretSchema
.
optional
(),
refreshToken
:
SecretSchema
.
optional
(),
expiresIn
:
z
.
number
().
optional
(),
expiresIn
:
z
.
number
().
optional
(),
tokenTimestamp
:
z
.
number
().
optional
(),
});
});
export
type
Supabase
=
z
.
infer
<
typeof
SupabaseSchema
>
;
export
type
Supabase
=
z
.
infer
<
typeof
SupabaseSchema
>
;
...
...
src/supabase_admin/supabase_management_client.ts
浏览文件 @
922ee7d9
import
{
readSettings
}
from
"../main/settings"
;
import
{
withLock
}
from
"../ipc/utils/lock_utils"
;
import
{
readSettings
,
writeSettings
}
from
"../main/settings"
;
import
{
SupabaseManagementAPI
}
from
"supabase-management-js"
;
import
{
SupabaseManagementAPI
}
from
"supabase-management-js"
;
/**
* Checks if the Supabase access token is expired or about to expire
* Returns true if token needs to be refreshed
*/
function
isTokenExpired
(
expiresIn
?:
number
):
boolean
{
if
(
!
expiresIn
)
return
true
;
// Get when the token was saved (expiresIn is stored at the time of token receipt)
const
settings
=
readSettings
();
const
tokenTimestamp
=
settings
.
supabase
?.
tokenTimestamp
||
0
;
const
currentTime
=
Math
.
floor
(
Date
.
now
()
/
1000
);
// Check if the token is expired or about to expire (within 5 minutes)
return
currentTime
>=
tokenTimestamp
+
expiresIn
-
300
;
}
/**
* Refreshes the Supabase access token using the refresh token
* Updates settings with new tokens and expiration time
*/
export
async
function
refreshSupabaseToken
():
Promise
<
void
>
{
const
settings
=
readSettings
();
const
refreshToken
=
settings
.
supabase
?.
refreshToken
?.
value
;
if
(
!
isTokenExpired
(
settings
.
supabase
?.
expiresIn
))
{
return
;
}
if
(
!
refreshToken
)
{
throw
new
Error
(
"Supabase refresh token not found. Please authenticate first."
);
}
try
{
// Make request to Supabase refresh endpoint
const
response
=
await
fetch
(
"https://supabase-oauth.dyad.sh/api/connect-supabase/refresh"
,
{
method
:
"POST"
,
headers
:
{
"Content-Type"
:
"application/json"
,
},
body
:
JSON
.
stringify
({
refreshToken
}),
}
);
if
(
!
response
.
ok
)
{
throw
new
Error
(
`Token refresh failed:
${
response
.
statusText
}
`
);
}
const
{
accessToken
,
refreshToken
:
newRefreshToken
,
expiresIn
,
}
=
await
response
.
json
();
// Update settings with new tokens
writeSettings
({
supabase
:
{
accessToken
:
{
value
:
accessToken
,
},
refreshToken
:
{
value
:
newRefreshToken
,
},
expiresIn
,
tokenTimestamp
:
Math
.
floor
(
Date
.
now
()
/
1000
),
// Store current timestamp
},
});
}
catch
(
error
)
{
console
.
error
(
"Error refreshing Supabase token:"
,
error
);
throw
error
;
}
}
// Function to get the Supabase Management API client
// Function to get the Supabase Management API client
export
async
function
getSupabaseClient
():
Promise
<
SupabaseManagementAPI
>
{
export
async
function
getSupabaseClient
():
Promise
<
SupabaseManagementAPI
>
{
const
settings
=
readSettings
();
const
settings
=
readSettings
();
// Check if Supabase token exists in settings
// Check if Supabase token exists in settings
const
supabaseAccessToken
=
settings
.
supabase
?.
accessToken
?.
value
;
const
supabaseAccessToken
=
settings
.
supabase
?.
accessToken
?.
value
;
const
expiresIn
=
settings
.
supabase
?.
expiresIn
;
if
(
!
supabaseAccessToken
)
{
if
(
!
supabaseAccessToken
)
{
throw
new
Error
(
throw
new
Error
(
...
@@ -13,6 +92,22 @@ export async function getSupabaseClient(): Promise<SupabaseManagementAPI> {
...
@@ -13,6 +92,22 @@ export async function getSupabaseClient(): Promise<SupabaseManagementAPI> {
);
);
}
}
// Check if token needs refreshing
if
(
isTokenExpired
(
expiresIn
))
{
await
withLock
(
"refresh-supabase-token"
,
refreshSupabaseToken
);
// Get updated settings after refresh
const
updatedSettings
=
readSettings
();
const
newAccessToken
=
updatedSettings
.
supabase
?.
accessToken
?.
value
;
if
(
!
newAccessToken
)
{
throw
new
Error
(
"Failed to refresh Supabase access token"
);
}
return
new
SupabaseManagementAPI
({
accessToken
:
newAccessToken
,
});
}
return
new
SupabaseManagementAPI
({
return
new
SupabaseManagementAPI
({
accessToken
:
supabaseAccessToken
,
accessToken
:
supabaseAccessToken
,
});
});
...
...
src/supabase_admin/supabase_return_handler.ts
浏览文件 @
922ee7d9
...
@@ -18,6 +18,7 @@ export function handleSupabaseOAuthReturn({
...
@@ -18,6 +18,7 @@ export function handleSupabaseOAuthReturn({
value
:
refreshToken
,
value
:
refreshToken
,
},
},
expiresIn
,
expiresIn
,
tokenTimestamp
:
Math
.
floor
(
Date
.
now
()
/
1000
),
},
},
});
});
}
}
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论