fix: delete legacy connect keys and ensure description

This commit is contained in:
Eli Bosley
2025-06-25 16:19:09 -04:00
parent b7c2407840
commit 22fe91cd56
2 changed files with 51 additions and 13 deletions

View File

@@ -12,6 +12,8 @@ import { ConnectConfigService } from './connect-config.service.js';
export class ConnectApiKeyService implements ApiKeyService {
private readonly logger = new Logger(ConnectApiKeyService.name);
private static readonly validRoles: Set<Role> = new Set(Object.values(Role));
private static readonly CONNECT_API_KEY_NAME = 'Connect';
private static readonly CONNECT_API_KEY_DESCRIPTION = 'Internal API Key Used By Unraid Connect to access your server resources for the connect.myunraid.net dashboard';
constructor(
@Inject(API_KEY_SERVICE_TOKEN)
@@ -72,11 +74,13 @@ export class ConnectApiKeyService implements ApiKeyService {
public async createLocalConnectApiKey(): Promise<ApiKeyWithSecret | null> {
try {
return await this.create({
name: 'Connect',
name: ConnectApiKeyService.CONNECT_API_KEY_NAME,
description: 'API key for Connect user',
roles: [Role.CONNECT],
overwrite: true,
});
// Delete all other API keys with the role CONNECT
} catch (err) {
this.logger.error(`Failed to create local API key for Connect user: ${err}`);
return null;
@@ -87,21 +91,57 @@ export class ConnectApiKeyService implements ApiKeyService {
* Gets or creates a local API key for Connect
*/
public async getOrCreateLocalApiKey(): Promise<string> {
// 1. Check in-memory config
const targetDescription = ConnectApiKeyService.CONNECT_API_KEY_DESCRIPTION;
// 1. Get all API keys first
const allKeys = await this.findAll();
// 2. Check in-memory config and verify key exists
const { localApiKey: localApiKeyFromConfig } = this.connectConfig.getConfig();
if (localApiKeyFromConfig && localApiKeyFromConfig !== '') {
return localApiKeyFromConfig;
const keyExists = allKeys.some(key => {
const keyWithSecret = this.findByIdWithSecret(key.id);
return keyWithSecret?.key === localApiKeyFromConfig;
});
if (keyExists) {
return localApiKeyFromConfig;
}
}
// 2. Check disk
const localApiKeyFromDisk = this.apiKeyService.findByField('name', 'Connect');
if (localApiKeyFromDisk) {
return localApiKeyFromDisk.key;
// 3. Filter by name "Connect"
const connectKeys = allKeys.filter(key => key.name === ConnectApiKeyService.CONNECT_API_KEY_NAME);
// 4. Find keys with correct description vs incorrect description
const correctKeys = connectKeys.filter(key => key.description === targetDescription);
const incorrectKeys = connectKeys.filter(key => key.description !== targetDescription);
// 5. Delete keys with incorrect description
if (incorrectKeys.length > 0) {
const idsToDelete = incorrectKeys.map(key => key.id);
await this.deleteApiKeys(idsToDelete);
this.logger.log(`Deleted ${incorrectKeys.length} Connect API keys with incorrect descriptions`);
}
// 3. If no key found, create one
const localApiKey = await this.createLocalConnectApiKey();
// 6. If we have a correct key, return it
if (correctKeys.length > 0) {
const correctKeyWithSecret = this.findByIdWithSecret(correctKeys[0].id);
if (correctKeyWithSecret) {
return correctKeyWithSecret.key;
}
}
// 7. Create a new key with the correct description
const localApiKey = await this.create({
name: ConnectApiKeyService.CONNECT_API_KEY_NAME,
description: targetDescription,
roles: [Role.CONNECT],
overwrite: true,
});
if (!localApiKey?.key) {
throw new Error('Failed to create local API key');
}
return localApiKey.key;
}
}

View File

@@ -180,11 +180,9 @@ async function upsertKey() {
const apiKeyResult = res?.data?.apiKey;
if (isEdit && apiKeyResult && 'update' in apiKeyResult) {
const fragmentData = useFragment(API_KEY_FRAGMENT_WITH_KEY, apiKeyResult.update);
console.log('fragmentData', fragmentData);
apiKeyStore.setCreatedKey(fragmentData);
} else if (!isEdit && apiKeyResult && 'create' in apiKeyResult) {
const fragmentData = useFragment(API_KEY_FRAGMENT_WITH_KEY, apiKeyResult.create);
console.log('fragmentData', fragmentData);
apiKeyStore.setCreatedKey(fragmentData);
}
@@ -262,7 +260,7 @@ async function upsertKey() {
areAllPermissionsSelected() ? clearAllPermissions() : selectAllPermissions()
"
>
{{ areAllPermissionsSelected() ? 'Select None' : 'Select All' }}
{{ areAllPermissionsSelected() ? 'Clear All' : 'Select All' }}
</Button>
</div>
<div class="flex flex-col gap-2 mt-1">
@@ -283,7 +281,7 @@ async function upsertKey() {
: selectAllActions(perm.resource)
"
>
{{ areAllActionsSelected(perm.resource) ? 'Select None' : 'Select All' }}
{{ areAllActionsSelected(perm.resource) ? 'Clear All' : 'Select All' }}
</Button>
</div>
<div class="flex gap-4 flex-wrap">