feat: add last updated timestamp to provider tokens in dashboard and API
This commit is contained in:
@@ -39,5 +39,6 @@ export class TokenManager {
|
||||
tokens.expiresIn,
|
||||
);
|
||||
await this.redis.set(`provider:${providerName}:refresh_token`, tokens.refreshToken);
|
||||
await this.redis.set(`provider:${providerName}:last_updated`, new Date().toISOString());
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -17,7 +17,8 @@ apiRoutes.get("/status", async (c) => {
|
||||
for (const provider of Object.keys(providers)) {
|
||||
const accessToken = await redis.get(`provider:${provider}:access_token`);
|
||||
const refreshToken = await redis.get(`provider:${provider}:refresh_token`);
|
||||
status[provider] = { accessToken, refreshToken };
|
||||
const lastUpdated = await redis.get(`provider:${provider}:last_updated`);
|
||||
status[provider] = { accessToken, refreshToken, lastUpdated } as any;
|
||||
}
|
||||
|
||||
return c.json(status);
|
||||
|
||||
@@ -140,13 +140,14 @@
|
||||
<div class="relative group">
|
||||
<input type="url" id="redirectUri" readonly
|
||||
class="input input-bordered w-full pr-12 focus:outline-none cursor-default opacity-80" />
|
||||
<button type="button" onclick="copyRedirectUri()"
|
||||
<button type="button" onclick="copyRedirectUri()"
|
||||
class="btn btn-ghost btn-xs absolute right-2 top-1/2 -translate-y-1/2 text-base-content/40 hover:text-primary transition-colors">
|
||||
<i class="ph ph-copy text-sm"></i>
|
||||
</button>
|
||||
</div>
|
||||
<label class="label py-0.5">
|
||||
<span class="label-text-alt opacity-40 italic text-[10px]">Must match provider's callback URL</span>
|
||||
<span class="label-text-alt opacity-40 italic text-[10px]">Must match provider's
|
||||
callback URL</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
@@ -193,12 +194,17 @@
|
||||
<th
|
||||
class="rounded-none font-bold uppercase tracking-wider text-[10px] opacity-60 px-6">
|
||||
Provider</th>
|
||||
<th class="font-bold uppercase tracking-wider text-[10px] opacity-60 px-6">Access
|
||||
<th class="font-bold uppercase tracking-wider text-[10px] opacity-60 px-6 w-1/3">
|
||||
Access
|
||||
Token</th>
|
||||
<th class="font-bold uppercase tracking-wider text-[10px] opacity-60 px-6">Refresh
|
||||
<th class="font-bold uppercase tracking-wider text-[10px] opacity-60 px-6 w-1/3">
|
||||
Refresh
|
||||
Token</th>
|
||||
<th
|
||||
class="rounded-none text-right font-bold uppercase tracking-wider text-[10px] opacity-60 px-6">
|
||||
class="font-bold uppercase tracking-wider text-[10px] opacity-60 px-6 whitespace-nowrap">
|
||||
Last Updated</th>
|
||||
<th
|
||||
class="rounded-none text-right font-bold uppercase tracking-wider text-[10px] opacity-60 px-1 w-px">
|
||||
Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -291,17 +297,29 @@
|
||||
}
|
||||
}
|
||||
|
||||
function formatTimeAgo(dateString) {
|
||||
if (!dateString) return '<span class="opacity-30">Never</span>';
|
||||
const date = new Date(dateString);
|
||||
const now = new Date();
|
||||
const diffInSeconds = Math.floor((now - date) / 1000);
|
||||
|
||||
if (diffInSeconds < 60) return 'Just now';
|
||||
if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)}m ago`;
|
||||
if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)}h ago`;
|
||||
return date.toLocaleDateString();
|
||||
}
|
||||
|
||||
function renderTable() {
|
||||
providerTableBody.innerHTML = '';
|
||||
const entries = Object.entries(providerData);
|
||||
|
||||
if (entries.length === 0) {
|
||||
providerTableBody.innerHTML = '<tr><td colspan="4" class="text-center py-16 opacity-50 font-medium italic">No providers configured yet.</td></tr>';
|
||||
providerTableBody.innerHTML = '<tr><td colspan="5" class="text-center py-16 opacity-50 font-medium italic">No providers configured yet.</td></tr>';
|
||||
return;
|
||||
}
|
||||
|
||||
for (const [name, config] of entries) {
|
||||
const status = tokenStatus[name] || { accessToken: null, refreshToken: null };
|
||||
const status = tokenStatus[name] || { accessToken: null, refreshToken: null, lastUpdated: null };
|
||||
const row = document.createElement('tr');
|
||||
row.className = "hover:bg-base-200/30 transition-colors group";
|
||||
|
||||
@@ -314,6 +332,9 @@
|
||||
</td>
|
||||
<td class="px-6 py-4">${renderTokenCell(name, 'access', status.accessToken)}</td>
|
||||
<td class="px-6 py-4">${renderTokenCell(name, 'refresh', status.refreshToken)}</td>
|
||||
<td class="px-6 py-4">
|
||||
<span class="text-xs font-medium opacity-60">${formatTimeAgo(status.lastUpdated)}</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-right">
|
||||
<div class="flex gap-2 justify-end opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
<a href="/auth/${name}/login" target="_blank" class="btn btn-xs btn-primary shadow-sm" title="Authenticate">
|
||||
|
||||
Reference in New Issue
Block a user