@breadstone/archipel-mcp 0.0.40 → 0.0.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/guides/blob-storage.md +20 -16
- package/data/guides/implementing-ports.md +2 -0
- package/data/guides/payments-and-feature-gating.md +76 -45
- package/data/packages/platform-blob-storage/api/Class.AwsS3BlobStorageStrategy.md +197 -0
- package/data/packages/platform-blob-storage/api/Class.AzureBlobStorageStrategy.md +197 -0
- package/data/packages/platform-blob-storage/api/Class.BlobModule.md +20 -10
- package/data/packages/platform-blob-storage/api/Class.BlobService.md +8 -8
- package/data/packages/platform-blob-storage/api/Class.BlobStorageStrategyBase.md +158 -0
- package/data/packages/platform-blob-storage/api/Class.LocalBlobStorageStrategy.md +197 -0
- package/data/packages/platform-blob-storage/api/Class.VercelBlobStorageStrategy.md +198 -0
- package/data/packages/platform-blob-storage/api/Interface.IBlobDeleteRequest.md +4 -4
- package/data/packages/platform-blob-storage/api/Interface.IBlobDownloadRequest.md +5 -5
- package/data/packages/platform-blob-storage/api/Interface.IBlobDownloadResult.md +6 -6
- package/data/packages/platform-blob-storage/api/Interface.IBlobModuleOptions.md +20 -10
- package/data/packages/platform-blob-storage/api/Interface.IBlobObjectMetadata.md +14 -14
- package/data/packages/platform-blob-storage/api/Interface.IBlobSignedUrlRequest.md +5 -5
- package/data/packages/platform-blob-storage/api/Interface.IBlobUploadRequest.md +10 -10
- package/data/packages/platform-blob-storage/api/Interface.IBlobUploadResult.md +3 -3
- package/data/packages/platform-blob-storage/api/TypeAlias.BlobDownloadResponseType.md +1 -1
- package/data/packages/platform-blob-storage/api/TypeAlias.BlobUploadBody.md +1 -1
- package/data/packages/platform-blob-storage/api/Variable.AWS_S3_ACCESS_KEY_ID.md +14 -0
- package/data/packages/platform-blob-storage/api/Variable.AWS_S3_BUCKET.md +14 -0
- package/data/packages/platform-blob-storage/api/Variable.AWS_S3_CONFIG_ENTRIES.md +14 -0
- package/data/packages/platform-blob-storage/api/Variable.AWS_S3_ENDPOINT.md +14 -0
- package/data/packages/platform-blob-storage/api/Variable.AWS_S3_REGION.md +14 -0
- package/data/packages/platform-blob-storage/api/Variable.AWS_S3_SECRET_ACCESS_KEY.md +14 -0
- package/data/packages/platform-blob-storage/api/Variable.AZURE_BLOB_CONNECTION_STRING.md +14 -0
- package/data/packages/platform-blob-storage/api/Variable.AZURE_BLOB_CONTAINER_NAME.md +14 -0
- package/data/packages/platform-blob-storage/api/Variable.AZURE_CONFIG_ENTRIES.md +14 -0
- package/data/packages/platform-blob-storage/api/{Variable.BLOB_PROVIDER.md → Variable.BLOB_STORAGE_STRATEGY_TOKEN.md} +3 -3
- package/data/packages/platform-blob-storage/api/Variable.BlobDownloadResponseTypes.md +5 -5
- package/data/packages/platform-blob-storage/api/Variable.LOCAL_BLOB_BASE_PATH.md +14 -0
- package/data/packages/platform-blob-storage/api/Variable.LOCAL_BLOB_BUCKET.md +14 -0
- package/data/packages/platform-blob-storage/api/Variable.LOCAL_CONFIG_ENTRIES.md +14 -0
- package/data/packages/platform-blob-storage/api/Variable.PLATFORM_BLOB_STORAGE_CONFIG_ENTRIES.md +14 -0
- package/data/packages/platform-blob-storage/api/Variable.VERCEL_BLOB_BUCKET.md +14 -0
- package/data/packages/platform-blob-storage/api/Variable.VERCEL_BLOB_PUBLIC_URL.md +14 -0
- package/data/packages/platform-blob-storage/api/Variable.VERCEL_BLOB_READ_WRITE_TOKEN.md +14 -0
- package/data/packages/platform-blob-storage/api/Variable.VERCEL_BLOB_REGION.md +14 -0
- package/data/packages/platform-blob-storage/api/Variable.VERCEL_BLOB_URL.md +14 -0
- package/data/packages/platform-blob-storage/api/Variable.VERCEL_CONFIG_ENTRIES.md +14 -0
- package/data/packages/platform-blob-storage/api/index.md +26 -18
- package/data/packages/platform-blob-storage/index.md +104 -68
- package/data/packages/platform-bootstrap/api/index.md +0 -2
- package/data/packages/platform-core/api/Class.ProblemDetailResponse.md +8 -8
- package/data/packages/platform-database/api/Class.DatabaseHealthIndicator.md +4 -4
- package/data/packages/platform-database/api/Class.DatabaseModule.md +3 -3
- package/data/packages/platform-database/api/Class.DatabaseService.md +12 -11
- package/data/packages/platform-database/api/Class.PrismaService.md +6 -5
- package/data/packages/platform-database/api/Class.RepositoryBase.md +23 -23
- package/data/packages/platform-database/api/Class.RepositoryBase2.md +7 -7
- package/data/packages/platform-database/api/Class.RepositoryExceptionFilter.md +2 -2
- package/data/packages/platform-database/api/Function.aggregate.md +1 -1
- package/data/packages/platform-database/api/Function.aggregateRaw.md +1 -1
- package/data/packages/platform-database/api/Function.count.md +1 -1
- package/data/packages/platform-database/api/Function.create.md +1 -1
- package/data/packages/platform-database/api/Function.createMany.md +1 -1
- package/data/packages/platform-database/api/Function.createManyAndReturn.md +1 -1
- package/data/packages/platform-database/api/Function.cursorPaginator.md +1 -1
- package/data/packages/platform-database/api/Function.findFirst.md +1 -1
- package/data/packages/platform-database/api/Function.findFirstOrThrow.md +1 -1
- package/data/packages/platform-database/api/Function.findMany.md +1 -1
- package/data/packages/platform-database/api/Function.findManyPaged.md +1 -1
- package/data/packages/platform-database/api/Function.findRaw.md +1 -1
- package/data/packages/platform-database/api/Function.findUnique.md +1 -1
- package/data/packages/platform-database/api/Function.findUniqueOrThrow.md +1 -1
- package/data/packages/platform-database/api/Function.groupBy.md +1 -1
- package/data/packages/platform-database/api/Function.pageable.md +1 -1
- package/data/packages/platform-database/api/Function.paginator.md +1 -1
- package/data/packages/platform-database/api/Function.pipe.md +1 -1
- package/data/packages/platform-database/api/Function.pipeAll.md +1 -1
- package/data/packages/platform-database/api/Function.query.md +1 -1
- package/data/packages/platform-database/api/Function.query2.md +1 -1
- package/data/packages/platform-database/api/Function.remove.md +1 -1
- package/data/packages/platform-database/api/Function.removeMany.md +1 -1
- package/data/packages/platform-database/api/Function.skip.md +1 -1
- package/data/packages/platform-database/api/Function.transactionalQuery.md +1 -1
- package/data/packages/platform-database/api/Function.update.md +1 -1
- package/data/packages/platform-database/api/Function.updateMany.md +1 -1
- package/data/packages/platform-database/api/Function.updateManyAndReturn.md +1 -1
- package/data/packages/platform-database/api/Function.upsert.md +1 -1
- package/data/packages/platform-database/api/Interface.ICursorPaginateOptions.md +4 -4
- package/data/packages/platform-database/api/Interface.ICursorPaginatedResult.md +3 -3
- package/data/packages/platform-database/api/Interface.IDatabaseModuleConfig.md +3 -3
- package/data/packages/platform-database/api/Interface.IPaginateOptions.md +3 -3
- package/data/packages/platform-database/api/Interface.IPaginatedResult.md +3 -3
- package/data/packages/platform-database/api/Interface.IPrismaServiceOptions.md +2 -2
- package/data/packages/platform-database/api/Interface.IRepositoryQuery.md +3 -3
- package/data/packages/platform-database/api/Interface.IRepositoryQuery2.md +3 -3
- package/data/packages/platform-database/api/Interface.ITransactionalRepositoryQuery.md +4 -4
- package/data/packages/platform-database/api/TypeAlias.CursorPaginateFunction.md +1 -1
- package/data/packages/platform-database/api/TypeAlias.DelegateArgs.md +1 -1
- package/data/packages/platform-database/api/TypeAlias.DelegateOperations.md +1 -1
- package/data/packages/platform-database/api/TypeAlias.DelegateReturnTypes.md +1 -1
- package/data/packages/platform-database/api/TypeAlias.PaginateFunction.md +1 -1
- package/data/packages/platform-database/api/TypeAlias.QueryResultType.md +1 -1
- package/data/packages/platform-database/api/TypeAlias.RepositoryOperationsAll.md +1 -1
- package/data/packages/platform-database/api/TypeAlias.RepositoryOperationsBase.md +1 -1
- package/data/packages/platform-database/api/TypeAlias.TransactionalQueryResultType.md +1 -1
- package/data/packages/platform-database/api/Variable.DATABASE_MODULE_CONFIG.md +1 -1
- package/data/packages/platform-database/api/Variable.DB_AUTO_CONNECTION.md +1 -1
- package/data/packages/platform-database/api/Variable.PLATFORM_DATABASE_CONFIG_ENTRIES.md +1 -1
- package/data/packages/platform-payments/api/Class.FeatureAccessPort.md +13 -5
- package/data/packages/platform-payments/api/Class.FeatureGuard.md +3 -3
- package/data/packages/platform-payments/api/Class.FeatureUsageInterceptor.md +3 -3
- package/data/packages/platform-payments/api/Function.RequiresFeature.md +1 -1
- package/data/packages/platform-payments/api/Interface.IFeatureAccessContext.md +46 -0
- package/data/packages/platform-payments/api/Variable.FEATURE_KEY_METADATA.md +1 -1
- package/data/packages/platform-payments/api/index.md +1 -0
- package/data/packages/platform-payments/index.md +33 -12
- package/data/patterns/guard-pattern.md +27 -6
- package/data/patterns/module-pattern.md +6 -2
- package/package.json +1 -1
- package/data/packages/platform-blob-storage/api/Class.AwsS3BlobProvider.md +0 -196
- package/data/packages/platform-blob-storage/api/Class.AzureBlobProvider.md +0 -196
- package/data/packages/platform-blob-storage/api/Class.VercelBlobProvider.md +0 -197
- package/data/packages/platform-blob-storage/api/Interface.IAwsS3BlobProviderRegistration.md +0 -28
- package/data/packages/platform-blob-storage/api/Interface.IAwsS3ProviderOptions.md +0 -68
- package/data/packages/platform-blob-storage/api/Interface.IAzureBlobProviderOptions.md +0 -48
- package/data/packages/platform-blob-storage/api/Interface.IAzureBlobProviderRegistration.md +0 -28
- package/data/packages/platform-blob-storage/api/Interface.IBlobProvider.md +0 -114
- package/data/packages/platform-blob-storage/api/Interface.ICustomBlobProviderRegistration.md +0 -28
- package/data/packages/platform-blob-storage/api/Interface.IVercelBlobProviderOptions.md +0 -80
- package/data/packages/platform-blob-storage/api/Interface.IVercelBlobProviderRegistration.md +0 -28
- package/data/packages/platform-blob-storage/api/TypeAlias.IBlobProviderRegistration.md +0 -16
- package/data/packages/platform-blob-storage/api/Variable.AWS_S3_PROVIDER_OPTIONS.md +0 -12
- package/data/packages/platform-blob-storage/api/Variable.AZURE_BLOB_PROVIDER_OPTIONS.md +0 -12
- package/data/packages/platform-blob-storage/api/Variable.BlobProviderKinds.md +0 -26
- package/data/packages/platform-blob-storage/api/Variable.VERCEL_BLOB_PROVIDER_OPTIONS.md +0 -12
- package/data/packages/platform-bootstrap/api/Function.useExpressServer.md +0 -32
- package/data/packages/platform-bootstrap/api/Interface.IExpressServerStepOptions.md +0 -46
|
@@ -18,16 +18,17 @@ yarn add @breadstone/archipel-platform-blob-storage
|
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
|
21
|
-
## Choose a
|
|
21
|
+
## Choose a Strategy
|
|
22
22
|
|
|
23
|
-
`platform-blob-storage` abstracts file operations behind a unified `BlobService`. You select the
|
|
23
|
+
`platform-blob-storage` abstracts file operations behind a unified `BlobService`. You select the storage strategy at module registration time. Built-in strategies:
|
|
24
24
|
|
|
25
|
-
|
|
|
26
|
-
|
|
|
27
|
-
| **Vercel**
|
|
28
|
-
| **Azure**
|
|
29
|
-
| **AWS S3**
|
|
30
|
-
| **
|
|
25
|
+
| Strategy | Import subpath | When to use |
|
|
26
|
+
| ----------------------------- | --------------------- | ------------------------------------------- |
|
|
27
|
+
| **Vercel** | `strategies/vercel` | Vercel-hosted apps with Vercel Blob storage |
|
|
28
|
+
| **Azure** | `strategies/azure` | Azure Blob Storage accounts |
|
|
29
|
+
| **AWS S3** | `strategies/aws-s3` | Amazon S3 or S3-compatible providers |
|
|
30
|
+
| **Local** | `strategies/local` | Local filesystem (development / testing) |
|
|
31
|
+
| **Custom** | — | Extend `BlobStorageStrategyBase` |
|
|
31
32
|
|
|
32
33
|
---
|
|
33
34
|
|
|
@@ -35,14 +36,14 @@ yarn add @breadstone/archipel-platform-blob-storage
|
|
|
35
36
|
|
|
36
37
|
```typescript
|
|
37
38
|
import { Module } from '@nestjs/common';
|
|
38
|
-
import { BlobModule
|
|
39
|
+
import { BlobModule } from '@breadstone/archipel-platform-blob-storage';
|
|
40
|
+
import { AwsS3BlobStorageStrategy, AWS_S3_CONFIG_ENTRIES } from '@breadstone/archipel-platform-blob-storage/strategies/aws-s3';
|
|
39
41
|
|
|
40
42
|
@Module({
|
|
41
43
|
imports: [
|
|
42
|
-
BlobModule.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
},
|
|
44
|
+
BlobModule.register({
|
|
45
|
+
storageStrategy: AwsS3BlobStorageStrategy,
|
|
46
|
+
configEntries: AWS_S3_CONFIG_ENTRIES,
|
|
46
47
|
}),
|
|
47
48
|
],
|
|
48
49
|
})
|
|
@@ -234,8 +235,11 @@ export class PrismaBlobVariantAdapter extends BlobVariantPersistencePort {
|
|
|
234
235
|
### Register with Persistence
|
|
235
236
|
|
|
236
237
|
```typescript
|
|
237
|
-
|
|
238
|
-
|
|
238
|
+
import { AwsS3BlobStorageStrategy, AWS_S3_CONFIG_ENTRIES } from '@breadstone/archipel-platform-blob-storage/strategies/aws-s3';
|
|
239
|
+
|
|
240
|
+
BlobModule.register({
|
|
241
|
+
storageStrategy: AwsS3BlobStorageStrategy,
|
|
242
|
+
configEntries: AWS_S3_CONFIG_ENTRIES,
|
|
239
243
|
objectPersistence: PrismaBlobObjectAdapter,
|
|
240
244
|
variantPersistence: PrismaBlobVariantAdapter,
|
|
241
245
|
}),
|
|
@@ -245,7 +249,7 @@ BlobModule.forRoot({
|
|
|
245
249
|
|
|
246
250
|
## Security
|
|
247
251
|
|
|
248
|
-
All built-in blob
|
|
252
|
+
All built-in blob storage strategies (AWS S3, Azure, Vercel, Local) apply automatic key normalization via the internal `normalizeKey` method before any storage operation. This includes:
|
|
249
253
|
|
|
250
254
|
- **Path traversal prevention** — segments such as `..` and `.` are removed so callers cannot escape the intended directory.
|
|
251
255
|
- **Backslash normalization** — backslashes (`\`) are converted to forward slashes before processing.
|
|
@@ -304,6 +304,8 @@ When omitted, blob uploads and deletes work normally, but metadata isn't tracked
|
|
|
304
304
|
|
|
305
305
|
When omitted, `FeatureGuard` and `FeatureUsageInterceptor` have no access port and will deny or skip feature checks. When provided, they delegate quota enforcement and usage recording to your adapter.
|
|
306
306
|
|
|
307
|
+
`FeatureGuard` calls `checkAccess(userId, featureKey, context)`, while `FeatureUsageInterceptor` calls `recordUsage(userId, featureKey, context)` after a successful request. The `IFeatureAccessContext` contains the resolved `userId`, optional `tenantId`, and original authenticated subject, so adapters can enforce tenant-scoped plans without controller-specific billing logic.
|
|
308
|
+
|
|
307
309
|
---
|
|
308
310
|
|
|
309
311
|
## Common Mistakes
|
|
@@ -36,16 +36,15 @@ yarn add @breadstone/archipel-platform-payments
|
|
|
36
36
|
```typescript
|
|
37
37
|
import { Module } from '@nestjs/common';
|
|
38
38
|
import { PaymentModule } from '@breadstone/archipel-platform-payments';
|
|
39
|
-
import {
|
|
39
|
+
import { StripeClient, STRIPE_CONFIG_ENTRIES } from '@breadstone/archipel-platform-payments/stripe';
|
|
40
40
|
import { PrismaFeatureAccessAdapter } from './adapters/PrismaFeatureAccessAdapter';
|
|
41
|
-
import { STRIPE_CONFIG_ENTRIES } from './env';
|
|
42
41
|
|
|
43
42
|
@Module({
|
|
44
43
|
imports: [
|
|
45
44
|
PaymentModule.register({
|
|
46
|
-
paymentClient:
|
|
45
|
+
paymentClient: StripeClient,
|
|
47
46
|
featureAccess: PrismaFeatureAccessAdapter,
|
|
48
|
-
configEntries:
|
|
47
|
+
configEntries: STRIPE_CONFIG_ENTRIES,
|
|
49
48
|
}),
|
|
50
49
|
],
|
|
51
50
|
})
|
|
@@ -54,9 +53,9 @@ export class AppModule {}
|
|
|
54
53
|
|
|
55
54
|
---
|
|
56
55
|
|
|
57
|
-
##
|
|
56
|
+
## Using the Payment Client Port
|
|
58
57
|
|
|
59
|
-
The
|
|
58
|
+
The built-in provider clients implement `PaymentClientPort`. Inject the port in application services so checkout and webhook code stays provider-agnostic:
|
|
60
59
|
|
|
61
60
|
```typescript
|
|
62
61
|
import { Injectable } from '@nestjs/common';
|
|
@@ -64,23 +63,27 @@ import {
|
|
|
64
63
|
PaymentClientPort,
|
|
65
64
|
type INormalizedCheckoutSession,
|
|
66
65
|
type INormalizedSubscription,
|
|
67
|
-
type INormalizedWebhookEvent,
|
|
68
66
|
} from '@breadstone/archipel-platform-payments';
|
|
69
67
|
|
|
70
68
|
@Injectable()
|
|
71
|
-
export class
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
69
|
+
export class CheckoutService {
|
|
70
|
+
private readonly _paymentClient: PaymentClientPort;
|
|
71
|
+
|
|
72
|
+
public constructor(paymentClient: PaymentClientPort) {
|
|
73
|
+
this._paymentClient = paymentClient;
|
|
75
74
|
}
|
|
76
75
|
|
|
77
|
-
public async
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
public async createCheckout(priceId: string, userId: string): Promise<INormalizedCheckoutSession> {
|
|
77
|
+
return this._paymentClient.createCheckoutSession(
|
|
78
|
+
priceId,
|
|
79
|
+
'https://app.example.com/billing/success',
|
|
80
|
+
'https://app.example.com/billing/cancel',
|
|
81
|
+
userId,
|
|
82
|
+
);
|
|
80
83
|
}
|
|
81
84
|
|
|
82
|
-
public async
|
|
83
|
-
|
|
85
|
+
public async getSubscription(subscriptionId: string): Promise<INormalizedSubscription | null> {
|
|
86
|
+
return this._paymentClient.fetchSubscription(subscriptionId);
|
|
84
87
|
}
|
|
85
88
|
}
|
|
86
89
|
```
|
|
@@ -97,7 +100,11 @@ The `FeatureAccessPort` lets you restrict functionality based on subscription ti
|
|
|
97
100
|
|
|
98
101
|
```typescript
|
|
99
102
|
import { Injectable } from '@nestjs/common';
|
|
100
|
-
import {
|
|
103
|
+
import {
|
|
104
|
+
FeatureAccessPort,
|
|
105
|
+
type IFeatureAccessContext,
|
|
106
|
+
type IFeatureAccessResult,
|
|
107
|
+
} from '@breadstone/archipel-platform-payments';
|
|
101
108
|
import { PrismaService } from '@breadstone/archipel-platform-database';
|
|
102
109
|
|
|
103
110
|
@Injectable()
|
|
@@ -109,53 +116,77 @@ export class PrismaFeatureAccessAdapter extends FeatureAccessPort {
|
|
|
109
116
|
this._prisma = prisma;
|
|
110
117
|
}
|
|
111
118
|
|
|
112
|
-
public async
|
|
119
|
+
public async checkAccess(
|
|
120
|
+
userId: string,
|
|
121
|
+
featureKey: string,
|
|
122
|
+
context?: IFeatureAccessContext,
|
|
123
|
+
): Promise<IFeatureAccessResult> {
|
|
113
124
|
const subscription = await this._prisma.subscription.findFirst({
|
|
114
|
-
where: { userId, status: 'active' },
|
|
115
|
-
include: { plan: true },
|
|
125
|
+
where: { userId, tenantId: context?.tenantId, status: 'active' },
|
|
126
|
+
include: { plan: { include: { features: true } } },
|
|
116
127
|
});
|
|
117
128
|
|
|
118
|
-
|
|
119
|
-
|
|
129
|
+
const feature = subscription?.plan.features.find((planFeature) => planFeature.key === featureKey);
|
|
130
|
+
if (!feature) {
|
|
131
|
+
return { allowed: false, used: 0, limit: 0, remaining: 0, resetAt: new Date() };
|
|
120
132
|
}
|
|
121
133
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
134
|
+
const used = await this._prisma.featureUsage.count({
|
|
135
|
+
where: {
|
|
136
|
+
userId,
|
|
137
|
+
tenantId: context?.tenantId,
|
|
138
|
+
featureKey,
|
|
139
|
+
usedAt: { gte: subscription.currentPeriodStart },
|
|
140
|
+
},
|
|
128
141
|
});
|
|
142
|
+
|
|
143
|
+
const limit = feature.limit;
|
|
144
|
+
const remaining = limit === -1 ? -1 : Math.max(0, limit - used);
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
allowed: limit === -1 || used < limit,
|
|
148
|
+
used,
|
|
149
|
+
limit,
|
|
150
|
+
remaining,
|
|
151
|
+
resetAt: subscription.currentPeriodEnd,
|
|
152
|
+
};
|
|
129
153
|
}
|
|
130
154
|
|
|
131
|
-
public async
|
|
132
|
-
|
|
133
|
-
|
|
155
|
+
public async recordUsage(userId: string, featureKey: string, context?: IFeatureAccessContext): Promise<void> {
|
|
156
|
+
const access = await this.checkAccess(userId, featureKey, context);
|
|
157
|
+
if (!access.allowed) {
|
|
158
|
+
throw new Error(`Feature quota exceeded for ${featureKey}`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
await this._prisma.featureUsage.create({
|
|
162
|
+
data: { userId, tenantId: context?.tenantId, featureKey, usedAt: new Date() },
|
|
134
163
|
});
|
|
135
164
|
}
|
|
136
165
|
}
|
|
137
166
|
```
|
|
138
167
|
|
|
168
|
+
`FeatureGuard` and `FeatureUsageInterceptor` pass `IFeatureAccessContext` into this adapter. The context includes the resolved `userId`, an optional `tenantId` from `request.user.tenantId`, and the original authenticated subject.
|
|
169
|
+
|
|
139
170
|
### Step 2: Use the Feature Guard
|
|
140
171
|
|
|
141
|
-
|
|
172
|
+
Use `@RequiresFeature()` to mark endpoints and `FeatureGuard` to enforce access checks:
|
|
142
173
|
|
|
143
174
|
```typescript
|
|
144
175
|
import { Controller, Post, UseGuards } from '@nestjs/common';
|
|
145
176
|
import { JwtAuthGuard } from '@breadstone/archipel-platform-authentication';
|
|
146
|
-
import { FeatureGuard } from '@breadstone/archipel-platform-payments';
|
|
177
|
+
import { FeatureGuard, RequiresFeature } from '@breadstone/archipel-platform-payments';
|
|
147
178
|
|
|
148
179
|
@Controller('exports')
|
|
149
|
-
@UseGuards(JwtAuthGuard)
|
|
180
|
+
@UseGuards(JwtAuthGuard, FeatureGuard)
|
|
150
181
|
export class ExportController {
|
|
151
182
|
@Post('pdf')
|
|
152
|
-
@
|
|
183
|
+
@RequiresFeature('export:pdf')
|
|
153
184
|
public async exportPdf(): Promise<void> {
|
|
154
185
|
// Only accessible to users with 'export:pdf' feature
|
|
155
186
|
}
|
|
156
187
|
|
|
157
188
|
@Post('csv')
|
|
158
|
-
@
|
|
189
|
+
@RequiresFeature('export:csv')
|
|
159
190
|
public async exportCsv(): Promise<void> {
|
|
160
191
|
// Only accessible to users with 'export:csv' feature
|
|
161
192
|
}
|
|
@@ -169,14 +200,14 @@ export class ExportController {
|
|
|
169
200
|
```typescript
|
|
170
201
|
import { Controller, Post, UseGuards, UseInterceptors } from '@nestjs/common';
|
|
171
202
|
import { JwtAuthGuard } from '@breadstone/archipel-platform-authentication';
|
|
172
|
-
import { FeatureGuard, FeatureUsageInterceptor } from '@breadstone/archipel-platform-payments';
|
|
203
|
+
import { FeatureGuard, FeatureUsageInterceptor, RequiresFeature } from '@breadstone/archipel-platform-payments';
|
|
173
204
|
|
|
174
205
|
@Controller('ai')
|
|
175
|
-
@UseGuards(JwtAuthGuard)
|
|
206
|
+
@UseGuards(JwtAuthGuard, FeatureGuard)
|
|
207
|
+
@UseInterceptors(FeatureUsageInterceptor)
|
|
176
208
|
export class AiController {
|
|
177
209
|
@Post('generate')
|
|
178
|
-
@
|
|
179
|
-
@UseInterceptors(FeatureUsageInterceptor)
|
|
210
|
+
@RequiresFeature('ai:generate')
|
|
180
211
|
public async generate(): Promise<string> {
|
|
181
212
|
// Usage is recorded automatically after success
|
|
182
213
|
return 'generated content';
|
|
@@ -208,17 +239,16 @@ export class PaymentWebhookController {
|
|
|
208
239
|
@Req() request: RawBodyRequest<Request>,
|
|
209
240
|
@Headers('stripe-signature') signature: string,
|
|
210
241
|
): Promise<void> {
|
|
211
|
-
const event = await this._paymentClient.
|
|
242
|
+
const event = await this._paymentClient.constructWebhookEvent(request.rawBody!, signature);
|
|
212
243
|
|
|
213
244
|
switch (event.type) {
|
|
214
|
-
case 'subscription.created':
|
|
215
245
|
case 'subscription.updated':
|
|
216
246
|
// Update subscription in your database
|
|
217
247
|
break;
|
|
218
248
|
case 'subscription.deleted':
|
|
219
249
|
// Handle cancellation
|
|
220
250
|
break;
|
|
221
|
-
case '
|
|
251
|
+
case 'invoice.payment_failed':
|
|
222
252
|
// Notify user, retry logic
|
|
223
253
|
break;
|
|
224
254
|
}
|
|
@@ -234,7 +264,8 @@ export class PaymentWebhookController {
|
|
|
234
264
|
2. **Verify webhook signatures.** Always validate the webhook payload before processing.
|
|
235
265
|
3. **Use normalized models.** Don't leak provider-specific types into your business logic.
|
|
236
266
|
4. **Implement idempotent webhook handlers.** Webhooks can be delivered multiple times.
|
|
237
|
-
5. **
|
|
267
|
+
5. **Use access context for tenancy.** Read `context.tenantId` instead of coupling controllers to billing tables.
|
|
268
|
+
6. **Separate feature gating from billing logic.** The `FeatureAccessPort` should check access and record metered usage, not manage subscriptions.
|
|
238
269
|
|
|
239
270
|
## Error Handling
|
|
240
271
|
|
|
@@ -258,7 +289,7 @@ All payment clients throw `PaymentError` — a domain error that wraps the under
|
|
|
258
289
|
import { PaymentError } from '@breadstone/archipel-platform-payments';
|
|
259
290
|
|
|
260
291
|
try {
|
|
261
|
-
await this._paymentClient.createCheckoutSession(
|
|
292
|
+
await this._paymentClient.createCheckoutSession(priceId, successUrl, cancelUrl, userId);
|
|
262
293
|
} catch (error) {
|
|
263
294
|
if (error instanceof PaymentError) {
|
|
264
295
|
// error.provider → 'stripe', 'paddle', 'mollie', 'lemonsqueezy'
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: 'Class: AwsS3BlobStorageStrategy'
|
|
3
|
+
generated: true
|
|
4
|
+
editUrl: false
|
|
5
|
+
---
|
|
6
|
+
# Class: AwsS3BlobStorageStrategy
|
|
7
|
+
|
|
8
|
+
Defined in: [strategies/aws-s3/AwsS3BlobStorageStrategy.ts:26](https://github.com/RueDeRennes/archipel/blob/main/libs/platform-blob-storage/src/strategies/aws-s3/AwsS3BlobStorageStrategy.ts#L26)
|
|
9
|
+
|
|
10
|
+
Provides a blob storage strategy backed by Amazon S3.
|
|
11
|
+
|
|
12
|
+
## Extends
|
|
13
|
+
|
|
14
|
+
- [`BlobStorageStrategyBase`](Class.BlobStorageStrategyBase)
|
|
15
|
+
|
|
16
|
+
## Constructors
|
|
17
|
+
|
|
18
|
+
### Constructor
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
new AwsS3BlobStorageStrategy(configService): AwsS3BlobStorageStrategy;
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Defined in: [strategies/aws-s3/AwsS3BlobStorageStrategy.ts:47](https://github.com/RueDeRennes/archipel/blob/main/libs/platform-blob-storage/src/strategies/aws-s3/AwsS3BlobStorageStrategy.ts#L47)
|
|
25
|
+
|
|
26
|
+
Initializes a new instance of the AwsS3BlobStorageStrategy class.
|
|
27
|
+
|
|
28
|
+
#### Parameters
|
|
29
|
+
|
|
30
|
+
| Parameter | Type | Description |
|
|
31
|
+
| ------ | ------ | ------ |
|
|
32
|
+
| `configService` | `ConfigService` | The configuration service providing environment values. |
|
|
33
|
+
|
|
34
|
+
#### Returns
|
|
35
|
+
|
|
36
|
+
`AwsS3BlobStorageStrategy`
|
|
37
|
+
|
|
38
|
+
#### Overrides
|
|
39
|
+
|
|
40
|
+
[`BlobStorageStrategyBase`](Class.BlobStorageStrategyBase).[`constructor`](Class.BlobStorageStrategyBase#constructor)
|
|
41
|
+
|
|
42
|
+
## Properties
|
|
43
|
+
|
|
44
|
+
### providerId
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
readonly providerId: string = 'aws-s3';
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Defined in: [strategies/aws-s3/AwsS3BlobStorageStrategy.ts:77](https://github.com/RueDeRennes/archipel/blob/main/libs/platform-blob-storage/src/strategies/aws-s3/AwsS3BlobStorageStrategy.ts#L77)
|
|
51
|
+
|
|
52
|
+
Gets the unique identifier for this storage provider.
|
|
53
|
+
|
|
54
|
+
#### Overrides
|
|
55
|
+
|
|
56
|
+
[`BlobStorageStrategyBase`](Class.BlobStorageStrategyBase).[`providerId`](Class.BlobStorageStrategyBase#providerid)
|
|
57
|
+
|
|
58
|
+
## Accessors
|
|
59
|
+
|
|
60
|
+
### defaultBucket
|
|
61
|
+
|
|
62
|
+
#### Get Signature
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
get defaultBucket(): string;
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Defined in: [strategies/aws-s3/AwsS3BlobStorageStrategy.ts:80](https://github.com/RueDeRennes/archipel/blob/main/libs/platform-blob-storage/src/strategies/aws-s3/AwsS3BlobStorageStrategy.ts#L80)
|
|
69
|
+
|
|
70
|
+
Gets the default bucket (container/directory) configured for the provider.
|
|
71
|
+
|
|
72
|
+
##### Returns
|
|
73
|
+
|
|
74
|
+
`string`
|
|
75
|
+
|
|
76
|
+
Gets the default bucket (container/directory) configured for the provider.
|
|
77
|
+
|
|
78
|
+
#### Overrides
|
|
79
|
+
|
|
80
|
+
[`BlobStorageStrategyBase`](Class.BlobStorageStrategyBase).[`defaultBucket`](Class.BlobStorageStrategyBase#defaultbucket)
|
|
81
|
+
|
|
82
|
+
## Methods
|
|
83
|
+
|
|
84
|
+
### createSignedUrl()
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
createSignedUrl(request): Promise<string>;
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Defined in: [strategies/aws-s3/AwsS3BlobStorageStrategy.ts:177](https://github.com/RueDeRennes/archipel/blob/main/libs/platform-blob-storage/src/strategies/aws-s3/AwsS3BlobStorageStrategy.ts#L177)
|
|
91
|
+
|
|
92
|
+
Creates a signed URL for the specified blob object.
|
|
93
|
+
Not all providers support this operation.
|
|
94
|
+
|
|
95
|
+
#### Parameters
|
|
96
|
+
|
|
97
|
+
| Parameter | Type | Description |
|
|
98
|
+
| ------ | ------ | ------ |
|
|
99
|
+
| `request` | [`IBlobSignedUrlRequest`](Interface.IBlobSignedUrlRequest) | The request describing the object to link. |
|
|
100
|
+
|
|
101
|
+
#### Returns
|
|
102
|
+
|
|
103
|
+
`Promise`\<`string`\>
|
|
104
|
+
|
|
105
|
+
The generated signed URL.
|
|
106
|
+
|
|
107
|
+
#### Overrides
|
|
108
|
+
|
|
109
|
+
[`BlobStorageStrategyBase`](Class.BlobStorageStrategyBase).[`createSignedUrl`](Class.BlobStorageStrategyBase#createsignedurl)
|
|
110
|
+
|
|
111
|
+
***
|
|
112
|
+
|
|
113
|
+
### deleteObject()
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
deleteObject(request): Promise<void>;
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Defined in: [strategies/aws-s3/AwsS3BlobStorageStrategy.ts:159](https://github.com/RueDeRennes/archipel/blob/main/libs/platform-blob-storage/src/strategies/aws-s3/AwsS3BlobStorageStrategy.ts#L159)
|
|
120
|
+
|
|
121
|
+
Deletes a blob object from the storage backend.
|
|
122
|
+
|
|
123
|
+
#### Parameters
|
|
124
|
+
|
|
125
|
+
| Parameter | Type | Description |
|
|
126
|
+
| ------ | ------ | ------ |
|
|
127
|
+
| `request` | [`IBlobDeleteRequest`](Interface.IBlobDeleteRequest) | The delete request describing the object to remove. |
|
|
128
|
+
|
|
129
|
+
#### Returns
|
|
130
|
+
|
|
131
|
+
`Promise`\<`void`\>
|
|
132
|
+
|
|
133
|
+
#### Overrides
|
|
134
|
+
|
|
135
|
+
[`BlobStorageStrategyBase`](Class.BlobStorageStrategyBase).[`deleteObject`](Class.BlobStorageStrategyBase#deleteobject)
|
|
136
|
+
|
|
137
|
+
***
|
|
138
|
+
|
|
139
|
+
### downloadObject()
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
downloadObject<TData>(request): Promise<IBlobDownloadResult<TData>>;
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Defined in: [strategies/aws-s3/AwsS3BlobStorageStrategy.ts:134](https://github.com/RueDeRennes/archipel/blob/main/libs/platform-blob-storage/src/strategies/aws-s3/AwsS3BlobStorageStrategy.ts#L134)
|
|
146
|
+
|
|
147
|
+
Downloads a blob object from the storage backend.
|
|
148
|
+
|
|
149
|
+
#### Type Parameters
|
|
150
|
+
|
|
151
|
+
| Type Parameter |
|
|
152
|
+
| ------ |
|
|
153
|
+
| `TData` |
|
|
154
|
+
|
|
155
|
+
#### Parameters
|
|
156
|
+
|
|
157
|
+
| Parameter | Type | Description |
|
|
158
|
+
| ------ | ------ | ------ |
|
|
159
|
+
| `request` | [`IBlobDownloadRequest`](Interface.IBlobDownloadRequest) | The download request describing the object to download. |
|
|
160
|
+
|
|
161
|
+
#### Returns
|
|
162
|
+
|
|
163
|
+
`Promise`\<[`IBlobDownloadResult`](Interface.IBlobDownloadResult)\<`TData`\>\>
|
|
164
|
+
|
|
165
|
+
The download result including payload and metadata.
|
|
166
|
+
|
|
167
|
+
#### Overrides
|
|
168
|
+
|
|
169
|
+
[`BlobStorageStrategyBase`](Class.BlobStorageStrategyBase).[`downloadObject`](Class.BlobStorageStrategyBase#downloadobject)
|
|
170
|
+
|
|
171
|
+
***
|
|
172
|
+
|
|
173
|
+
### uploadObject()
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
uploadObject(request): Promise<IBlobUploadResult>;
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Defined in: [strategies/aws-s3/AwsS3BlobStorageStrategy.ts:89](https://github.com/RueDeRennes/archipel/blob/main/libs/platform-blob-storage/src/strategies/aws-s3/AwsS3BlobStorageStrategy.ts#L89)
|
|
180
|
+
|
|
181
|
+
Uploads a blob object to the storage backend.
|
|
182
|
+
|
|
183
|
+
#### Parameters
|
|
184
|
+
|
|
185
|
+
| Parameter | Type | Description |
|
|
186
|
+
| ------ | ------ | ------ |
|
|
187
|
+
| `request` | [`IBlobUploadRequest`](Interface.IBlobUploadRequest) | The upload request describing the object to upload. |
|
|
188
|
+
|
|
189
|
+
#### Returns
|
|
190
|
+
|
|
191
|
+
`Promise`\<[`IBlobUploadResult`](Interface.IBlobUploadResult)\>
|
|
192
|
+
|
|
193
|
+
The resulting upload metadata.
|
|
194
|
+
|
|
195
|
+
#### Overrides
|
|
196
|
+
|
|
197
|
+
[`BlobStorageStrategyBase`](Class.BlobStorageStrategyBase).[`uploadObject`](Class.BlobStorageStrategyBase#uploadobject)
|