Integrate with your AWS environment with ChatGPT
Architecture
To quickly test the integration of ChatGPT with my environment, I utilized this architecture to gain a quick use experience. However, there are several areas where this deployment can be improved.
Firstly, the AWS HTTP protocol API gateway has a maximum timeout of 30 seconds. After 30 seconds, the HTTP request will be terminated, leading to an HTTP error 503.
Additionally, the web site could be migrated to a containerized environment to provide more control. Currently, the DynamoDB is being used as the identity provider in this demo. To enhance security, a more standardized authentication method could be implemented for authentication purposes.
Login OpenAI with URL https://platform.openai.com/login/.
Generate an API secret key.
设置本地环境
安装 Node.js on MacOS.Download Node.js from https://nodejs.org/en/download/.
Install Node.js after download complete.


获取源代码并构建包
从GitHub下载代码包,保存在当前目录下的chatbotgpt子目录中。% git clone https://github.com/aws-samples/aws-serverless-openai-chatbot-demo.git chatbotgpt
Cloning into 'chatbotgpt'... remote: Enumerating objects: 102, done. remote: Counting objects: 100% (102/102), done. remote: Compressing objects: 100% (89/89), done. remote: Total 102 (delta 32), reused 73 (delta 13), pack-reused 0 Receiving objects: 100% (102/102), 1.61 MiB | 1.52 MiB/s, done. Resolving deltas: 100% (32/32), done.
该项目文件夹结构为:
% tree chatbotgpt
chatbotgpt ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── assets │ ├── UIdemo.png │ ├── apigw1.png │ ├── architecture.png │ ├── cors.png │ ├── createlambda1.png │ ├── daynamo1.png │ ├── dynamo2.png │ ├── lambdaauth.png │ ├── openaikey.png │ └── runtimeout.png ├── client │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── index.html │ │ └── robots.txt │ ├── src │ │ ├── App.css │ │ ├── App.js │ │ ├── commons │ │ │ ├── apigw.js │ │ │ ├── localStorage.js │ │ │ └── use-auth.js │ │ ├── index.css │ │ ├── index.js │ │ └── pages │ │ ├── chatpage.jsx │ │ ├── components.jsx │ │ ├── login.jsx │ │ └── private-route.jsx │ └── yarn.lock └── server ├── dynamodb.cli.txt ├── lambda_authorization │ ├── index.mjs │ ├── package-lock.json │ ├── package.json │ └── yarn.lock ├── lambda_chat │ ├── index.mjs │ ├── package.json │ └── yarn.lock └── lambda_login ├── index.mjs ├── package-lock.json ├── package.json └── yarn.lock 10 directories, 42 files
在server文件夹中,包含后端Lambda function code。在client文件夹中,包含前端网页的代码。
分别转到server文件夹下的各个Lambda function文件夹,安装依赖项并打包制作成zip后的代码文件,上传到Amazon Lambda。
NB
我在login的Lambda中对region和table name的设置做了细微的调整(for AWS global)和修正(原hard code的DynamoDB table名字不正确)。
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: MIT-0 import jwt from "jsonwebtoken"; import bcryptjs from "bcryptjs"; import { DynamoDBClient, GetItemCommand } from "@aws-sdk/client-dynamodb"; const DEFAULT_REGION = "us-west-2"; const TABLE_NAME = "chat_user_info"; const createToken = (username) => { return jwt.sign({ username: username }, process.env.TOKEN_KEY, { expiresIn: "24h", }); }; // const hashPassword = async (plaintextPassword) => { // const hash = await bcrypt.hash(plaintextPassword, 5); //It commonly ranges between 5 and 15. In this demo, we will use 5. // console.log(hash); // }; // const comparePassword = async (plaintextPassword, hash) => { // const result = await bcrypt.compare(plaintextPassword, hash); // return result; // }; const formatResponse = (code, errormsg, token) => { const response = { isAuthorized:(code === 200), body: { message: errormsg, token: token, }, }; return response; }; const queryDynamoDb = async (key) => { const client = new DynamoDBClient({ region: DEFAULT_REGION, TableName: TABLE_NAME, }); const params = { Key: { username: { S: key } }, TableName: TABLE_NAME, }; const command = new GetItemCommand(params); try { const results = await client.send(command); console.log(results); if (!results.Item) { return null; } else { console.log(results.Item); return results.Item.password.S; } } catch (err) { console.error(err); return null; } }; export const handler = async (event) => { //query user in DB const body = JSON.parse(event.body); console.log(body); const password = await queryDynamoDb(body.username); //if user is not found, return 403 if (!password) { return formatResponse(403, "User not found", ""); } //if the password is not match, return 403 if (password !== body.password) { return formatResponse(403, "Invalid credential", ""); } //create jwt token const token = createToken(body.username); return formatResponse(200, "success", token); };
要制作zip文件,执行以下命令:
% cd server/lambda_login
% npm install
% zip -r lambda_login.zip .
% cd ..
% cd lambda_chat
% npm install
added 10 packages, and audited 11 packages in 14s 1 package is looking for funding run `npm fund` for details found 0 vulnerabilities% zip -r lambda_chat.zip .
% cd ..
% cd lambda_authorization
% npm install
added 11 packages, and audited 12 packages in 1s 1 package is looking for funding run `npm fund` for details found 0 vulnerabilities% zip -r lambda_auth.zip .
% cd ..
创建Lambda函数
创建Lambda函数处理聊天会话
首先创建一个IAM role,这里使用的名称是role_lambda_chatgpt。它用来给Lambda function login赋予必要的权限。它上面挂了AWSLambdaVPCAccessExecutionRole这个AWS managed IAM policy以及如下的DynamoDB inline policy。{ "Version": "2012-10-17", "Statement": [ { "Action": [ "dynamodb:BatchGetItem", "dynamodb:Describe*", "dynamodb:List*", "dynamodb:GetItem", "dynamodb:Query", "dynamodb:Scan", "dynamodb:PartiQLSelect" ], "Effect": "Allow", "Resource": "arn:aws:dynamodb:us-west-2:<111122223333>:table/chat_user_info" } ] }
它的trust relationship如下所示:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }
再创建一个IAM role,这里使用的名称是role_lambda_chatgpt_basic。它用来给其余2个Lambda function赋予必要的权限。它上面只挂了AWSLambdaVPCAccessExecutionRole这个AWS managed IAM policy。
在AWS Lambda控制台中,创建一个名为openai-chat的函数,选择Node.js作为运行时。将上一步创建的lambda_chat.zip上传到Lambda代码源。OpenAI的接口的响应时间往往会超过3秒(AWS Lambda function默认的timeout),因此要请将这个Lambda函数的超时设置更改为更大的值,这里设置为1分钟。
% aws lambda create-function --function-name openai-chat --runtime nodejs18.x --role arn:aws:iam::<111122223333>:role/role_lambda_chatgpt_basic --zip-file fileb://lambda_chat/lambda_chat.zip --architectures x86_64 --region us-west-2 --memory-size 256 --handler index.handler --timeout 120
{ "FunctionName": "openai-chat", "FunctionArn": "arn:aws:lambda:us-west-2:111122223333:function:openai-chat", "Runtime": "nodejs18.x", "Role": "arn:aws:iam::<111122223333>:role/role_lambda_chatgpt_basic", "Handler": "index.handler", "CodeSize": 290459, "Description": "", "Timeout": 120, "MemorySize": 256, "LastModified": "2023-02-09T11:35:23.476+0000", "CodeSha256": "U96X***m8I=", "Version": "$LATEST", "TracingConfig": { "Mode": "PassThrough" }, "RevisionId": "9902b16d-***-81d519ebc8a1", "State": "Pending", "StateReason": "The function is being created.", "StateReasonCode": "Creating", "PackageType": "Zip", "Architectures": [ "x86_64" ], "EphemeralStorage": { "Size": 512 } }
创建Lambda 函数用于AWS API Gateway请求鉴权
在AWS Lambda 控制台中,创建一个名为chat-authorizer的Lambda函数。将之前创建的lambda_auth.zip文件上传到Lambda。% aws lambda create-function --function-name chat-authorizer --runtime nodejs18.x --role arn:aws:iam::<111122223333>:role/role_lambda_chatgpt_basic --zip-file fileb://lambda_authorization/lambda_auth.zip --architectures x86_64 --region us-west-2 --memory-size 256 --handler index.handler --timeout 10
{ "FunctionName": "chat-authorizer", "FunctionArn": "arn:aws:lambda:us-west-2:<111122223333>:function:chat-authorizer", "Runtime": "nodejs18.x", "Role": "arn:aws:iam::<111122223333>:role/role_lambda_chatgpt_basic", "Handler": "index.handler", "CodeSize": 796317, "Description": "", "Timeout": 10, "MemorySize": 256, "LastModified": "2023-02-09T11:39:59.307+0000", "CodeSha256": "Y4Mq***5K8=", "Version": "$LATEST", "TracingConfig": { "Mode": "PassThrough" }, "RevisionId": "c23f085c-***-49703ddc002e", "State": "Pending", "StateReason": "The function is being created.", "StateReasonCode": "Creating", "PackageType": "Zip", "Architectures": [ "x86_64" ], "EphemeralStorage": { "Size": 512 } }
创建Lambda函数来处理用户登录请求
在AWS Lambda控制台中,创建一个名为openai-login的Lambda function。将之前创建的lambda_login.zip文件上传到Lambda。此函数将调用DynamoDB的API来验证用户名和密码,因此这个Lambda function挂的role授予了对相关DynamoDB表的只读权限。% aws lambda create-function --function-name openai-login --runtime nodejs18.x --role arn:aws:iam::<111122223333>:role/role_lambda_chatgpt --zip-file fileb://lambda_login/lambda_login.zip --architectures x86_64 --region us-west-2 --memory-size 256 --handler index.handler --timeout 10
{ "FunctionName": "openai-login", "FunctionArn": "arn:aws:lambda:us-west-2:<111122223333>:function:openai-login", "Runtime": "nodejs18.x", "Role": "arn:aws:iam::<111122223333>:role/role_lambda_chatgpt", "Handler": "index.handler", "CodeSize": 883225, "Description": "", "Timeout": 10, "MemorySize": 256, "LastModified": "2023-02-09T11:42:09.241+0000", "CodeSha256": "Lgdv***9AU=", "Version": "$LATEST", "TracingConfig": { "Mode": "PassThrough" }, "RevisionId": "3d3d3966-***-f81b4f02fbcb", "State": "Pending", "StateReason": "The function is being created.", "StateReasonCode": "Creating", "PackageType": "Zip", "Architectures": [ "x86_64" ], "EphemeralStorage": { "Size": 512 } }
在名为openai-chat的Lambda function中,配置环境变量,键为OPENAI_API_KEY,配置它的值为自己的OpenAI API KEY的值。
在名为chat-authorizer的Lambda function中,配置环境变量TOKEN_KEY,自定义一个值,该值将作为JWT签名密钥。
在名为openai-login的Lambda function中,配置环境变量TOKEN_KEY,其值与chat-authorizer相同。
7. 创建API Gateway
创建一个名为chatbot的HTTP API
% aws apigatewayv2 create-api --name chatbot --protocol-type HTTP{ "ApiEndpoint": "https://<ApiId>.execute-api.us-west-2.amazonaws.com", "ApiId": "<ApiId>", "ApiKeySelectionExpression": "$request.header.x-api-key", "CreatedDate": "2023-02-09T12:15:16+00:00", "DisableExecuteApiEndpoint": false, "Name": "chatbot", "ProtocolType": "HTTP", "RouteSelectionExpression": "$request.method $request.path" }记下这里返回的<ApiId>,后面见到<ApiId>的地方用这里的值进行替换。
% aws apigatewayv2 create-stage --api-id <ApiId> --stage-name "\$default" --auto-deploy
{ "AutoDeploy": true, "CreatedDate": "2023-02-09T12:19:12+00:00", "DefaultRouteSettings": { "DetailedMetricsEnabled": false }, "LastUpdatedDate": "2023-02-09T12:19:12+00:00", "RouteSettings": {}, "StageName": "$default", "StageVariables": {}, "Tags": {} }
% aws apigatewayv2 create-route --api-id <ApiId> --route-key 'POST /chat'
{ "ApiKeyRequired": false, "AuthorizationType": "NONE", "RouteId": "xs***of", "RouteKey": "POST /chat" }
% aws apigatewayv2 create-route --api-id <ApiId> --route-key 'POST /login'
{ "ApiKeyRequired": false, "AuthorizationType": "NONE", "RouteId": "cz***c4", "RouteKey": "POST /login" }
对于/chat路由,创建Lambda authorizer,以及集成到Lambda函数
创建一个Lambda authorizer,使用之前创建的chat-authorizer函数。% aws apigatewayv2 create-authorizer --api-id <ApiId> --authorizer-type REQUEST --authorizer-uri arn:aws:apigateway:us-west-2:lambda:path/2015-03-31/functions/arn:aws:lambda:us-west-2:<111122223333>:function:chat-authorizer/invocations --authorizer-payload-format-version 2.0 --name chat-authorizer --identity-source '$request.header.Authorization' --enable-simple-responses --authorizer-result-ttl-in-seconds 300
{ "AuthorizerId": "<AuthorizerId>", "AuthorizerPayloadFormatVersion": "2.0", "AuthorizerResultTtlInSeconds": 300, "AuthorizerType": "REQUEST", "AuthorizerUri": "arn:aws:apigateway:us-west-2:lambda:path/2015-03-31/functions/arn:aws:lambda:us-west-2:<111122223333>:function:chat-authorizer/invocations", "EnableSimpleResponses": true, "IdentitySource": [ "$request.header.Authorization" ], "Name": "chat-authorizer" }记下这里返回的<AuthorizerId>,后面见到<AuthorizerId>的地方用这里的值进行替换。
另外,下面的ID与具体路由和集成的对应关系也在这里总结出来:
xs***of ~ /chat的Route ID
cz***c4 ~ /login的Route ID
2u***z9 ~ 与路由/chat对应的integration ID
pv***t3 ~ 与路由/login的integration ID
Grant invoke permission
% aws lambda add-permission \
--statement-id chatgpt \
--action lambda:InvokeFunction \
--function-name "arn:aws:lambda:us-west-2:<111122223333>:function:chat-authorizer" \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:us-west-2:<111122223333>:<ApiId>/authorizers/<AuthorizerId>"
{ "Statement": "{\"Sid\":\"chatgpt\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"apigateway.amazonaws.com\"},\"Action\":\"lambda:InvokeFunction\",\"Resource\":\"arn:aws:lambda:us-west-2:<111122223333>:function:chat-authorizer\",\"Condition\":{\"ArnLike\":{\"AWS:SourceArn\":\"arn:aws:execute-api:us-west-2:<111122223333>:<ApiId>/authorizers/<AuthorizerId>\"}}}" }
创建Lambda集成,集成目标是之前创建的openai-chat函数。
% aws apigatewayv2 create-integration --api-id <ApiId> --integration-type AWS_PROXY --integration-uri arn:aws:lambda:us-west-2:<111122223333>:function:openai-chat --payload-format-version 2.0
{ "ConnectionType": "INTERNET", "IntegrationId": "2u***z9", "IntegrationMethod": "POST", "IntegrationType": "AWS_PROXY", "IntegrationUri": "arn:aws:lambda:us-west-2:<111122223333>:function:openai-chat", "PayloadFormatVersion": "2.0", "TimeoutInMillis": 30000 }
The following command grants API Gateway permission to invoke your Lambda function. If API Gateway doesn't have permission to invoke your function, clients receive a
500 Internal Server Error
.aws lambda add-permission \
--statement-id chatgpt \
--action lambda:InvokeFunction \
--function-name "arn:aws:lambda:us-west-2:<111122223333>:function:openai-chat" \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:us-west-2:<111122223333>:<ApiId>/*/*/chat"
{ "Statement": "{\"Sid\":\"chatgpt\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"apigateway.amazonaws.com\"},\"Action\":\"lambda:InvokeFunction\",\"Resource\":\"arn:aws:lambda:us-west-2:<111122223333>:function:openai-chat\",\"Condition\":{\"ArnLike\":{\"AWS:SourceArn\":\"arn:aws:execute-api:us-west-2:<111122223333>:<ApiId>/*/*/chat\"}}}" }
将上面提到的Lambda集成,附加到route “/chat”,并附加到之前创建的Lambda authorizer上。
% aws apigatewayv2 update-route \
--api-id<ApiId> \
--route-id xs***of \
--target integrations/2u***z9 \
--authorization-type CUSTOM \
--authorizer-id <AuthorizerId>
{ "ApiKeyRequired": false, "AuthorizationType": "CUSTOM", "AuthorizerId": "<AuthorizerId>", "RouteId": "xs***of", "RouteKey": "POST /chat", "Target": "integrations/2u***z9" }
对于"/login"路由,创建并附加一个Lambda integration,集成目标是之前创建的“openai-login”函数
% aws apigatewayv2 create-integration --api-id<ApiId> --integration-type AWS_PROXY --integration-uri arn:aws:lambda:us-west-2:<111122223333>:function:openai-login --payload-format-version 2.0
{ "ConnectionType": "INTERNET", "IntegrationId": "pv***t3", "IntegrationMethod": "POST", "IntegrationType": "AWS_PROXY", "IntegrationUri": "arn:aws:lambda:us-west-2:<111122223333>:function:openai-login", "PayloadFormatVersion": "2.0", "TimeoutInMillis": 30000 }
% aws lambda add-permission \
--statement-id chatgpt \
--action lambda:InvokeFunction \
--function-name "arn:aws:lambda:us-west-2:<111122223333>:function:openai-login" \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:us-west-2:<111122223333>:<ApiId>/*/*/login"
{ "Statement": "{\"Sid\":\"chatgpt\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"apigateway.amazonaws.com\"},\"Action\":\"lambda:InvokeFunction\",\"Resource\":\"arn:aws:lambda:us-west-2:<111122223333>:function:openai-login\",\"Condition\":{\"ArnLike\":{\"AWS:SourceArn\":\"arn:aws:execute-api:us-west-2:<111122223333>:<ApiId>/*/*/login\"}}}" }
% aws apigatewayv2 update-route \
--api-id<ApiId> \
--route-id cz***c4 \
--target integrations/pv***t3
{ "ApiKeyRequired": false, "AuthorizationType": "NONE", "RouteId": "cz***c4", "RouteKey": "POST /login", "Target": "integrations/pv***t3" }
设置CORS
Enable CORS.% aws apigatewayv2 update-api --api-id <ApiId> --cors-configuration AllowOrigins="*",AllowHeaders="*",AllowMethods="*",AllowCredentials=false,ExposeHeaders="",MaxAge=0
{ "ApiEndpoint": "https://<ApiId>.execute-api.us-west-2.amazonaws.com", "ApiId": "<ApiId>", "ApiKeySelectionExpression": "$request.header.x-api-key", "CorsConfiguration": { "AllowCredentials": false, "AllowHeaders": [ "*" ], "AllowMethods": [ "*" ], "AllowOrigins": [ "*" ], "ExposeHeaders": [ "" ], "MaxAge": 0 }, "CreatedDate": "2023-02-09T12:15:16+00:00", "DisableExecuteApiEndpoint": false, "Name": "chatbot", "ProtocolType": "HTTP", "RouteSelectionExpression": "$request.method $request.path", "Tags": {} }
效果:

创建DynamoDB表
本演示应用使用Amazon DynamoDB存储用户名和密码凭证。为了简化演示,暂未实现注册功能和密码加密存储功能,你可以直接将用户和明文密码添加到DynamoDB的表中,或者参考代码包中的AWS CLI。打开Amazon DynamoDB控制台,创建一个表名chat_user_info,分区键名为username,字符串类型。
% aws dynamodb create-table --attribute-definitions AttributeName=username,AttributeType=S --table-name chat_user_info --key-schema AttributeName=username,KeyType=HASH --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1
{ "TableDescription": { "AttributeDefinitions": [ { "AttributeName": "username", "AttributeType": "S" } ], "TableName": "chat_user_info", "KeySchema": [ { "AttributeName": "username", "KeyType": "HASH" } ], "TableStatus": "CREATING", "CreationDateTime": "2023-02-09T20:58:37.191000+08:00", "ProvisionedThroughput": { "NumberOfDecreasesToday": 0, "ReadCapacityUnits": 1, "WriteCapacityUnits": 1 }, "TableSizeBytes": 0, "ItemCount": 0, "TableArn": "arn:aws:dynamodb:us-west-2:<111122223333>:table/chat_user_info", "TableId": "eabc4530-***-c6b479292842" } }
将用户名和密码对添加到表中。添加一个字符串类型的新属性password,用于存放用户对应的密码。
% vim item.json
{ "username": {"S": "eXample"}, "password": {"S": "Example"} }
% aws dynamodb put-item \
--table-name chat_user_info \
--item file://item.json \
--return-consumed-capacity TOTAL \
--return-item-collection-metrics SIZE
{ "ConsumedCapacity": { "TableName": "chat_user_info", "CapacityUnits": 1.0 } }
9. 部署S3托管网站
创建S3存储桶,并启用静态网站托管功能
详情可参考:https://docs.aws.amazon.com/AmazonS3/latest/userguide/WebsiteHosting.html将存储桶配置为静态网站时,必须启用静态网站托管、配置索引文档并设置权限。
在Amazon S3控制台上创建一个名为<Bucket-name>(请替换成自己的S3存储桶名称)的S3存储桶。在“属性”选项卡中启用此存储桶的静态网站托管功能。在索引文档中,输入索引文档的文件名
index.html
。% aws s3 mb s3://<Bucket-name>
make_bucket: <Bucket-name>
% aws s3 website s3://<Bucket-name> --index-document index.html
S3存储桶的公共访问如果被阻止了,更改权限设置。
aws s3control delete-public-access-block --account-id <111122223333>
将以下策略(请修改<Bucket-name>为实际存储桶名称。另外,如果使用AWS China账号,ARN的前缀修改为arn:aws-cn:...。)
% vim bucketpolicy.json
{ "Version": "2012-10-17", "Statement": [ { "Sid": "PublicReadGetObject", "Effect": "Allow", "Principal": "*", "Action": [ "s3:GetObject" ], "Resource": [ "arn:aws:s3:::<Bucket-name>/*" ] } ] }
应用上面定义的S3 bucket policy,以允许能从Internet访问。
% aws s3api put-bucket-policy --bucket <Bucket-name> --policy file://bucketpolicy.json
编译网站静态文件
在本地环境中,转到client/src/commons文件夹,将apigw.js
的第一行的API Gateway ID根据实际情况进行更新。% cd client/src/commons
% vim apigw.js
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: MIT-0 //Change to your own API Gateway endpoint const API_endpoint = 'https://<ApiId>.execute-api.us-west-2.amazonaws.com/'; export const getAnswer = async(respid,text,headers) =>{ const options ={ method:'POST', // mode: 'no-cors', headers:headers, body:JSON.stringify({id:respid,prompt:text}) } try { var resp = await fetch(API_endpoint+'chat', options); if (!resp.ok){ const data = await resp.text(); throw (Error(`Error: ${resp.status},${data}`)); } var data = await resp.json() ; return data; } catch (err) { throw err; } } export const loginAuth = async(username,password) =>{ const options ={ method:'POST', headers:{'Content-Type': 'application/json', 'Access-Control-Allow-Origin':'*'}, body:JSON.stringify({username:username,password:password}) } try { var resp = await fetch(API_endpoint+'login', options); if (!resp.ok) { console.log('resp.ok'); const data = await resp.json(); throw (Error(`Error: ${resp.status},${data.msg}`)); } var data = await resp.json() ; return data; } catch (err) { throw err; } }
% cd ../..
在client目录下,然后运行这些命令来编译静态网站的文件。
% npm install
npm WARN deprecated svgo@1.3.2: This SVGO version is no longer supported. Upgrade to v2.x.x. npm WARN deprecated stable@0.1.8: Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility npm WARN deprecated sourcemap-codec@1.4.8: Please use @jridgewell/sourcemap-codec instead npm WARN deprecated rollup-plugin-terser@7.0.2: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser added 1529 packages, and audited 1530 packages in 1m 3 packages are looking for funding run `npm fund` for details 6 high severity vulnerabilities To address all issues (including breaking changes), run: npm audit fix --force Run `npm audit` for details.
% npm run build
> gptapp@0.1.0 build > react-scripts build Creating an optimized production build... Compiled with warnings. [eslint] src/App.js Line 5:50: 'Link' is defined but never used no-unused-vars Search for the keywords to learn more about each warning. To ignore, add // eslint-disable-next-line to the line before. File sizes after gzip: 133.7 kB build/static/js/main.f04a3f6d.js 541 B build/static/css/main.073c9b0a.css The project was built assuming it is hosted at /. You can control this with the homepage field in your package.json. The build folder is ready to be deployed. You may serve it with a static server: yarn global add serve serve -s build Find out more about deployment here: https://cra.link/deployment
当npm run build命令运行完成后,它将在client文件夹中生成一个名为build的文件夹。该文件夹包含了要部署到Amazon S3网站的所有静态文件。
% cd ../..
% ls
build package.json yarn.lock node_modules public package-lock.json src
将此文件夹上传到S3。
% aws s3 sync ./build/ s3://<Bucket-name>/
upload: build/asset-manifest.json to s3://<Bucket-name>/asset-manifest.json upload: build/index.html to s3://<Bucket-name>/index.html upload: build/static/css/main.073c9b0a.css.map to s3://<Bucket-name>/static/css/main.073c9b0a.css.map upload: build/static/js/main.f04a3f6d.js.LICENSE.txt to s3://<Bucket-name>/static/js/main.f04a3f6d.js.LICENSE.txt upload: build/robots.txt to s3://<Bucket-name>/robots.txt upload: build/static/css/main.073c9b0a.css to s3://<Bucket-name>/static/css/main.073c9b0a.css upload: build/static/js/main.f04a3f6d.js to s3://<Bucket-name>/static/js/main.f04a3f6d.js upload: build/static/js/main.f04a3f6d.js.map to s3://<Bucket-name>/static/js/main.f04a3f6d.js.map
现在您可以通过浏览器中访问S3静态网站,使用预先设置在DynamoDB表中的用户名和密码来登录进入。Amazon S3静态托管网站的访问端点可以在“属性”选项卡中找到。
References
使用Amazon Lambda等无服务器产品构建自己的ChatGPT应用
Controlling and managing access to an HTTP API in API Gateway
Configuring CORS for an HTTP API
GitHub - aws-samples / openbanking-brazilian-auth-samples