From db930bbab938c6a1c4dbbfea6afb9bb1001155db Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Tue, 27 Oct 2026 07:37:32 +0100 Subject: [PATCH] Task/deprecate access endpoints in favor of accesses endpoints (#8316) * Deprecate access endpoints in favor of accesses endpoints * Update changelog --- CHANGELOG.md | 6 + .../app/__snapshots__/app.module.spec.ts.snap | 35 +++ apps/api/src/app/app.module.ts | 4 +- .../accesses}/access.controller.ts | 4 +- .../endpoints/accesses/accesses.controller.ts | 250 ++++++++++++++++++ .../app/endpoints/accesses/accesses.module.ts | 13 + .../src/app/endpoints/public/public.module.ts | 2 +- .../app/endpoints/public/public.service.ts | 2 +- .../api/src/app/portfolio/portfolio.module.ts | 2 +- .../{app => services}/access/access.module.ts | 5 +- .../access/access.service.ts | 0 .../impersonation/impersonation.module.ts | 2 +- .../impersonation.service.spec.ts | 2 +- .../impersonation/impersonation.service.ts | 2 +- .../access-dialog-host.component.ts | 2 +- .../user-account-access.component.ts | 2 +- libs/common/src/lib/interfaces/index.ts | 4 + .../responses/access-response.interface.ts | 3 + .../responses/accesses-response.interface.ts | 5 + libs/ui/src/lib/services/data.service.ts | 14 +- 20 files changed, 338 insertions(+), 21 deletions(-) rename apps/api/src/app/{access => endpoints/accesses}/access.controller.ts (98%) create mode 100644 apps/api/src/app/endpoints/accesses/accesses.controller.ts create mode 100644 apps/api/src/app/endpoints/accesses/accesses.module.ts rename apps/api/src/{app => services}/access/access.module.ts (53%) rename apps/api/src/{app => services}/access/access.service.ts (100%) create mode 100644 libs/common/src/lib/interfaces/responses/access-response.interface.ts create mode 100644 libs/common/src/lib/interfaces/responses/accesses-response.interface.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 835df19a77..d520269e0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Changed + +- Deprecated the `api/v1/access` endpoints in favor of the `api/v1/accesses` endpoints + ## 3.93.0 - 2026-10-25 ### Changed diff --git a/apps/api/src/app/__snapshots__/app.module.spec.ts.snap b/apps/api/src/app/__snapshots__/app.module.spec.ts.snap index 927ecc7ebc..13afcc9f2c 100644 --- a/apps/api/src/app/__snapshots__/app.module.spec.ts.snap +++ b/apps/api/src/app/__snapshots__/app.module.spec.ts.snap @@ -11,6 +11,15 @@ exports[`AppModule should keep the security of each route 1`] = ` "interceptors": [], "permission": "deleteAccess", }, + "DELETE /accesses/:id": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "deleteAccess", + }, "DELETE /account-balance/:id": { "guards": [ "AuthGuard('jwt')", @@ -203,6 +212,14 @@ exports[`AppModule should keep the security of each route 1`] = ` ], "interceptors": [], }, + "GET /accesses": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + }, "GET /account": { "guards": [ "AuthGuard('jwt')", @@ -802,6 +819,15 @@ exports[`AppModule should keep the security of each route 1`] = ` "interceptors": [], "permission": "createAccess", }, + "POST /accesses": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "createAccess", + }, "POST /account": { "guards": [ "AuthGuard('jwt')", @@ -1133,6 +1159,15 @@ exports[`AppModule should keep the security of each route 1`] = ` "interceptors": [], "permission": "updateAccess", }, + "PUT /accesses/:id": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "updateAccess", + }, "PUT /account/:id": { "guards": [ "AuthGuard('jwt')", diff --git a/apps/api/src/app/app.module.ts b/apps/api/src/app/app.module.ts index 1a6ffd372e..2bcbbdaf4a 100644 --- a/apps/api/src/app/app.module.ts +++ b/apps/api/src/app/app.module.ts @@ -35,11 +35,11 @@ import { ThrottlerModule } from '@nestjs/throttler'; import { getReasonPhrase, StatusCodes } from 'http-status-codes'; import { join } from 'node:path'; -import { AccessModule } from './access/access.module'; import { AccountModule } from './account/account.module'; import { ActivitiesModule } from './activities/activities.module'; import { AdminModule } from './admin/admin.module'; import { AppController } from './app.controller'; +import { AccessesModule } from './endpoints/accesses/accesses.module'; import { AiModule } from './endpoints/ai/ai.module'; import { ApiKeysModule } from './endpoints/api-keys/api-keys.module'; import { AssetProfilesModule } from './endpoints/asset-profiles/asset-profiles.module'; @@ -71,7 +71,7 @@ import { UserModule } from './user/user.module'; controllers: [AppController], imports: [ AdminModule, - AccessModule, + AccessesModule, AccountModule, ActivitiesModule, AiModule, diff --git a/apps/api/src/app/access/access.controller.ts b/apps/api/src/app/endpoints/accesses/access.controller.ts similarity index 98% rename from apps/api/src/app/access/access.controller.ts rename to apps/api/src/app/endpoints/accesses/access.controller.ts index f68b25d61c..a4f053fc48 100644 --- a/apps/api/src/app/access/access.controller.ts +++ b/apps/api/src/app/endpoints/accesses/access.controller.ts @@ -1,6 +1,7 @@ import { AllowDuringImpersonation } from '@ghostfolio/api/decorators/allow-during-impersonation.decorator'; import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator'; import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; +import { AccessService } from '@ghostfolio/api/services/access/access.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { CreateAccessDto, UpdateAccessDto } from '@ghostfolio/common/dtos'; import { SubscriptionType } from '@ghostfolio/common/enums'; @@ -30,8 +31,7 @@ import { AuthGuard } from '@nestjs/passport'; import { Access as AccessModel } from '@prisma/client'; import { StatusCodes, getReasonPhrase } from 'http-status-codes'; -import { AccessService } from './access.service'; - +/** @deprecated */ @AllowDuringImpersonation() @Controller('access') export class AccessController { diff --git a/apps/api/src/app/endpoints/accesses/accesses.controller.ts b/apps/api/src/app/endpoints/accesses/accesses.controller.ts new file mode 100644 index 0000000000..818c13c7cb --- /dev/null +++ b/apps/api/src/app/endpoints/accesses/accesses.controller.ts @@ -0,0 +1,250 @@ +import { AllowDuringImpersonation } from '@ghostfolio/api/decorators/allow-during-impersonation.decorator'; +import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator'; +import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; +import { AccessService } from '@ghostfolio/api/services/access/access.service'; +import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; +import { CreateAccessDto, UpdateAccessDto } from '@ghostfolio/common/dtos'; +import { SubscriptionType } from '@ghostfolio/common/enums'; +import { + canApplyFiltersToAccess, + isValidGranteeOfAccess +} from '@ghostfolio/common/helper'; +import { + AccessResponse, + AccessSettings, + AccessesResponse +} from '@ghostfolio/common/interfaces'; +import { permissions } from '@ghostfolio/common/permissions'; +import { getScopesOfAccess } from '@ghostfolio/common/scopes'; +import type { RequestWithUser } from '@ghostfolio/common/types'; + +import { + Body, + Controller, + Delete, + Get, + HttpException, + Inject, + Param, + Post, + Put, + UseGuards +} from '@nestjs/common'; +import { REQUEST } from '@nestjs/core'; +import { AuthGuard } from '@nestjs/passport'; +import { Access as AccessModel } from '@prisma/client'; +import { StatusCodes, getReasonPhrase } from 'http-status-codes'; + +@AllowDuringImpersonation() +@Controller('accesses') +export class AccessesController { + public constructor( + private readonly accessService: AccessService, + private readonly configurationService: ConfigurationService, + @Inject(REQUEST) private readonly request: RequestWithUser + ) {} + + @HasPermission(permissions.createAccess) + @Post() + @UseGuards(AuthGuard('jwt'), HasPermissionGuard) + public async createAccess( + @Body() data: CreateAccessDto + ): Promise { + if ( + this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION') && + this.request.user.subscription?.type === SubscriptionType.Basic + ) { + throw new HttpException( + getReasonPhrase(StatusCodes.FORBIDDEN), + StatusCodes.FORBIDDEN + ); + } + + const type = data.type ?? (data.granteeUserId ? 'PRIVATE' : 'PUBLIC'); + + if ( + type === 'MCP' && + !this.configurationService.get('ENABLE_FEATURE_MCP') + ) { + // The client hides the type while the feature is disabled, hence an + // access of this type must not become a credential which is dormant + // until the feature is enabled + throw new HttpException( + getReasonPhrase(StatusCodes.BAD_REQUEST), + StatusCodes.BAD_REQUEST + ); + } + + if (!isValidGranteeOfAccess({ granteeUserId: data.granteeUserId, type })) { + throw new HttpException( + getReasonPhrase(StatusCodes.BAD_REQUEST), + StatusCodes.BAD_REQUEST + ); + } + + if (data.filters?.length && !canApplyFiltersToAccess({ type })) { + throw new HttpException( + getReasonPhrase(StatusCodes.BAD_REQUEST), + StatusCodes.BAD_REQUEST + ); + } + + try { + const access = await this.accessService.createAccess({ + type, + alias: data.alias || undefined, + expiresAt: new Date(data.expiresAt), + granteeUser: data.granteeUserId + ? { connect: { id: data.granteeUserId } } + : undefined, + scopes: getScopesOfAccess({ + type, + scopes: data.scopes + }), + settings: this.accessService.buildSettings(data.filters), + user: { connect: { id: this.request.user.id } } + }); + + return this.getAccessResponse(access); + } catch { + throw new HttpException( + getReasonPhrase(StatusCodes.BAD_REQUEST), + StatusCodes.BAD_REQUEST + ); + } + } + + @Delete(':id') + @HasPermission(permissions.deleteAccess) + @UseGuards(AuthGuard('jwt'), HasPermissionGuard) + public async deleteAccess(@Param('id') id: string): Promise { + const originalAccess = await this.accessService.access({ + id, + OR: [ + { granteeUserId: this.request.user.id }, + { userId: this.request.user.id } + ] + }); + + if (!originalAccess) { + throw new HttpException( + getReasonPhrase(StatusCodes.FORBIDDEN), + StatusCodes.FORBIDDEN + ); + } + + await this.accessService.deleteAccess({ + id + }); + } + + @Get() + @UseGuards(AuthGuard('jwt'), HasPermissionGuard) + public async getAllAccesses(): Promise { + const accesses = await this.accessService.accesses({ + orderBy: [{ granteeUserId: 'desc' }, { createdAt: 'asc' }], + where: { userId: this.request.user.id } + }); + + return { + accesses: accesses.map((access) => { + return this.getAccessResponse(access); + }) + }; + } + + @HasPermission(permissions.updateAccess) + @Put(':id') + @UseGuards(AuthGuard('jwt'), HasPermissionGuard) + public async updateAccess( + @Body() data: UpdateAccessDto, + @Param('id') id: string + ): Promise { + if ( + this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION') && + this.request.user.subscription?.type === SubscriptionType.Basic + ) { + throw new HttpException( + getReasonPhrase(StatusCodes.FORBIDDEN), + StatusCodes.FORBIDDEN + ); + } + + const originalAccess = await this.accessService.access({ + id, + userId: this.request.user.id + }); + + if (!originalAccess) { + throw new HttpException( + getReasonPhrase(StatusCodes.FORBIDDEN), + StatusCodes.FORBIDDEN + ); + } + + if ( + !isValidGranteeOfAccess({ + granteeUserId: data.granteeUserId, + type: originalAccess.type + }) + ) { + throw new HttpException( + getReasonPhrase(StatusCodes.BAD_REQUEST), + StatusCodes.BAD_REQUEST + ); + } + + if ( + data.filters?.length && + !canApplyFiltersToAccess({ type: originalAccess.type }) + ) { + throw new HttpException( + getReasonPhrase(StatusCodes.BAD_REQUEST), + StatusCodes.BAD_REQUEST + ); + } + + try { + const access = await this.accessService.updateAccess({ + data: { + alias: data.alias, + expiresAt: new Date(data.expiresAt), + granteeUser: data.granteeUserId + ? { connect: { id: data.granteeUserId } } + : { disconnect: true }, + scopes: getScopesOfAccess({ + scopes: data.scopes ?? originalAccess.scopes, + type: originalAccess.type + }), + settings: this.accessService.buildSettings(data.filters) + }, + where: { id } + }); + + return this.getAccessResponse(access); + } catch { + throw new HttpException( + getReasonPhrase(StatusCodes.BAD_REQUEST), + StatusCodes.BAD_REQUEST + ); + } + } + + private getAccessResponse(access: AccessModel): AccessResponse { + const { alias, expiresAt, granteeUserId, id, lastUsedAt, settings, type } = + access; + + const { filters } = (settings ?? {}) as AccessSettings; + + return { + alias, + expiresAt, + id, + lastUsedAt, + type, + grantee: granteeUserId ?? undefined, + scopes: getScopesOfAccess(access), + settings: canApplyFiltersToAccess({ type }) ? { filters } : {} + }; + } +} diff --git a/apps/api/src/app/endpoints/accesses/accesses.module.ts b/apps/api/src/app/endpoints/accesses/accesses.module.ts new file mode 100644 index 0000000000..7bc5cd65a2 --- /dev/null +++ b/apps/api/src/app/endpoints/accesses/accesses.module.ts @@ -0,0 +1,13 @@ +import { AccessModule } from '@ghostfolio/api/services/access/access.module'; +import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; + +import { Module } from '@nestjs/common'; + +import { AccessController } from './access.controller'; +import { AccessesController } from './accesses.controller'; + +@Module({ + controllers: [AccessController, AccessesController], + imports: [AccessModule, ConfigurationModule] +}) +export class AccessesModule {} diff --git a/apps/api/src/app/endpoints/public/public.module.ts b/apps/api/src/app/endpoints/public/public.module.ts index 4e4c4b7c00..34b94e378b 100644 --- a/apps/api/src/app/endpoints/public/public.module.ts +++ b/apps/api/src/app/endpoints/public/public.module.ts @@ -1,4 +1,3 @@ -import { AccessModule } from '@ghostfolio/api/app/access/access.module'; import { AccountBalanceService } from '@ghostfolio/api/app/account-balance/account-balance.service'; import { AccountService } from '@ghostfolio/api/app/account/account.service'; import { ActivitiesModule } from '@ghostfolio/api/app/activities/activities.module'; @@ -8,6 +7,7 @@ import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.servic import { RulesService } from '@ghostfolio/api/app/portfolio/rules.service'; import { UserModule } from '@ghostfolio/api/app/user/user.module'; import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.module'; +import { AccessModule } from '@ghostfolio/api/services/access/access.module'; import { BenchmarkModule } from '@ghostfolio/api/services/benchmark/benchmark.module'; import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module'; diff --git a/apps/api/src/app/endpoints/public/public.service.ts b/apps/api/src/app/endpoints/public/public.service.ts index c32df6d82d..051d380b26 100644 --- a/apps/api/src/app/endpoints/public/public.service.ts +++ b/apps/api/src/app/endpoints/public/public.service.ts @@ -1,7 +1,7 @@ -import { AccessService } from '@ghostfolio/api/app/access/access.service'; import { ActivitiesService } from '@ghostfolio/api/app/activities/activities.service'; import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.service'; import { UserService } from '@ghostfolio/api/app/user/user.service'; +import { AccessService } from '@ghostfolio/api/services/access/access.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { DEFAULT_CURRENCY } from '@ghostfolio/common/config'; diff --git a/apps/api/src/app/portfolio/portfolio.module.ts b/apps/api/src/app/portfolio/portfolio.module.ts index 82eeb5395a..f533c83158 100644 --- a/apps/api/src/app/portfolio/portfolio.module.ts +++ b/apps/api/src/app/portfolio/portfolio.module.ts @@ -1,4 +1,3 @@ -import { AccessModule } from '@ghostfolio/api/app/access/access.module'; import { AccountBalanceService } from '@ghostfolio/api/app/account-balance/account-balance.service'; import { AccountService } from '@ghostfolio/api/app/account/account.service'; import { ActivitiesModule } from '@ghostfolio/api/app/activities/activities.module'; @@ -7,6 +6,7 @@ import { PerformanceLoggingModule } from '@ghostfolio/api/interceptors/performan import { RedactValuesInResponseModule } from '@ghostfolio/api/interceptors/redact-values-in-response/redact-values-in-response.module'; import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.module'; import { TransformDataSourceInResponseModule } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.module'; +import { AccessModule } from '@ghostfolio/api/services/access/access.module'; import { ApiModule } from '@ghostfolio/api/services/api/api.module'; import { BenchmarkModule } from '@ghostfolio/api/services/benchmark/benchmark.module'; import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; diff --git a/apps/api/src/app/access/access.module.ts b/apps/api/src/services/access/access.module.ts similarity index 53% rename from apps/api/src/app/access/access.module.ts rename to apps/api/src/services/access/access.module.ts index 44d28a2300..6da94be11e 100644 --- a/apps/api/src/app/access/access.module.ts +++ b/apps/api/src/services/access/access.module.ts @@ -1,15 +1,12 @@ -import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; import { Module } from '@nestjs/common'; -import { AccessController } from './access.controller'; import { AccessService } from './access.service'; @Module({ - controllers: [AccessController], exports: [AccessService], - imports: [ConfigurationModule, PrismaModule], + imports: [PrismaModule], providers: [AccessService] }) export class AccessModule {} diff --git a/apps/api/src/app/access/access.service.ts b/apps/api/src/services/access/access.service.ts similarity index 100% rename from apps/api/src/app/access/access.service.ts rename to apps/api/src/services/access/access.service.ts diff --git a/apps/api/src/services/impersonation/impersonation.module.ts b/apps/api/src/services/impersonation/impersonation.module.ts index 7e4c5d9270..737ff99033 100644 --- a/apps/api/src/services/impersonation/impersonation.module.ts +++ b/apps/api/src/services/impersonation/impersonation.module.ts @@ -1,4 +1,4 @@ -import { AccessModule } from '@ghostfolio/api/app/access/access.module'; +import { AccessModule } from '@ghostfolio/api/services/access/access.module'; import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; import { ImpersonationService } from '@ghostfolio/api/services/impersonation/impersonation.service'; import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; diff --git a/apps/api/src/services/impersonation/impersonation.service.spec.ts b/apps/api/src/services/impersonation/impersonation.service.spec.ts index 7e90849414..f16b027018 100644 --- a/apps/api/src/services/impersonation/impersonation.service.spec.ts +++ b/apps/api/src/services/impersonation/impersonation.service.spec.ts @@ -1,4 +1,4 @@ -import { AccessService } from '@ghostfolio/api/app/access/access.service'; +import { AccessService } from '@ghostfolio/api/services/access/access.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { SubscriptionService } from '@ghostfolio/api/services/subscription/subscription.service'; diff --git a/apps/api/src/services/impersonation/impersonation.service.ts b/apps/api/src/services/impersonation/impersonation.service.ts index 546579c6ac..97c3295f02 100644 --- a/apps/api/src/services/impersonation/impersonation.service.ts +++ b/apps/api/src/services/impersonation/impersonation.service.ts @@ -1,4 +1,4 @@ -import { AccessService } from '@ghostfolio/api/app/access/access.service'; +import { AccessService } from '@ghostfolio/api/services/access/access.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { SubscriptionService } from '@ghostfolio/api/services/subscription/subscription.service'; diff --git a/apps/client/src/app/components/user-account-access/access-dialog-host/access-dialog-host.component.ts b/apps/client/src/app/components/user-account-access/access-dialog-host/access-dialog-host.component.ts index 72e92700d6..1adafb118b 100644 --- a/apps/client/src/app/components/user-account-access/access-dialog-host/access-dialog-host.component.ts +++ b/apps/client/src/app/components/user-account-access/access-dialog-host/access-dialog-host.component.ts @@ -135,7 +135,7 @@ export class GfAccessDialogHostComponent implements OnDestroy, OnInit { private fetchAccess(aAccessId: string) { return this.dataService.fetchAccesses().pipe( - map((accesses) => { + map(({ accesses }) => { return accesses.find(({ id }) => { return id === aAccessId; }); diff --git a/apps/client/src/app/components/user-account-access/user-account-access.component.ts b/apps/client/src/app/components/user-account-access/user-account-access.component.ts index 0dc34c0298..6004540b8b 100644 --- a/apps/client/src/app/components/user-account-access/user-account-access.component.ts +++ b/apps/client/src/app/components/user-account-access/user-account-access.component.ts @@ -216,7 +216,7 @@ export class GfUserAccountAccessComponent { this.dataService .fetchAccesses() .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe((accesses) => { + .subscribe(({ accesses }) => { this.accessesGive = accesses; this.changeDetectorRef.markForCheck(); diff --git a/libs/common/src/lib/interfaces/index.ts b/libs/common/src/lib/interfaces/index.ts index fba80e68cb..5459642a05 100644 --- a/libs/common/src/lib/interfaces/index.ts +++ b/libs/common/src/lib/interfaces/index.ts @@ -38,7 +38,9 @@ import type { PortfolioReportRule } from './portfolio-report-rule.interface'; import type { PortfolioSummary } from './portfolio-summary.interface'; import type { Product } from './product.interface'; import type { ReferralPartner } from './referral-partner.interface'; +import type { AccessResponse } from './responses/access-response.interface'; import type { AccessTokenResponse } from './responses/access-token-response.interface'; +import type { AccessesResponse } from './responses/accesses-response.interface'; import type { AccountBalancesResponse } from './responses/account-balances-response.interface'; import type { AccountResponse } from './responses/account-response.interface'; import type { AccountsResponse } from './responses/accounts-response.interface'; @@ -107,8 +109,10 @@ import type { XRayRulesSettings } from './x-ray-rules-settings.interface'; export { Access, + AccessResponse, AccessSettings, AccessTokenResponse, + AccessesResponse, AccountBalance, AccountBalancesResponse, AccountResponse, diff --git a/libs/common/src/lib/interfaces/responses/access-response.interface.ts b/libs/common/src/lib/interfaces/responses/access-response.interface.ts new file mode 100644 index 0000000000..f45d00b455 --- /dev/null +++ b/libs/common/src/lib/interfaces/responses/access-response.interface.ts @@ -0,0 +1,3 @@ +import type { Access } from '../access.interface'; + +export interface AccessResponse extends Access {} diff --git a/libs/common/src/lib/interfaces/responses/accesses-response.interface.ts b/libs/common/src/lib/interfaces/responses/accesses-response.interface.ts new file mode 100644 index 0000000000..13e685f79f --- /dev/null +++ b/libs/common/src/lib/interfaces/responses/accesses-response.interface.ts @@ -0,0 +1,5 @@ +import type { AccessResponse } from './access-response.interface'; + +export interface AccessesResponse { + accesses: AccessResponse[]; +} diff --git a/libs/ui/src/lib/services/data.service.ts b/libs/ui/src/lib/services/data.service.ts index c69e0cf0e6..972523b750 100644 --- a/libs/ui/src/lib/services/data.service.ts +++ b/libs/ui/src/lib/services/data.service.ts @@ -21,8 +21,9 @@ import { } from '@ghostfolio/common/dtos'; import { DATE_FORMAT } from '@ghostfolio/common/helper'; import { - Access, + AccessResponse, AccessTokenResponse, + AccessesResponse, AccountBalancesResponse, AccountResponse, AccountsResponse, @@ -310,7 +311,7 @@ export class DataService { } public deleteAccess(aId: string) { - return this.http.delete(`/api/v1/access/${aId}`); + return this.http.delete(`/api/v1/accesses/${aId}`); } public deleteAccount(aId: string) { @@ -376,7 +377,7 @@ export class DataService { } public fetchAccesses() { - return this.http.get('/api/v1/access'); + return this.http.get('/api/v1/accesses'); } public fetchAsset({ @@ -862,7 +863,7 @@ export class DataService { } public postAccess(aAccess: CreateAccessDto) { - return this.http.post('/api/v1/access', aAccess); + return this.http.post('/api/v1/accesses', aAccess); } public postAccount(aAccount: CreateAccountDto) { @@ -926,7 +927,10 @@ export class DataService { } public putAccess(aAccess: UpdateAccessDto) { - return this.http.put(`/api/v1/access/${aAccess.id}`, aAccess); + return this.http.put( + `/api/v1/accesses/${aAccess.id}`, + aAccess + ); } public putAccount(aAccount: UpdateAccountDto) {