opennextjs-azure 0.1.1
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/LICENSE +21 -0
- package/README.md +194 -0
- package/dist/adapters/converters/azure-http.d.mts +22 -0
- package/dist/adapters/converters/azure-http.d.ts +22 -0
- package/dist/adapters/converters/azure-http.js +97 -0
- package/dist/adapters/wrappers/azure-functions.d.mts +10 -0
- package/dist/adapters/wrappers/azure-functions.d.ts +10 -0
- package/dist/adapters/wrappers/azure-functions.js +102 -0
- package/dist/cli/index.d.mts +2 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +67 -0
- package/dist/config/index.d.mts +3 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.js +68 -0
- package/dist/deploy.js +835 -0
- package/dist/index.d.mts +35 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +20 -0
- package/dist/infrastructure/main.bicep +241 -0
- package/dist/overrides/incrementalCache/azure-blob.d.mts +23 -0
- package/dist/overrides/incrementalCache/azure-blob.d.ts +23 -0
- package/dist/overrides/incrementalCache/azure-blob.js +89 -0
- package/dist/overrides/queue/azure-queue.d.mts +19 -0
- package/dist/overrides/queue/azure-queue.d.ts +19 -0
- package/dist/overrides/queue/azure-queue.js +39 -0
- package/dist/overrides/tagCache/azure-table.d.mts +26 -0
- package/dist/overrides/tagCache/azure-table.d.ts +26 -0
- package/dist/overrides/tagCache/azure-table.js +104 -0
- package/dist/shared/opennextjs-azure.d619537c.d.mts +61 -0
- package/dist/shared/opennextjs-azure.d619537c.d.ts +61 -0
- package/infrastructure/main.bicep +241 -0
- package/package.json +99 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export { A as AzureConfig, a as AzureDeploymentTarget, d as defineAzureConfig } from './shared/opennextjs-azure.d619537c.mjs';
|
|
2
|
+
export { default as azureBlobCache } from './overrides/incrementalCache/azure-blob.mjs';
|
|
3
|
+
export { default as azureTableTagCache } from './overrides/tagCache/azure-table.mjs';
|
|
4
|
+
export { default as azureQueueRevalidation } from './overrides/queue/azure-queue.mjs';
|
|
5
|
+
export { default as azureFunctionsWrapper } from './adapters/wrappers/azure-functions.mjs';
|
|
6
|
+
export { default as azureHttpConverter } from './adapters/converters/azure-http.mjs';
|
|
7
|
+
import '@opennextjs/aws/types/open-next.js';
|
|
8
|
+
import '@opennextjs/aws/types/overrides.js';
|
|
9
|
+
import '@azure/functions';
|
|
10
|
+
|
|
11
|
+
interface ScaffoldOptions {
|
|
12
|
+
typescript?: boolean;
|
|
13
|
+
tailwind?: boolean;
|
|
14
|
+
eslint?: boolean;
|
|
15
|
+
srcDir?: boolean;
|
|
16
|
+
appRouter?: boolean;
|
|
17
|
+
importAlias?: string;
|
|
18
|
+
packageManager?: "npm" | "yarn" | "pnpm" | "bun";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
declare function init(options?: ScaffoldOptions & {
|
|
22
|
+
scaffold?: boolean;
|
|
23
|
+
}): Promise<void>;
|
|
24
|
+
|
|
25
|
+
declare function build(configPath?: string): Promise<void>;
|
|
26
|
+
|
|
27
|
+
declare function deploy(options: {
|
|
28
|
+
appName?: string;
|
|
29
|
+
resourceGroup?: string;
|
|
30
|
+
location?: string;
|
|
31
|
+
environment?: "dev" | "staging" | "prod";
|
|
32
|
+
skipInfrastructure?: boolean;
|
|
33
|
+
}): Promise<void>;
|
|
34
|
+
|
|
35
|
+
export { build, deploy, init };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export { A as AzureConfig, a as AzureDeploymentTarget, d as defineAzureConfig } from './shared/opennextjs-azure.d619537c.js';
|
|
2
|
+
export { default as azureBlobCache } from './overrides/incrementalCache/azure-blob.js';
|
|
3
|
+
export { default as azureTableTagCache } from './overrides/tagCache/azure-table.js';
|
|
4
|
+
export { default as azureQueueRevalidation } from './overrides/queue/azure-queue.js';
|
|
5
|
+
export { default as azureFunctionsWrapper } from './adapters/wrappers/azure-functions.js';
|
|
6
|
+
export { default as azureHttpConverter } from './adapters/converters/azure-http.js';
|
|
7
|
+
import '@opennextjs/aws/types/open-next.js';
|
|
8
|
+
import '@opennextjs/aws/types/overrides.js';
|
|
9
|
+
import '@azure/functions';
|
|
10
|
+
|
|
11
|
+
interface ScaffoldOptions {
|
|
12
|
+
typescript?: boolean;
|
|
13
|
+
tailwind?: boolean;
|
|
14
|
+
eslint?: boolean;
|
|
15
|
+
srcDir?: boolean;
|
|
16
|
+
appRouter?: boolean;
|
|
17
|
+
importAlias?: string;
|
|
18
|
+
packageManager?: "npm" | "yarn" | "pnpm" | "bun";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
declare function init(options?: ScaffoldOptions & {
|
|
22
|
+
scaffold?: boolean;
|
|
23
|
+
}): Promise<void>;
|
|
24
|
+
|
|
25
|
+
declare function build(configPath?: string): Promise<void>;
|
|
26
|
+
|
|
27
|
+
declare function deploy(options: {
|
|
28
|
+
appName?: string;
|
|
29
|
+
resourceGroup?: string;
|
|
30
|
+
location?: string;
|
|
31
|
+
environment?: "dev" | "staging" | "prod";
|
|
32
|
+
skipInfrastructure?: boolean;
|
|
33
|
+
}): Promise<void>;
|
|
34
|
+
|
|
35
|
+
export { build, deploy, init };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export { defineAzureConfig } from './config/index.js';
|
|
2
|
+
export { b as build, d as deploy, i as init } from './deploy.js';
|
|
3
|
+
export { default as azureBlobCache } from './overrides/incrementalCache/azure-blob.js';
|
|
4
|
+
export { default as azureTableTagCache } from './overrides/tagCache/azure-table.js';
|
|
5
|
+
export { default as azureQueueRevalidation } from './overrides/queue/azure-queue.js';
|
|
6
|
+
export { default as azureFunctionsWrapper } from './adapters/wrappers/azure-functions.js';
|
|
7
|
+
export { default as azureHttpConverter } from './adapters/converters/azure-http.js';
|
|
8
|
+
import 'node:fs/promises';
|
|
9
|
+
import 'node:path';
|
|
10
|
+
import 'node:url';
|
|
11
|
+
import 'node:child_process';
|
|
12
|
+
import 'node:util';
|
|
13
|
+
import '@opennextjs/aws/build.js';
|
|
14
|
+
import 'node:fs';
|
|
15
|
+
import 'node:readline';
|
|
16
|
+
import '@azure/storage-blob';
|
|
17
|
+
import '@azure/data-tables';
|
|
18
|
+
import '@azure/storage-queue';
|
|
19
|
+
import 'node:stream';
|
|
20
|
+
import 'node:buffer';
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
// OpenNext Azure - Main Infrastructure Template
|
|
2
|
+
// This creates all resources needed to run a Next.js app on Azure
|
|
3
|
+
//
|
|
4
|
+
// DO NOT EDIT THIS FILE, IT IS AUTO-GENERATED.
|
|
5
|
+
|
|
6
|
+
@description('Name of the application (used as prefix for all resources)')
|
|
7
|
+
param appName string
|
|
8
|
+
|
|
9
|
+
@description('Location for all resources')
|
|
10
|
+
param location string = resourceGroup().location
|
|
11
|
+
|
|
12
|
+
@description('Environment (dev, staging, prod)')
|
|
13
|
+
@allowed(['dev', 'staging', 'prod'])
|
|
14
|
+
param environment string = 'dev'
|
|
15
|
+
|
|
16
|
+
@description('Node.js version for Functions')
|
|
17
|
+
@allowed(['20', '22'])
|
|
18
|
+
param nodeVersion string = '20'
|
|
19
|
+
|
|
20
|
+
@description('Enable Application Insights for monitoring and logging')
|
|
21
|
+
param enableApplicationInsights bool = false
|
|
22
|
+
|
|
23
|
+
// Variables
|
|
24
|
+
var uniqueSuffix = uniqueString(resourceGroup().id)
|
|
25
|
+
var sanitizedAppName = replace(toLower(appName), '-', '')
|
|
26
|
+
var maxAppNameLength = 24 - length(uniqueSuffix)
|
|
27
|
+
var truncatedAppName = length(sanitizedAppName) > maxAppNameLength
|
|
28
|
+
? substring(sanitizedAppName, 0, maxAppNameLength)
|
|
29
|
+
: sanitizedAppName
|
|
30
|
+
var storageAccountName = '${truncatedAppName}${uniqueSuffix}'
|
|
31
|
+
var functionAppName = '${appName}-func-${environment}'
|
|
32
|
+
var appServicePlanName = '${appName}-plan-${environment}'
|
|
33
|
+
var containerName = 'nextjs-cache'
|
|
34
|
+
var tableName = 'nextjstags'
|
|
35
|
+
var queueName = 'nextjsrevalidation'
|
|
36
|
+
var applicationInsightsName = '${appName}-insights-${environment}'
|
|
37
|
+
|
|
38
|
+
// Application Insights (optional)
|
|
39
|
+
resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = if (enableApplicationInsights) {
|
|
40
|
+
name: applicationInsightsName
|
|
41
|
+
location: location
|
|
42
|
+
kind: 'web'
|
|
43
|
+
properties: {
|
|
44
|
+
Application_Type: 'web'
|
|
45
|
+
Request_Source: 'rest'
|
|
46
|
+
RetentionInDays: environment == 'prod' ? 90 : 30
|
|
47
|
+
publicNetworkAccessForIngestion: 'Enabled'
|
|
48
|
+
publicNetworkAccessForQuery: 'Enabled'
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Storage Account (for cache, static assets, and function storage)
|
|
53
|
+
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
|
|
54
|
+
name: storageAccountName
|
|
55
|
+
location: location
|
|
56
|
+
sku: {
|
|
57
|
+
name: environment == 'prod' ? 'Standard_GRS' : 'Standard_LRS'
|
|
58
|
+
}
|
|
59
|
+
kind: 'StorageV2'
|
|
60
|
+
properties: {
|
|
61
|
+
minimumTlsVersion: 'TLS1_2'
|
|
62
|
+
supportsHttpsTrafficOnly: true
|
|
63
|
+
allowBlobPublicAccess: true
|
|
64
|
+
accessTier: 'Hot'
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Blob service for cache and static assets
|
|
68
|
+
resource blobService 'blobServices' = {
|
|
69
|
+
name: 'default'
|
|
70
|
+
properties: {
|
|
71
|
+
cors: {
|
|
72
|
+
corsRules: [
|
|
73
|
+
{
|
|
74
|
+
allowedOrigins: ['*']
|
|
75
|
+
allowedMethods: ['GET', 'HEAD']
|
|
76
|
+
maxAgeInSeconds: 3600
|
|
77
|
+
exposedHeaders: ['*']
|
|
78
|
+
allowedHeaders: ['*']
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Container for Next.js cache
|
|
85
|
+
resource cacheContainer 'containers' = {
|
|
86
|
+
name: containerName
|
|
87
|
+
properties: {
|
|
88
|
+
publicAccess: 'None'
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Container for static assets (public CDN access)
|
|
93
|
+
resource assetsContainer 'containers' = {
|
|
94
|
+
name: 'assets'
|
|
95
|
+
properties: {
|
|
96
|
+
publicAccess: 'Blob'
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Table service for tag cache
|
|
102
|
+
resource tableService 'tableServices' = {
|
|
103
|
+
name: 'default'
|
|
104
|
+
|
|
105
|
+
resource tagTable 'tables' = {
|
|
106
|
+
name: tableName
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Queue service for ISR revalidation (revalidateTag/revalidatePath)
|
|
111
|
+
resource queueService 'queueServices' = {
|
|
112
|
+
name: 'default'
|
|
113
|
+
|
|
114
|
+
resource revalidationQueue 'queues' = {
|
|
115
|
+
name: queueName
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// App Service Plan (Consumption or Premium based on environment)
|
|
121
|
+
resource appServicePlan 'Microsoft.Web/serverfarms@2023-01-01' = {
|
|
122
|
+
name: appServicePlanName
|
|
123
|
+
location: location
|
|
124
|
+
sku: {
|
|
125
|
+
name: environment == 'prod' ? 'EP1' : 'Y1' // EP1 = Premium, Y1 = Consumption
|
|
126
|
+
tier: environment == 'prod' ? 'ElasticPremium' : 'Dynamic'
|
|
127
|
+
}
|
|
128
|
+
kind: 'functionapp'
|
|
129
|
+
properties: {
|
|
130
|
+
reserved: true // Linux
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Function App
|
|
135
|
+
resource functionApp 'Microsoft.Web/sites@2023-01-01' = {
|
|
136
|
+
name: functionAppName
|
|
137
|
+
location: location
|
|
138
|
+
kind: 'functionapp,linux'
|
|
139
|
+
properties: {
|
|
140
|
+
serverFarmId: appServicePlan.id
|
|
141
|
+
siteConfig: {
|
|
142
|
+
linuxFxVersion: 'NODE|${nodeVersion}'
|
|
143
|
+
appSettings: concat([
|
|
144
|
+
{
|
|
145
|
+
name: 'AzureWebJobsStorage'
|
|
146
|
+
value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};AccountKey=${storageAccount.listKeys().keys[0].value};EndpointSuffix=core.windows.net'
|
|
147
|
+
}
|
|
148
|
+
{
|
|
149
|
+
name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING'
|
|
150
|
+
value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};AccountKey=${storageAccount.listKeys().keys[0].value};EndpointSuffix=core.windows.net'
|
|
151
|
+
}
|
|
152
|
+
{
|
|
153
|
+
name: 'WEBSITE_CONTENTSHARE'
|
|
154
|
+
value: toLower(functionAppName)
|
|
155
|
+
}
|
|
156
|
+
{
|
|
157
|
+
name: 'FUNCTIONS_EXTENSION_VERSION'
|
|
158
|
+
value: '~4'
|
|
159
|
+
}
|
|
160
|
+
{
|
|
161
|
+
name: 'FUNCTIONS_WORKER_RUNTIME'
|
|
162
|
+
value: 'node'
|
|
163
|
+
}
|
|
164
|
+
{
|
|
165
|
+
name: 'WEBSITE_NODE_DEFAULT_VERSION'
|
|
166
|
+
value: '~${nodeVersion}'
|
|
167
|
+
}
|
|
168
|
+
{
|
|
169
|
+
name: 'WEBSITE_RUN_FROM_PACKAGE'
|
|
170
|
+
value: '1'
|
|
171
|
+
}
|
|
172
|
+
{
|
|
173
|
+
name: 'AzureWebJobsDisableHomepage'
|
|
174
|
+
value: 'true'
|
|
175
|
+
}
|
|
176
|
+
// Next.js / OpenNext environment variables
|
|
177
|
+
{
|
|
178
|
+
name: 'AZURE_STORAGE_CONNECTION_STRING'
|
|
179
|
+
value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};AccountKey=${storageAccount.listKeys().keys[0].value};EndpointSuffix=core.windows.net'
|
|
180
|
+
}
|
|
181
|
+
{
|
|
182
|
+
name: 'AZURE_STORAGE_ACCOUNT_NAME'
|
|
183
|
+
value: storageAccountName
|
|
184
|
+
}
|
|
185
|
+
{
|
|
186
|
+
name: 'AZURE_STORAGE_CONTAINER_NAME'
|
|
187
|
+
value: containerName
|
|
188
|
+
}
|
|
189
|
+
{
|
|
190
|
+
name: 'AZURE_TABLE_NAME'
|
|
191
|
+
value: tableName
|
|
192
|
+
}
|
|
193
|
+
{
|
|
194
|
+
name: 'AZURE_QUEUE_NAME'
|
|
195
|
+
value: queueName
|
|
196
|
+
}
|
|
197
|
+
{
|
|
198
|
+
name: 'NODE_ENV'
|
|
199
|
+
value: 'production'
|
|
200
|
+
}
|
|
201
|
+
], enableApplicationInsights ? [
|
|
202
|
+
{
|
|
203
|
+
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
|
|
204
|
+
value: applicationInsights.properties.ConnectionString
|
|
205
|
+
}
|
|
206
|
+
{
|
|
207
|
+
name: 'ApplicationInsightsAgent_EXTENSION_VERSION'
|
|
208
|
+
value: '~3'
|
|
209
|
+
}
|
|
210
|
+
] : [])
|
|
211
|
+
|
|
212
|
+
ftpsState: 'Disabled'
|
|
213
|
+
minTlsVersion: '1.2'
|
|
214
|
+
cors: {
|
|
215
|
+
allowedOrigins: ['*']
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
httpsOnly: true
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Outputs
|
|
223
|
+
output functionAppName string = functionApp.name
|
|
224
|
+
output functionAppUrl string = 'https://${functionApp.properties.defaultHostName}'
|
|
225
|
+
output storageAccountName string = storageAccount.name
|
|
226
|
+
output assetsUrl string = 'https://${storageAccount.name}.blob.${az.environment().suffixes.storage}/assets'
|
|
227
|
+
output resourceGroupName string = resourceGroup().name
|
|
228
|
+
output applicationInsightsName string = enableApplicationInsights ? applicationInsights.name : ''
|
|
229
|
+
output applicationInsightsInstrumentationKey string = enableApplicationInsights ? applicationInsights.properties.InstrumentationKey : ''
|
|
230
|
+
|
|
231
|
+
output deploymentInfo object = {
|
|
232
|
+
functionApp: functionAppName
|
|
233
|
+
storageAccount: storageAccountName
|
|
234
|
+
containerName: containerName
|
|
235
|
+
tableName: tableName
|
|
236
|
+
queueName: queueName
|
|
237
|
+
assetsUrl: 'https://${storageAccount.name}.blob.${az.environment().suffixes.storage}/assets'
|
|
238
|
+
functionUrl: 'https://${functionApp.properties.defaultHostName}'
|
|
239
|
+
appUrl: 'https://${functionApp.properties.defaultHostName}'
|
|
240
|
+
applicationInsights: enableApplicationInsights ? applicationInsightsName : null
|
|
241
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { IncrementalCache, CacheEntryType, WithLastModified, CacheValue } from '@opennextjs/aws/types/overrides.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Azure Blob Storage implementation of IncrementalCache.
|
|
5
|
+
*
|
|
6
|
+
* Stores Next.js ISR cache entries in Azure Blob Storage.
|
|
7
|
+
* Compatible with the S3 cache interface from @opennextjs/aws.
|
|
8
|
+
*/
|
|
9
|
+
declare class AzureBlobIncrementalCache implements IncrementalCache {
|
|
10
|
+
name: string;
|
|
11
|
+
private containerClient;
|
|
12
|
+
constructor();
|
|
13
|
+
/**
|
|
14
|
+
* Builds the blob key path, mimicking S3 structure:
|
|
15
|
+
* [prefix]/[__fetch]/[buildId]/[key].[extension]
|
|
16
|
+
*/
|
|
17
|
+
private buildBlobKey;
|
|
18
|
+
get<CacheType extends CacheEntryType = "cache">(key: string, cacheType?: CacheType): Promise<WithLastModified<CacheValue<CacheType>> | null>;
|
|
19
|
+
set<CacheType extends CacheEntryType = "cache">(key: string, value: CacheValue<CacheType>, cacheType?: CacheType): Promise<void>;
|
|
20
|
+
delete(key: string): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export { AzureBlobIncrementalCache as default };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { IncrementalCache, CacheEntryType, WithLastModified, CacheValue } from '@opennextjs/aws/types/overrides.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Azure Blob Storage implementation of IncrementalCache.
|
|
5
|
+
*
|
|
6
|
+
* Stores Next.js ISR cache entries in Azure Blob Storage.
|
|
7
|
+
* Compatible with the S3 cache interface from @opennextjs/aws.
|
|
8
|
+
*/
|
|
9
|
+
declare class AzureBlobIncrementalCache implements IncrementalCache {
|
|
10
|
+
name: string;
|
|
11
|
+
private containerClient;
|
|
12
|
+
constructor();
|
|
13
|
+
/**
|
|
14
|
+
* Builds the blob key path, mimicking S3 structure:
|
|
15
|
+
* [prefix]/[__fetch]/[buildId]/[key].[extension]
|
|
16
|
+
*/
|
|
17
|
+
private buildBlobKey;
|
|
18
|
+
get<CacheType extends CacheEntryType = "cache">(key: string, cacheType?: CacheType): Promise<WithLastModified<CacheValue<CacheType>> | null>;
|
|
19
|
+
set<CacheType extends CacheEntryType = "cache">(key: string, value: CacheValue<CacheType>, cacheType?: CacheType): Promise<void>;
|
|
20
|
+
delete(key: string): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export { AzureBlobIncrementalCache as default };
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { BlobServiceClient } from '@azure/storage-blob';
|
|
2
|
+
import { getAzureConfig } from '../../config/index.js';
|
|
3
|
+
|
|
4
|
+
class AzureBlobIncrementalCache {
|
|
5
|
+
name = "azure-blob";
|
|
6
|
+
containerClient;
|
|
7
|
+
constructor() {
|
|
8
|
+
const { storage } = getAzureConfig();
|
|
9
|
+
const connectionString = storage.connectionString;
|
|
10
|
+
const accountName = storage.accountName;
|
|
11
|
+
const accountKey = storage.accountKey;
|
|
12
|
+
if (connectionString) {
|
|
13
|
+
const blobServiceClient = BlobServiceClient.fromConnectionString(connectionString);
|
|
14
|
+
this.containerClient = blobServiceClient.getContainerClient(storage.containerName || "nextjs-cache");
|
|
15
|
+
} else if (accountName && accountKey) {
|
|
16
|
+
const blobServiceClient = new BlobServiceClient(`https://${accountName}.blob.core.windows.net`, {
|
|
17
|
+
accountName,
|
|
18
|
+
accountKey
|
|
19
|
+
});
|
|
20
|
+
this.containerClient = blobServiceClient.getContainerClient(storage.containerName || "nextjs-cache");
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Builds the blob key path, mimicking S3 structure:
|
|
25
|
+
* [prefix]/[__fetch]/[buildId]/[key].[extension]
|
|
26
|
+
*/
|
|
27
|
+
buildBlobKey(key, cacheType = "cache") {
|
|
28
|
+
const { storage } = getAzureConfig();
|
|
29
|
+
const { NEXT_BUILD_ID } = process.env;
|
|
30
|
+
const prefix = storage.containerName || "";
|
|
31
|
+
const type = cacheType === "fetch" ? "__fetch" : "";
|
|
32
|
+
return [prefix, type, NEXT_BUILD_ID, cacheType === "fetch" ? key : `${key}.${cacheType}`].filter(Boolean).join("/");
|
|
33
|
+
}
|
|
34
|
+
async get(key, cacheType) {
|
|
35
|
+
try {
|
|
36
|
+
const blobKey = this.buildBlobKey(key, cacheType);
|
|
37
|
+
const blobClient = this.containerClient.getBlobClient(blobKey);
|
|
38
|
+
const downloadResponse = await blobClient.download();
|
|
39
|
+
if (!downloadResponse.readableStreamBody) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
const chunks = [];
|
|
43
|
+
for await (const chunk of downloadResponse.readableStreamBody) {
|
|
44
|
+
chunks.push(Buffer.from(chunk));
|
|
45
|
+
}
|
|
46
|
+
const content = Buffer.concat(chunks).toString("utf8");
|
|
47
|
+
const value = JSON.parse(content);
|
|
48
|
+
return {
|
|
49
|
+
value,
|
|
50
|
+
lastModified: downloadResponse.lastModified?.getTime()
|
|
51
|
+
};
|
|
52
|
+
} catch (error) {
|
|
53
|
+
if (error.statusCode === 404) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
process.stderr.write(`Failed to get from Azure Blob cache: ${error}
|
|
57
|
+
`);
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async set(key, value, cacheType) {
|
|
62
|
+
try {
|
|
63
|
+
const blobKey = this.buildBlobKey(key, cacheType);
|
|
64
|
+
const blobClient = this.containerClient.getBlockBlobClient(blobKey);
|
|
65
|
+
const content = JSON.stringify(value);
|
|
66
|
+
await blobClient.upload(content, content.length, {
|
|
67
|
+
blobHTTPHeaders: {
|
|
68
|
+
blobContentType: "application/json"
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
} catch (error) {
|
|
72
|
+
process.stderr.write(`Failed to set Azure Blob cache: ${error}
|
|
73
|
+
`);
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async delete(key) {
|
|
78
|
+
try {
|
|
79
|
+
const blobKey = this.buildBlobKey(key, "cache");
|
|
80
|
+
const blobClient = this.containerClient.getBlobClient(blobKey);
|
|
81
|
+
await blobClient.deleteIfExists();
|
|
82
|
+
} catch (error) {
|
|
83
|
+
process.stderr.write(`Failed to delete from Azure Blob cache: ${error}
|
|
84
|
+
`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export { AzureBlobIncrementalCache as default };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Queue, QueueMessage } from '@opennextjs/aws/types/overrides.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Azure Queue Storage implementation of the revalidation queue.
|
|
5
|
+
*
|
|
6
|
+
* Handles on-demand ISR revalidation triggered by:
|
|
7
|
+
* - revalidateTag('product')
|
|
8
|
+
* - revalidatePath('/products/123')
|
|
9
|
+
*
|
|
10
|
+
* The queue receives revalidation messages and triggers re-renders.
|
|
11
|
+
*/
|
|
12
|
+
declare class AzureQueueRevalidation implements Queue {
|
|
13
|
+
name: string;
|
|
14
|
+
private queueClient;
|
|
15
|
+
constructor();
|
|
16
|
+
send(message: QueueMessage): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export { AzureQueueRevalidation as default };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Queue, QueueMessage } from '@opennextjs/aws/types/overrides.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Azure Queue Storage implementation of the revalidation queue.
|
|
5
|
+
*
|
|
6
|
+
* Handles on-demand ISR revalidation triggered by:
|
|
7
|
+
* - revalidateTag('product')
|
|
8
|
+
* - revalidatePath('/products/123')
|
|
9
|
+
*
|
|
10
|
+
* The queue receives revalidation messages and triggers re-renders.
|
|
11
|
+
*/
|
|
12
|
+
declare class AzureQueueRevalidation implements Queue {
|
|
13
|
+
name: string;
|
|
14
|
+
private queueClient;
|
|
15
|
+
constructor();
|
|
16
|
+
send(message: QueueMessage): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export { AzureQueueRevalidation as default };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { QueueServiceClient } from '@azure/storage-queue';
|
|
2
|
+
import { getAzureConfig } from '../../config/index.js';
|
|
3
|
+
|
|
4
|
+
class AzureQueueRevalidation {
|
|
5
|
+
name = "azure-queue";
|
|
6
|
+
queueClient;
|
|
7
|
+
constructor() {
|
|
8
|
+
const { storage } = getAzureConfig();
|
|
9
|
+
const connectionString = storage.connectionString;
|
|
10
|
+
const queueName = storage.queueName || "nextjsrevalidation";
|
|
11
|
+
if (connectionString) {
|
|
12
|
+
const queueServiceClient = QueueServiceClient.fromConnectionString(connectionString);
|
|
13
|
+
this.queueClient = queueServiceClient.getQueueClient(queueName);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
async send(message) {
|
|
17
|
+
if (!this.queueClient) {
|
|
18
|
+
process.stderr.write("Azure Queue not configured. Skipping revalidation message.\n");
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const messageContent = JSON.stringify({
|
|
23
|
+
host: message.MessageBody.host,
|
|
24
|
+
url: message.MessageBody.url,
|
|
25
|
+
lastModified: message.MessageBody.lastModified,
|
|
26
|
+
eTag: message.MessageBody.eTag,
|
|
27
|
+
deduplicationId: message.MessageDeduplicationId,
|
|
28
|
+
groupId: message.MessageGroupId
|
|
29
|
+
});
|
|
30
|
+
await this.queueClient.sendMessage(Buffer.from(messageContent).toString("base64"));
|
|
31
|
+
} catch (error) {
|
|
32
|
+
process.stderr.write(`Failed to send revalidation message: ${error}
|
|
33
|
+
`);
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export { AzureQueueRevalidation as default };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { OriginalTagCache, OriginalTagCacheWriteInput } from '@opennextjs/aws/types/overrides.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Azure Table Storage implementation of TagCache.
|
|
5
|
+
*
|
|
6
|
+
* Stores tag-to-path mappings for Next.js revalidateTag/revalidatePath.
|
|
7
|
+
* Uses the "original" mode which requires pre-population but offers fast reads.
|
|
8
|
+
*
|
|
9
|
+
* Schema:
|
|
10
|
+
* - PartitionKey: tag (e.g., "buildId/product")
|
|
11
|
+
* - RowKey: path (e.g., "buildId/products/123")
|
|
12
|
+
* - revalidatedAt: timestamp
|
|
13
|
+
*/
|
|
14
|
+
declare class AzureTableTagCache implements OriginalTagCache {
|
|
15
|
+
mode: "original";
|
|
16
|
+
name: string;
|
|
17
|
+
private tableClient;
|
|
18
|
+
constructor();
|
|
19
|
+
private buildKey;
|
|
20
|
+
getByTag(tag: string): Promise<string[]>;
|
|
21
|
+
getByPath(path: string): Promise<string[]>;
|
|
22
|
+
getLastModified(path: string, lastModified?: number): Promise<number>;
|
|
23
|
+
writeTags(tags: OriginalTagCacheWriteInput[]): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { AzureTableTagCache as default };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { OriginalTagCache, OriginalTagCacheWriteInput } from '@opennextjs/aws/types/overrides.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Azure Table Storage implementation of TagCache.
|
|
5
|
+
*
|
|
6
|
+
* Stores tag-to-path mappings for Next.js revalidateTag/revalidatePath.
|
|
7
|
+
* Uses the "original" mode which requires pre-population but offers fast reads.
|
|
8
|
+
*
|
|
9
|
+
* Schema:
|
|
10
|
+
* - PartitionKey: tag (e.g., "buildId/product")
|
|
11
|
+
* - RowKey: path (e.g., "buildId/products/123")
|
|
12
|
+
* - revalidatedAt: timestamp
|
|
13
|
+
*/
|
|
14
|
+
declare class AzureTableTagCache implements OriginalTagCache {
|
|
15
|
+
mode: "original";
|
|
16
|
+
name: string;
|
|
17
|
+
private tableClient;
|
|
18
|
+
constructor();
|
|
19
|
+
private buildKey;
|
|
20
|
+
getByTag(tag: string): Promise<string[]>;
|
|
21
|
+
getByPath(path: string): Promise<string[]>;
|
|
22
|
+
getLastModified(path: string, lastModified?: number): Promise<number>;
|
|
23
|
+
writeTags(tags: OriginalTagCacheWriteInput[]): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { AzureTableTagCache as default };
|