mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-01-11 22:40:26 +01:00
Update 1Password plugin for simpler fetching and state handling (#353)
This commit is contained in:
@@ -12,14 +12,16 @@ interface CacheEntry<T> {
|
||||
expiresAt: number;
|
||||
}
|
||||
|
||||
type Result<T> = { error: unknown } | T;
|
||||
|
||||
const CACHE_TTL_MS = 60 * 1000; // 1 minute cache TTL
|
||||
const _cache: Record<string, CacheEntry<unknown>> = {};
|
||||
|
||||
async function op(
|
||||
args: CallTemplateFunctionArgs,
|
||||
): Promise<{ client?: Client; clientHash?: string; error?: unknown }> {
|
||||
let authMethod: string | DesktopAuth | null = null;
|
||||
let hash: string | null = null;
|
||||
): Promise<Result<{ client: Client; clientHash: string }>> {
|
||||
let authMethod: string | DesktopAuth;
|
||||
let hash: string;
|
||||
switch (args.values.authMethod) {
|
||||
case 'desktop': {
|
||||
const account = args.values.token;
|
||||
@@ -37,18 +39,20 @@ async function op(
|
||||
authMethod = token;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return { error: 'Invalid authentication method' };
|
||||
}
|
||||
|
||||
if (hash == null || authMethod == null) return { error: 'Invalid authentication method' };
|
||||
|
||||
try {
|
||||
_clients[hash] ??= await createClient({
|
||||
auth: authMethod,
|
||||
integrationName: 'Yaak 1Password Plugin',
|
||||
integrationVersion: 'v1.0.0',
|
||||
});
|
||||
} catch (e) {
|
||||
return { error: e };
|
||||
if (!_clients[hash]) {
|
||||
try {
|
||||
_clients[hash] = await createClient({
|
||||
auth: authMethod,
|
||||
integrationName: 'Yaak 1Password Plugin',
|
||||
integrationVersion: 'v1.0.0',
|
||||
});
|
||||
} catch (e) {
|
||||
return { error: e };
|
||||
}
|
||||
}
|
||||
|
||||
return { client: _clients[hash], clientHash: hash };
|
||||
@@ -59,47 +63,35 @@ async function getValue(
|
||||
vaultId?: JsonPrimitive,
|
||||
itemId?: JsonPrimitive,
|
||||
fieldId?: JsonPrimitive,
|
||||
): Promise<{ value?: string; error?: unknown }> {
|
||||
const { client, error, clientHash } = await op(args);
|
||||
if (!client || !clientHash) return { error };
|
||||
): Promise<Result<{ value: string }>> {
|
||||
const res = await op(args);
|
||||
if ('error' in res) return { error: res.error };
|
||||
const clientHash = res.clientHash;
|
||||
const client = res.client;
|
||||
|
||||
if (vaultId && typeof vaultId === 'string') {
|
||||
const vaultCacheKey = `${clientHash}:vault:${vaultId}`;
|
||||
const cachedVault = getCached(vaultCacheKey);
|
||||
if (!cachedVault) {
|
||||
try {
|
||||
const vault = await client.vaults.getOverview(vaultId);
|
||||
setCache(vaultCacheKey, vault);
|
||||
} catch {
|
||||
return { error: `Vault ${vaultId} not found` };
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!vaultId || typeof vaultId !== 'string') {
|
||||
return { error: 'No vault specified' };
|
||||
}
|
||||
|
||||
if (itemId && typeof itemId === 'string') {
|
||||
const itemCacheKey = `${clientHash}:item:${vaultId}:${itemId}`;
|
||||
try {
|
||||
const item =
|
||||
getCached<Awaited<ReturnType<typeof client.items.get>>>(itemCacheKey) ??
|
||||
setCache(itemCacheKey, await client.items.get(vaultId, itemId));
|
||||
if (fieldId && typeof fieldId === 'string') {
|
||||
const field = item.fields.find((f) => f.id === fieldId);
|
||||
if (field) {
|
||||
return { value: field.value };
|
||||
} else {
|
||||
return { error: `Field ${fieldId} not found in item ${itemId} in vault ${vaultId}` };
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
return { error: `Item ${itemId} not found in vault ${vaultId}` };
|
||||
}
|
||||
} else {
|
||||
if (!itemId || typeof itemId !== 'string') {
|
||||
return { error: 'No item specified' };
|
||||
}
|
||||
if (!fieldId || typeof fieldId !== 'string') {
|
||||
return { error: 'No field specified' };
|
||||
}
|
||||
|
||||
return {};
|
||||
try {
|
||||
const cacheKey = `${clientHash}:item:${vaultId}:${itemId}:${fieldId}`;
|
||||
let value = getCached<string>(cacheKey);
|
||||
|
||||
if (!value) {
|
||||
value = await client.secrets.resolve(`op://${vaultId}/${itemId}/${fieldId}`);
|
||||
setCache(cacheKey, value);
|
||||
}
|
||||
|
||||
return { value };
|
||||
} catch (e) {
|
||||
return { error: e };
|
||||
}
|
||||
}
|
||||
|
||||
export const plugin: PluginDefinition = {
|
||||
@@ -161,8 +153,10 @@ export const plugin: PluginDefinition = {
|
||||
type: 'select',
|
||||
options: [],
|
||||
async dynamic(_ctx, args) {
|
||||
const { client, clientHash } = await op(args);
|
||||
if (client == null || clientHash == null) return { hidden: true };
|
||||
const res = await op(args);
|
||||
if ('error' in res) return { hidden: true };
|
||||
const clientHash = res.clientHash;
|
||||
const client = res.client;
|
||||
|
||||
const cacheKey = `${clientHash}:vaults`;
|
||||
const cachedVaults =
|
||||
@@ -170,11 +164,22 @@ export const plugin: PluginDefinition = {
|
||||
const vaults =
|
||||
cachedVaults ??
|
||||
setCache(cacheKey, await client.vaults.list({ decryptDetails: true }));
|
||||
|
||||
return {
|
||||
options: vaults.map((vault) => ({
|
||||
label: `${vault.title} (${vault.activeItemCount} Items)`,
|
||||
value: vault.id,
|
||||
})),
|
||||
options: vaults.map((vault) => {
|
||||
let title = vault.id;
|
||||
if ('title' in vault) {
|
||||
title = vault.title;
|
||||
} else if ('name' in vault) {
|
||||
// The SDK returns 'name' instead of 'title' but the bindings still use 'title'
|
||||
title = (vault as { name: string }).name;
|
||||
}
|
||||
|
||||
return {
|
||||
label: `${title} (${vault.activeItemCount} Items)`,
|
||||
value: vault.id,
|
||||
};
|
||||
}),
|
||||
};
|
||||
},
|
||||
},
|
||||
@@ -184,8 +189,11 @@ export const plugin: PluginDefinition = {
|
||||
type: 'select',
|
||||
options: [],
|
||||
async dynamic(_ctx, args) {
|
||||
const { client, clientHash } = await op(args);
|
||||
if (client == null || clientHash == null) return { hidden: true };
|
||||
const res = await op(args);
|
||||
if ('error' in res) return { hidden: true };
|
||||
const clientHash = res.clientHash;
|
||||
const client = res.client;
|
||||
|
||||
const vaultId = args.values.vault;
|
||||
if (typeof vaultId !== 'string') return { hidden: true };
|
||||
|
||||
@@ -212,8 +220,11 @@ export const plugin: PluginDefinition = {
|
||||
type: 'select',
|
||||
options: [],
|
||||
async dynamic(_ctx, args) {
|
||||
const { client, clientHash } = await op(args);
|
||||
if (client == null || clientHash == null) return { hidden: true };
|
||||
const res = await op(args);
|
||||
if ('error' in res) return { hidden: true };
|
||||
const clientHash = res.clientHash;
|
||||
const client = res.client;
|
||||
|
||||
const vaultId = args.values.vault;
|
||||
const itemId = args.values.item;
|
||||
if (typeof vaultId !== 'string' || typeof itemId !== 'string') {
|
||||
@@ -239,12 +250,12 @@ export const plugin: PluginDefinition = {
|
||||
const vaultId = args.values.vault;
|
||||
const itemId = args.values.item;
|
||||
const fieldId = args.values.field;
|
||||
const { value, error } = await getValue(args, vaultId, itemId, fieldId);
|
||||
if (error) {
|
||||
throw error;
|
||||
const res = await getValue(args, vaultId, itemId, fieldId);
|
||||
if ('error' in res) {
|
||||
throw res.error;
|
||||
}
|
||||
|
||||
return value ?? '';
|
||||
return res.value;
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user