Last active
September 19, 2024 06:35
-
-
Save ManotLuijiu/1ec5237965f9d50faa9659f8715e0e8d to your computer and use it in GitHub Desktop.
Migrate SST from V2 to V3 on Next.js app router project
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// aws-lambda/card-image.ts | |
import { APIGatewayProxyHandlerV2 } from 'aws-lambda'; | |
import { generateCardSvg } from '@/components/Dashboard/CardImage/GenerateCardSvg'; | |
export const handler: APIGatewayProxyHandlerV2 = async (event) => { | |
const { lastFourDigits, cardType } = event.queryStringParameters || {}; | |
if (!lastFourDigits || !cardType) { | |
return { | |
statusCode: 400, | |
body: JSON.stringify({ error: 'Missing required parameters' }), | |
}; | |
} | |
const svg = generateCardSvg({ lastFourDigits, cardType }); | |
return { | |
statusCode: 200, | |
headers: { | |
'Content-Type': 'image/svg+xml', | |
}, | |
body: svg, | |
}; | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// // @/components/Dashboard/CardImage/CardImage.tsx | |
interface CardImageProps { | |
lastFourDigits: string; | |
cardType: string; | |
} | |
export default function CardImage({ lastFourDigits, cardType }: CardImageProps) { | |
const apiUrl = process.env.NEXT_PUBLIC_API_URL; // Set this in your environment variables | |
const imageUrl = `${apiUrl}/api/card-image?lastFourDigits=${lastFourDigits}&cardType=${cardType}`; | |
return ( | |
<div className="w-[300px] h-[180px]"> | |
<img src={imageUrl} alt={`${cardType} card ending in ${lastFourDigits}`} className="w-full h-full object-cover rounded-lg" /> | |
</div> | |
); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @/components/Dashboard/CardImage/GenerateCardSvg.ts | |
interface CardDetails { | |
lastFourDigits: string; | |
cardType: string; | |
} | |
export function generateCardSvg({ lastFourDigits, cardType }: CardDetails): string { | |
const svg = ` | |
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="180" viewBox="0 0 300 180"> | |
<rect width="100%" height="100%" fill="#f0f0f0" rx="10" ry="10"/> | |
<text x="20" y="40" font-family="Arial, sans-serif" font-size="18" fill="#333333">${cardType}</text> | |
<text x="20" y="140" font-family="Arial, sans-serif" font-size="24" fill="#333333">**** **** **** ${lastFourDigits}</text> | |
</svg> | |
`; | |
return svg.trim(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Create folder aws-lambda inside src folder, | |
// If you do not use src folder just put in root directory | |
// If so do not forget to modify sst.config.ts change handler: 'src/aws-lambda/sender.handler', to handler: 'aws-lambda/sender.handler', | |
// src/aws-lambda/route.ts | |
import { APIGatewayProxyEventV2 } from 'aws-lambda'; | |
export const handler = async (event: APIGatewayProxyEventV2) => { | |
console.log('event', event); | |
return { | |
statusCode: 200, | |
body: JSON.stringify({ route: event.routeKey, status: 'ok' }, null, 2), | |
}; | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Create folder aws-lambda inside src folder, | |
// If you do not use src folder just put in root directory | |
// If so do not forget to modify sst.config.ts change handler: 'src/aws-lambda/sender.handler', to handler: 'aws-lambda/sender.handler', | |
// src/aws-lambda/sender.ts | |
import { SendEmailCommand, SESv2Client } from '@aws-sdk/client-sesv2'; | |
import { Resource } from 'sst'; | |
const client = new SESv2Client({}); | |
export const handler = async () => { | |
await client.send( | |
new SendEmailCommand({ | |
FromEmailAddress: Resource.YourAppEmail.sender, | |
Destination: { | |
ToAddresses: ['[email protected]'], | |
}, | |
Content: { | |
Simple: { | |
Subject: { | |
Data: 'Test AWS SES send by AWS Lambda', | |
}, | |
Body: { | |
Text: { | |
Data: 'Send from my SST app', | |
}, | |
}, | |
}, | |
}, | |
}), | |
); | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <reference path="./.sst/platform/config.d.ts" /> | |
export default $config({ | |
app(input) { | |
return { | |
name: 'your-app-name', | |
removal: input?.stage === 'production' ? 'retain' : 'remove', | |
home: 'aws', | |
providers: { | |
aws: { | |
region: 'ap-southeast-1', | |
}, | |
}, | |
}; | |
}, | |
async run() { | |
const email = new sst.aws.Email('YourAppEmail', { | |
sender: $app.stage === 'production' ? '[email protected]' : `${$app.stage}@your-domain.com`, | |
}); | |
const emailApi = new sst.aws.Function('YourAppEmailApi', { | |
handler: 'src/aws-lambda/sender.handler', | |
link: [email], | |
url: true, | |
}); | |
const api = new sst.aws.ApiGatewayV2('YourAppApi', { | |
domain: { | |
name: $app.stage === 'production' ? 'api.your-domain.com' : `${$app.stage}.your-domain.com`, | |
path: 'v1', | |
}, | |
}); | |
api.route('GET /', { | |
handler: 'src/aws-lambda/route.handler', | |
}); | |
api.route('GET /foo', 'src/aws-lambda/route.handler', { auth: { iam: true } }); | |
// Generate dynamic image | |
api.route('GET /card-image', { | |
handler: 'src/aws-lambda/card-image.handler', | |
}); | |
const bucket = new sst.aws.Bucket('YourAppBucket', { | |
cors: { | |
allowHeaders: ['Authorization', 'Content-Type'], | |
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'HEAD'], | |
allowOrigins: ['https://www.your-domain.com', 'https://staging.your-domain.com', 'http://localhost:3000'], | |
}, | |
}); | |
const openai = new sst.Secret('OpenAiApiKey'); | |
const stripePublishKey = new sst.Secret('StripePublishKey'); | |
const stripeSecret = new sst.Secret('StripeSecretKey'); | |
const appUrl = new sst.Secret('AppUrl'); | |
const databaseUrl = new sst.Secret('DatabaseUrl'); | |
const directUrl = new sst.Secret('DirectUrl'); | |
const frontEndUrl = new sst.Secret('FrontEndUrl'); | |
const clerkPublishableKey = new sst.Secret('ClerkPublishableKey'); | |
const clerkSecretKey = new sst.Secret('ClerkSecretKey'); | |
const clerkWebhookSecret = new sst.Secret('ClerkWebhookSecret'); | |
new sst.aws.Nextjs('YourAppSAASWeb', { | |
link: [ | |
bucket, | |
openai, | |
stripePublishKey, | |
stripeSecret, | |
appUrl, | |
frontEndUrl, | |
databaseUrl, | |
directUrl, | |
clerkPublishableKey, | |
clerkSecretKey, | |
clerkWebhookSecret | |
], | |
environment: | |
$app.stage === 'production' | |
? { | |
OPENAI_API_KEY: openai.value, | |
NEXT_PUBLIC_STRIPE_PUBLISH_KEY: stripePublishKey.value, | |
STRIPE_SECRET_KEY: stripeSecret.value, | |
NEXT_PUBLIC_APP_URL: appUrl.value, | |
DATABASE_URL: databaseUrl.value, | |
DIRECT_URL: directUrl.value, | |
FRONTEND_URL: frontEndUrl.value, | |
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: clerkPublishableKey.value, | |
CLERK_SECRET_KEY: clerkSecretKey.value, | |
CLERK_WEBHOOK_SECRET: clerkWebhookSecret.value, | |
} | |
: { | |
OPENAI_API_KEY: openai.value, | |
NEXT_PUBLIC_STRIPE_PUBLISH_KEY: stripePublishKey.value, | |
STRIPE_SECRET_KEY: stripeSecret.value, | |
NEXT_PUBLIC_APP_URL: appUrl.value, | |
DATABASE_URL: databaseUrl.value, | |
DIRECT_URL: directUrl.value, | |
FRONTEND_URL: frontEndUrl.value, | |
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: clerkPublishableKey.value, | |
CLERK_SECRET_KEY: clerkSecretKey.value, | |
CLERK_WEBHOOK_SECRET: clerkWebhookSecret.value, | |
}, | |
domain: { | |
name: | |
// Redirect your-domain.com to www.your-domain.com | |
$app.stage === 'production' | |
? 'www.your-domain.com' | |
: `${$app.stage}.your-domain.com`, | |
redirects: $app.stage === 'production' ? ['your-domain.com'] : undefined, | |
}, | |
}); | |
return { | |
Bucket: bucket.name, | |
Api: api.url, | |
EmailUrl: emailApi.url, | |
}; | |
}, | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Then you can put your secret value in the terminal using a command like this.
Case: Development(http://localhost:3000 or https://your-user-name.your-domain.com)
npx sst secret set StripeSecretWebhook whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Case: Staging(https://staging.your-domain.com)
npx sst secret set StripeSecretWebhook whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx --stage staging
Case: Production(https://www.your-domain.com)
npx sst secret set StripeSecretWebhook whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx --stage production
And run this command over again for every secret.
If you want to see all the secrets for each stage use this command
Case: Development(http://localhost:3000 or https://your-user-name.your-domain.com)
npx sst secret list
Case: Staging(https://staging.your-domain.com)
npx sst secret list --stage staging
Case: Production(https://www.your-domain.com)
npx sst secret list --stage production
Once ready, you can use this secret everywhere on the Next.js project using this import
import { Resource } from 'sst';
Remark:
Resource
undernpx sst dev
, thenext dev
command does not get access to Resource from sst.Resource
click atsst
.sst's Resource
, you must runnpx sst dev
ornpx sst deploy
.Add dynamic image to AWS Lambda
Reference: https://v0.dev/chat/-NYYJGoTEzj?b=b_u33Pfd3