From 52e4a8ec61260bdfb898e33e83beec950d2c6fbd Mon Sep 17 00:00:00 2001 From: important-new Date: Sun, 14 Jun 2026 17:45:39 +0800 Subject: [PATCH] docs: fix stale code/doc inconsistencies; reconcile no-tag policy; fix 2 script bugs Doc-vs-code audit follow-up. Corrections: API docs (docs/ + docs/zh-CN/): - API_CONFIGURATION: re-point the tenant-business-interface section away from the non-existent server/tenant-template.yaml to the real split files (services/tenant-api/template.yaml + services/order-service/template.yaml); fix GetOrdersFunction CodeUri (OrderService/ -> src/), the cross-stack ARN wiring (!Ref GetOrdersFunctionArn), the ReservedConcurrentExecutions expr, and CreateTenantAdminUser CodeUri (../TenantManagementService/ -> ../tenant-management/). Snippets now match the templates verbatim. - THROTTLING_AND_MONITORING: fix the metric-filter file/line reference. Frontend docs/code: - Admin README + modules.md: Angular 14 -> 20, drop Flex Layout, v4 Auth snippets -> v6 fetchAuthSession, fix component names, drop broken screenshot links. - Application/Landing README: Angular CLI 14.0.5 -> 20.x; e2e text -> Playwright. - cypress/README: mark Cypress legacy, point to canonical Playwright e2e/, fix stale repo path. - Landing register.component.html: remove dead fxLayout directive (flex-layout was removed). Scripts (code bugs): - generate-env-config.js: query a non-existent API GW name -> read AdminApi output from saas-control-stack (mirrors get-login-info.js). - DEPLOYMENT_GUIDE.md: rewrite the fictional multi-env guide to the real ./deployment.sh + ./geturl.sh flow. Governance: - CONTRIBUTING: document main branch protection (PR required, CI gating). - SECURITY + CHANGELOG: reconcile to "no tagged releases" (tag was deleted); remove broken compare/tag links. - CHANGELOG: "per-service tests" -> product/order only; add Unreleased entry. - README.zh-CN: fix deep-dive link text to match zh-CN targets. - PR template: use repo-relative CONTRIBUTING.md link. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- CHANGELOG.md | 17 +- CONTRIBUTING.md | 32 ++- README.zh-CN.md | 8 +- SECURITY.md | 10 +- client/Admin/README.md | 16 +- client/Admin/docs/modules.md | 17 +- client/Application/README.md | 10 +- client/Application/cypress/README.md | 16 +- client/Landing/README.md | 8 +- .../views/register/register.component.html | 2 +- docs/API_CONFIGURATION.md | 43 ++- docs/THROTTLING_AND_MONITORING.md | 4 +- docs/zh-CN/API_CONFIGURATION.md | 43 ++- docs/zh-CN/THROTTLING_AND_MONITORING.md | 4 +- scripts/DEPLOYMENT_GUIDE.md | 272 ++++-------------- scripts/generate-env-config.js | 33 +-- 17 files changed, 202 insertions(+), 335 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index ca6b6b6..ca9baae 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -26,5 +26,5 @@ Closes # - [ ] For frontend changes, I built all three apps (Admin, Application, Landing) and the Playwright smoke tests pass. - [ ] I updated the docs (README.md / README.zh-CN.md and docs/) if behavior changed. - [ ] No secrets, credentials, or AWS account IDs are committed. -- [ ] My changes follow the coding conventions in [CONTRIBUTING.md](../blob/main/CONTRIBUTING.md). +- [ ] My changes follow the coding conventions in [CONTRIBUTING.md](CONTRIBUTING.md). - [ ] I confirm my contribution is licensed under the MIT-0 license. diff --git a/CHANGELOG.md b/CHANGELOG.md index a9ea527..edf2544 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- Branch protection on `main`: direct pushes are blocked, a Pull Request is + required for all changes, and required status checks must pass before merge. + +### Changed +- Both CI workflows (`backend-tests.yml`, `frontend-e2e.yml`) now run the full + pytest and Playwright suites on every pull request, gating merges into `main`. + ## [2.0.0] - 2026-06-14 Dependency & runtime modernization, a cross-platform test suite, and a security @@ -21,7 +29,7 @@ major upgrades, hence the major version bump. ### Added - Cross-platform backend test suite using **pytest + moto** (in-memory DynamoDB, no Docker/AWS required): `requirements-test.txt`, `pytest.ini`, `server/conftest.py`, - and per-service `tests/test_*.py`. + and tests for the product and order services (`tests/test_*.py`). - Cross-platform frontend smoke tests using **Playwright** (`e2e/`). - **GitHub Actions CI**: `.github/workflows/backend-tests.yml` (pytest on ubuntu/windows/macOS) and `.github/workflows/frontend-e2e.yml`, both with @@ -63,7 +71,7 @@ major upgrades, hence the major version bump. ## [1.0.1] - 2025-08-05 -Initial tagged baseline of the derivative: Lab6 reworked from a teaching-oriented +Initial baseline of the derivative: Lab6 reworked from a teaching-oriented lab into a deployable, structurally simplified reference implementation. See [`docs/CHANGES_FROM_WORKSHOP.md`](docs/CHANGES_FROM_WORKSHOP.md) for the complete divergence record and the upstream→quickstart path mapping. @@ -97,6 +105,5 @@ divergence record and the upstream→quickstart path mapping. `shared-template.yaml` / `tenant-template.yaml`, plus the `shardId`-based sharding strategy and its parallel-query logic. -[Unreleased]: https://github.com/important-new/aws-serverless-saas-quickstart/compare/2.0.0...HEAD -[2.0.0]: https://github.com/important-new/aws-serverless-saas-quickstart/compare/1.0.1...2.0.0 -[1.0.1]: https://github.com/important-new/aws-serverless-saas-quickstart/releases/tag/1.0.1 + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index de7e7b5..3ed5f12 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -49,18 +49,27 @@ npx playwright install chromium npx playwright test ``` -CI runs both on every push/PR — see [`.github/workflows/`](.github/workflows). +CI runs both on every pull request — see [`.github/workflows/`](.github/workflows). ### Submitting changes -1. Fork the repository and create a topic branch off `main` - (e.g. `fix/order-update-bug`, `feat/add-invoice-service`). +`main` is a protected branch: **direct pushes are blocked and all changes must go +through a Pull Request**. Required status checks must pass before a PR can be +merged — the backend `pytest` suite on ubuntu/windows/macOS +(`backend-tests.yml`) and the frontend Playwright suite (`frontend-e2e.yml`). +These rules apply to everyone, maintainers included. + +1. Fork the repository (or, if you have write access, create a topic branch); + either way, branch off `main` (e.g. `fix/order-update-bug`, + `feat/add-invoice-service`). Never push directly to `main`. 2. Make focused commits with clear messages. Keep unrelated changes in separate PRs. 3. Ensure `pytest` passes and, for frontend changes, that all three apps build (`npx ng build --configuration production`) and Playwright smoke tests pass. + The same checks run in CI and must pass before merge. 4. Update docs (`README.md` / `README.zh-CN.md` and anything under `docs/`) when behaviour or setup changes. -5. Open a Pull Request using the template. Link any related issue. +5. Open a Pull Request using the template. Link any related issue. A maintainer + merges it once the required checks are green. ### Coding conventions @@ -118,15 +127,22 @@ npx playwright install chromium npx playwright test ``` -CI 在每次 push / PR 时运行二者,见 [`.github/workflows/`](.github/workflows)。 +CI 在每次 Pull Request 时运行二者,见 [`.github/workflows/`](.github/workflows)。 ### 提交变更 -1. Fork 仓库,从 `main` 切出主题分支(如 `fix/order-update-bug`)。 +`main` 是受保护分支:**禁止直接推送,所有改动都必须通过 Pull Request 合入**。合并前 +必须通过所需的状态检查 —— 后端在 ubuntu/windows/macOS 上的 `pytest` 套件 +(`backend-tests.yml`)以及前端的 Playwright 套件(`frontend-e2e.yml`)。该规则对所有 +人生效,包括维护者。 + +1. Fork 仓库(若你拥有写权限,也可直接创建主题分支),无论哪种方式都从 `main` 切出主题 + 分支(如 `fix/order-update-bug`)。切勿直接推送到 `main`。 2. 提交粒度清晰、信息明确;无关改动请拆分到不同 PR。 -3. 确保 `pytest` 通过;前端改动需保证三个 app 均能构建并通过 Playwright 冒烟。 +3. 确保 `pytest` 通过;前端改动需保证三个 app 均能构建并通过 Playwright 冒烟。相同的检查 + 会在 CI 中运行,且必须在合并前通过。 4. 行为或配置变化时,同步更新文档(`README.md` / `README.zh-CN.md` 及 `docs/`)。 -5. 使用模板提交 Pull Request,并关联相关 issue。 +5. 使用模板提交 Pull Request,并关联相关 issue。所需检查全部通过后,由维护者完成合并。 ### 编码约定 diff --git a/README.zh-CN.md b/README.zh-CN.md index eb54612..89834eb 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -161,10 +161,10 @@ aws-serverless-saas-quickstart/ 本 README 为高层入口,深入的架构剖析位于 [`docs/`](docs/) 目录下: -- [`docs/ARCHITECTURE.md`](docs/zh-CN/ARCHITECTURE.md) —— 架构索引(技术栈、数据库设计、结构) -- [`docs/TENANT_MANAGEMENT.md`](docs/zh-CN/TENANT_MANAGEMENT.md) —— 平台租户管理实现(生命周期、用户/租户关联、权限控制、分级) -- [`docs/API_CONFIGURATION.md`](docs/zh-CN/API_CONFIGURATION.md) —— 管理端与租户端接口配置关系剖析 -- [`docs/THROTTLING_AND_MONITORING.md`](docs/zh-CN/THROTTLING_AND_MONITORING.md) —— 监控运维、使用计划限流,以及 CloudWatch 限流指标机制 +- [`docs/zh-CN/ARCHITECTURE.md`](docs/zh-CN/ARCHITECTURE.md) —— 架构索引(技术栈、数据库设计、结构) +- [`docs/zh-CN/TENANT_MANAGEMENT.md`](docs/zh-CN/TENANT_MANAGEMENT.md) —— 平台租户管理实现(生命周期、用户/租户关联、权限控制、分级) +- [`docs/zh-CN/API_CONFIGURATION.md`](docs/zh-CN/API_CONFIGURATION.md) —— 管理端与租户端接口配置关系剖析 +- [`docs/zh-CN/THROTTLING_AND_MONITORING.md`](docs/zh-CN/THROTTLING_AND_MONITORING.md) —— 监控运维、使用计划限流,以及 CloudWatch 限流指标机制 - [`docs/CONFIGURATION.md`](docs/CONFIGURATION.md) | [`docs/LOCAL_TESTING.md`](docs/LOCAL_TESTING.md) | [`docs/CHANGES_FROM_WORKSHOP.md`](docs/CHANGES_FROM_WORKSHOP.md) | [`docs/DEPENDENCY_AUDIT.md`](docs/DEPENDENCY_AUDIT.md) 深度剖析的英文版位于 [`docs/`](docs/) 根目录。 diff --git a/SECURITY.md b/SECURITY.md index 9dd4c15..2758ca0 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -10,8 +10,8 @@ This project is a reference architecture and quick-start derived from the AWS Serverless SaaS Workshop. It is community-maintained and licensed under MIT-0. Only the latest `main` branch is supported. There are no tagged releases and no -backported security patches for older commits. Fixes land on `main`; if you are -running an older checkout, please update to the latest `main`. +backported security patches for older commits; security fixes land on `main`. +If you are running an older checkout, please update to the latest `main`. | Version | Supported | | --- | --- | @@ -83,9 +83,9 @@ This project is distributed under the terms in [LICENSE](LICENSE). 本项目是基于 AWS Serverless SaaS Workshop 衍生的参考架构与快速入门模板,由社区维护, 采用 MIT-0 许可证。 -仅支持最新的 `main` 分支。本项目没有发布带标签的版本,也不会为较旧的提交提供安全补丁的 -回溯(backport)。所有修复都会合入 `main`;如果你正在使用较旧的检出版本,请更新到最新的 -`main`。 +仅支持最新的 `main` 分支。本项目没有打标签的发布版本,也不会为较旧的提交提供回溯 +(backport)安全补丁;安全修复都会合入 `main`。如果你正在使用较旧的检出版本,请更新到 +最新的 `main`。 | 版本 | 是否受支持 | | --- | --- | diff --git a/client/Admin/README.md b/client/Admin/README.md index 8f178f5..0e5307b 100644 --- a/client/Admin/README.md +++ b/client/Admin/README.md @@ -1,11 +1,11 @@ ## 项目概览 -Admin 是一个使用 Angular 14 构建的管理端前端应用,集成 AWS Amplify(Cognito User Pool)完成用户认证,通过全局 HTTP 拦截器为 API 请求自动附加 ID Token 进行后端鉴权。路由采用懒加载模块,顶层使用 `AmplifyAuthenticator` 门户在 UI 层完成“登录后可见”的访问控制。 +Admin 是一个使用 Angular 20 构建的管理端前端应用,集成 AWS Amplify(Cognito User Pool)完成用户认证,通过全局 HTTP 拦截器为 API 请求自动附加 ID Token 进行后端鉴权。路由采用懒加载模块,顶层使用 `AmplifyAuthenticator` 门户在 UI 层完成“登录后可见”的访问控制。 ## 技术栈 -- **框架**: Angular 14, RxJS 7 -- **UI**: Angular Material, Angular CDK, Flex Layout, Bootstrap +- **框架**: Angular 20, RxJS 7 +- **UI**: Angular Material, Angular CDK, Bootstrap - **认证**: AWS Amplify UI + Cognito User Pool (`@aws-amplify/ui-angular`, `aws-amplify`) - **网络**: Angular `HttpClient` + 自定义 `HttpInterceptor` - **部署**: S3/CloudFront(脚本使用 AWS CLI 从 CloudFormation 读取输出并同步静态资源) @@ -24,14 +24,14 @@ Admin 是一个使用 Angular 14 构建的管理端前端应用,集成 AWS Amp ` ``` -- **会话与登出**: 使用 `Auth.currentSession()` 获取当前会话;`Auth.signOut({ global: true })` 退出登录(见 `views/auth/AuthComponent`)。 +- **会话与登出**: 使用 `fetchAuthSession()` 获取当前会话;`signOut({ global: true })` 退出登录(均来自 `aws-amplify/auth`,见 `views/auth/AuthComponent`)。 - **后端鉴权(令牌附加)**: 自定义拦截器 `AuthInterceptor`(在 `app/interceptors` 提供)对除包含 `tenant/init` 的请求外的所有 HTTP 请求读取当前会话 ID Token,并在请求头加入 `Authorization: Bearer `: ```ts // src/app/interceptors/auth.interceptor.ts(节选) - return from(Auth.currentSession()).pipe( - filter((sesh) => !!sesh), - map((sesh) => sesh.getIdToken().getJwtToken()), + return from(fetchAuthSession()).pipe( + filter((sesh) => !!sesh?.tokens?.idToken), + map((sesh) => sesh?.tokens?.idToken?.toString() ?? ''), switchMap((tok) => next.handle(req.clone({ headers: req.headers.set('Authorization', 'Bearer ' + tok), }))) @@ -104,7 +104,7 @@ Admin 是一个使用 Angular 14 构建的管理端前端应用,集成 AWS Amp ## 权限与路由控制的扩展建议 -- 如需前端基于角色的控制,可新增守卫(如 `CanActivate`)读取 `Auth.currentSession()` 的 ID Token Claim(例如 Cognito 组或自定义 Claim),结合路由 `data` 元信息决定放行与否。 +- 如需前端基于角色的控制,可新增守卫(如 `CanActivate`)读取 `fetchAuthSession()` 的 ID Token Claim(例如 Cognito 组或自定义 Claim),结合路由 `data` 元信息决定放行与否。 - 导航菜单可基于角色/Claim 进行动态过滤。 - 如需白名单更多无需鉴权的接口,可在 `AuthInterceptor` 中扩展排除逻辑。 diff --git a/client/Admin/docs/modules.md b/client/Admin/docs/modules.md index 26cf83d..c3f38d8 100644 --- a/client/Admin/docs/modules.md +++ b/client/Admin/docs/modules.md @@ -1,10 +1,10 @@ # Admin 模块功能说明 ## 项目概述 -Admin 是一个基于 Angular 14 和 AWS Amplify 构建的管理端前端应用,提供完整的租户和用户管理功能。 +Admin 是一个基于 Angular 20 和 AWS Amplify 构建的管理端前端应用,提供完整的租户和用户管理功能。 ## 技术架构 -- **前端框架**: Angular 14 + RxJS 7 +- **前端框架**: Angular 20 + RxJS 7 - **UI 组件**: Angular Material + Angular CDK - **认证系统**: AWS Amplify + Cognito User Pool - **状态管理**: RxJS + Angular 服务 @@ -29,9 +29,6 @@ Admin 是一个基于 Angular 14 和 AWS Amplify 构建的管理端前端应用 - **路由配置**: `/dashboard` - **懒加载**: 是 -### 页面截图 -![Dashboard](./screenshots/dashboard.png) - --- ## Tenants 模块 @@ -47,13 +44,10 @@ Admin 是一个基于 Angular 14 和 AWS Amplify 构建的管理端前端应用 ### 技术实现 - **模块路径**: `views/tenants` -- **主要组件**: TenantsComponent +- **主要组件**: ListComponent / CreateComponent - **路由配置**: `/tenants` - **懒加载**: 是 -### 页面截图 -![Tenants](./screenshots/tenants.png) - --- ## Users 模块 @@ -69,13 +63,10 @@ Admin 是一个基于 Angular 14 和 AWS Amplify 构建的管理端前端应用 ### 技术实现 - **模块路径**: `views/users` -- **主要组件**: UsersComponent +- **主要组件**: ListComponent / CreateComponent - **路由配置**: `/users` - **懒加载**: 是 -### 页面截图 -![Users](./screenshots/users.png) - ## 部署信息 ### 构建命令 diff --git a/client/Application/README.md b/client/Application/README.md index bec004b..8688112 100644 --- a/client/Application/README.md +++ b/client/Application/README.md @@ -1,6 +1,6 @@ # Application -This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 14.0.5. +This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 20.x. ## Development server @@ -20,7 +20,13 @@ Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github. ## Running end-to-end tests -Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. +The canonical e2e suite is [Playwright](https://playwright.dev) at the repo root `e2e/` directory. Run it with: + +```bash +cd e2e && npx playwright test +``` + +Legacy Cypress specs still exist under `cypress/` (see `cypress/README.md`), but Playwright is the canonical path. ## Further help diff --git a/client/Application/cypress/README.md b/client/Application/cypress/README.md index a959113..49ada5f 100644 --- a/client/Application/cypress/README.md +++ b/client/Application/cypress/README.md @@ -1,8 +1,16 @@ -# Application End-to-End Testing +# Application End-to-End Testing (Legacy Cypress) -## Instructions +> **Note:** The canonical e2e suite is now [Playwright](https://playwright.dev) at the repo root `e2e/` directory. Run it with: +> +> ```bash +> cd e2e && npx playwright test +> ``` +> +> The Cypress specs in this folder are legacy/optional and kept for reference only. -To run End-to-End (e2e) tests against the Sample Application, take the following steps: +## Instructions (legacy Cypress) + +To run the legacy Cypress e2e tests against the Sample Application, take the following steps: 1. Make a copy of the example env file (`cypress.env.json.example`): @@ -22,7 +30,7 @@ cp cypress.env.json.example cypress.env.json - `email`: The email address to use for testing. (This should be a valid email address.) -3. Navigate to the root of the Application project (`aws-saas-factory-ref-solution-serverless-saas/clients/Application/`) and run the following: +3. Navigate to the root of the Application project (`aws-serverless-saas-quickstart/client/Application/`) and run the following: ```bash npx cypress run diff --git a/client/Landing/README.md b/client/Landing/README.md index ecf68c2..da96af1 100644 --- a/client/Landing/README.md +++ b/client/Landing/README.md @@ -1,6 +1,6 @@ # Landing -This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 14.0.5. +This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 20.x. ## Development server @@ -20,7 +20,11 @@ Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github. ## Running end-to-end tests -Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. +The canonical e2e suite is [Playwright](https://playwright.dev) at the repo root `e2e/` directory. Run it with: + +```bash +cd e2e && npx playwright test +``` ## Further help diff --git a/client/Landing/src/app/views/register/register.component.html b/client/Landing/src/app/views/register/register.component.html index 5436284..6734cc3 100644 --- a/client/Landing/src/app/views/register/register.component.html +++ b/client/Landing/src/app/views/register/register.component.html @@ -1,4 +1,4 @@ -
+
Provision a new Tenant diff --git a/docs/API_CONFIGURATION.md b/docs/API_CONFIGURATION.md index e9f3426..e1e49ff 100644 --- a/docs/API_CONFIGURATION.md +++ b/docs/API_CONFIGURATION.md @@ -51,7 +51,7 @@ CreateTenantAdminUserFunction: Type: AWS::Serverless::Function DependsOn: CreateUserLambdaExecutionRole Properties: - CodeUri: ../TenantManagementService/ + CodeUri: ../tenant-management/ Handler: user-management.create_tenant_admin_user Runtime: python3.13 Role: !GetAtt CreateUserLambdaExecutionRole.Arn @@ -68,7 +68,7 @@ CreateTenantAdminUserFunction: **Key configuration**: - **Function name**: `CreateTenantAdminUserFunction` -- **Code path**: `../TenantManagementService/` +- **Code path**: `../tenant-management/` - **Handler function**: `user-management.create_tenant_admin_user` - **Runtime**: `python3.13` - **IAM role**: `CreateUserLambdaExecutionRole` @@ -175,7 +175,7 @@ The `GetOrdersFunction` interface is a core component of the tenant business app #### 1. Tenant API Gateway Configuration -**Configuration file**: `server/tenant-template.yaml` +**Configuration file**: `server/services/tenant-api/template.yaml` ```yaml ApiGatewayTenantApi: @@ -211,11 +211,9 @@ ApiGatewayTenantApi: - api_key: [] # API key authentication - Authorizer: [] # Lambda authorizer x-amazon-apigateway-integration: - uri: !Join - - '' - - - !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/ - - !GetAtt GetOrdersFunction.Arn - - /invocations + uri: !Sub + - arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${FunctionArn}/invocations + - FunctionArn: !Ref GetOrdersFunctionArn httpMethod: POST type: aws_proxy ``` @@ -231,18 +229,17 @@ ApiGatewayTenantApi: #### 2. Lambda Function Definition -**Configuration file**: `server/tenant-template.yaml` +**Configuration file**: `server/services/order-service/template.yaml` ```yaml GetOrdersFunction: Type: AWS::Serverless::Function - DependsOn: OrderFunctionExecutionRole Properties: - CodeUri: OrderService/ + CodeUri: src/ Handler: order_service.get_orders Tracing: Active Role: !GetAtt OrderFunctionExecutionRole.Arn - ReservedConcurrentExecutions: !If [IsPooledDeploy, !Ref "AWS::NoValue" , !Ref "AWS::NoValue"] + ReservedConcurrentExecutions: !If [IsPooledDeploy, !Ref "AWS::NoValue" , !Ref LambdaReserveConcurrency] Layers: - !Ref ServerlessSaaSLayers Environment: @@ -256,7 +253,7 @@ GetOrdersFunction: **Key configuration**: - **Function name**: `GetOrdersFunction` -- **Code path**: `OrderService/` +- **Code path**: `src/` - **Handler function**: `order_service.get_orders` - **Runtime**: `python3.13` (global configuration) - **IAM role**: `OrderFunctionExecutionRole` @@ -264,16 +261,14 @@ GetOrdersFunction: #### 3. Lambda Permission Configuration -**Configuration file**: `server/tenant-template.yaml` +**Configuration file**: `server/services/tenant-api/template.yaml` ```yaml GetOrdersLambdaApiGatewayExecutionPermission: Type: AWS::Lambda::Permission Properties: Action: lambda:InvokeFunction - FunctionName: !GetAtt - - GetOrdersFunction - - Arn + FunctionName: !Ref GetOrdersFunctionArn Principal: apigateway.amazonaws.com SourceArn: !Join [ "", [ @@ -293,7 +288,7 @@ GetOrdersLambdaApiGatewayExecutionPermission: #### 4. IAM Role and Policy -**Configuration file**: `server/tenant-template.yaml` +**Configuration file**: `server/services/order-service/template.yaml` ```yaml OrderFunctionExecutionRole: @@ -378,12 +373,12 @@ OrderTable: | Component | Configuration File | Key Configuration | Purpose | |------|----------|----------|------| -| **Tenant API Gateway** | `tenant-template.yaml` | `ApiGatewayTenantApi` | Creates a tenant-dedicated API gateway | -| **API endpoint definition** | `tenant-template.yaml` | `DefinitionBody.paths` | Defines the specific API routes | -| **Lambda function** | `tenant-template.yaml` | `GetOrdersFunction` | Handles business logic | -| **Permission control** | `tenant-template.yaml` | `GetOrdersLambdaApiGatewayExecutionPermission` | API Gateway invocation permission | -| **IAM role** | `tenant-template.yaml` | `OrderFunctionExecutionRole` | Lambda execution permissions | -| **Data storage** | `tenant-template.yaml` | `OrderTable` | Order data storage | +| **Tenant API Gateway** | `services/tenant-api/template.yaml` | `ApiGatewayTenantApi` | Creates a tenant-dedicated API gateway | +| **API endpoint definition** | `services/tenant-api/template.yaml` | `DefinitionBody.paths` | Defines the specific API routes | +| **Lambda function** | `services/order-service/template.yaml` | `GetOrdersFunction` | Handles business logic | +| **Permission control** | `services/tenant-api/template.yaml` | `GetOrdersLambdaApiGatewayExecutionPermission` | API Gateway invocation permission | +| **IAM role** | `services/order-service/template.yaml` | `OrderFunctionExecutionRole` | Lambda execution permissions | +| **Data storage** | `services/order-service/template.yaml` | `OrderTable` | Order data storage | ### 🔄 Invocation Flow diff --git a/docs/THROTTLING_AND_MONITORING.md b/docs/THROTTLING_AND_MONITORING.md index 9e861ff..5d1da77 100644 --- a/docs/THROTTLING_AND_MONITORING.md +++ b/docs/THROTTLING_AND_MONITORING.md @@ -223,8 +223,8 @@ This design ensures that the multi-tenant SaaS platform can provide a differenti #### 📍 Definition Location -**File**: `server/tenant-template.yaml` -**Lines**: 375-384 +**File**: `server/services/tenant-api/template.yaml` +**Lines**: 109-119 #### 🔧 Metric Filter Configuration diff --git a/docs/zh-CN/API_CONFIGURATION.md b/docs/zh-CN/API_CONFIGURATION.md index a1a5acd..a2dc5d7 100644 --- a/docs/zh-CN/API_CONFIGURATION.md +++ b/docs/zh-CN/API_CONFIGURATION.md @@ -51,7 +51,7 @@ CreateTenantAdminUserFunction: Type: AWS::Serverless::Function DependsOn: CreateUserLambdaExecutionRole Properties: - CodeUri: ../TenantManagementService/ + CodeUri: ../tenant-management/ Handler: user-management.create_tenant_admin_user Runtime: python3.13 Role: !GetAtt CreateUserLambdaExecutionRole.Arn @@ -68,7 +68,7 @@ CreateTenantAdminUserFunction: **关键配置**: - **函数名**: `CreateTenantAdminUserFunction` -- **代码路径**: `../TenantManagementService/` +- **代码路径**: `../tenant-management/` - **处理函数**: `user-management.create_tenant_admin_user` - **运行时**: `python3.13` - **IAM角色**: `CreateUserLambdaExecutionRole` @@ -175,7 +175,7 @@ Environment: #### 1. 租户API Gateway配置 -**配置文件**: `server/tenant-template.yaml` +**配置文件**: `server/services/tenant-api/template.yaml` ```yaml ApiGatewayTenantApi: @@ -211,11 +211,9 @@ ApiGatewayTenantApi: - api_key: [] # API密钥认证 - Authorizer: [] # Lambda授权器 x-amazon-apigateway-integration: - uri: !Join - - '' - - - !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/ - - !GetAtt GetOrdersFunction.Arn - - /invocations + uri: !Sub + - arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${FunctionArn}/invocations + - FunctionArn: !Ref GetOrdersFunctionArn httpMethod: POST type: aws_proxy ``` @@ -231,18 +229,17 @@ ApiGatewayTenantApi: #### 2. Lambda 函数定义 -**配置文件**: `server/tenant-template.yaml` +**配置文件**: `server/services/order-service/template.yaml` ```yaml GetOrdersFunction: Type: AWS::Serverless::Function - DependsOn: OrderFunctionExecutionRole Properties: - CodeUri: OrderService/ + CodeUri: src/ Handler: order_service.get_orders Tracing: Active Role: !GetAtt OrderFunctionExecutionRole.Arn - ReservedConcurrentExecutions: !If [IsPooledDeploy, !Ref "AWS::NoValue" , !Ref "AWS::NoValue"] + ReservedConcurrentExecutions: !If [IsPooledDeploy, !Ref "AWS::NoValue" , !Ref LambdaReserveConcurrency] Layers: - !Ref ServerlessSaaSLayers Environment: @@ -256,7 +253,7 @@ GetOrdersFunction: **关键配置**: - **函数名**: `GetOrdersFunction` -- **代码路径**: `OrderService/` +- **代码路径**: `src/` - **处理函数**: `order_service.get_orders` - **运行时**: `python3.13` (全局配置) - **IAM角色**: `OrderFunctionExecutionRole` @@ -264,16 +261,14 @@ GetOrdersFunction: #### 3. Lambda 权限配置 -**配置文件**: `server/tenant-template.yaml` +**配置文件**: `server/services/tenant-api/template.yaml` ```yaml GetOrdersLambdaApiGatewayExecutionPermission: Type: AWS::Lambda::Permission Properties: Action: lambda:InvokeFunction - FunctionName: !GetAtt - - GetOrdersFunction - - Arn + FunctionName: !Ref GetOrdersFunctionArn Principal: apigateway.amazonaws.com SourceArn: !Join [ "", [ @@ -293,7 +288,7 @@ GetOrdersLambdaApiGatewayExecutionPermission: #### 4. IAM 角色和策略 -**配置文件**: `server/tenant-template.yaml` +**配置文件**: `server/services/order-service/template.yaml` ```yaml OrderFunctionExecutionRole: @@ -378,12 +373,12 @@ OrderTable: | 组件 | 配置文件 | 关键配置 | 作用 | |------|----------|----------|------| -| **租户API Gateway** | `tenant-template.yaml` | `ApiGatewayTenantApi` | 创建租户专用API网关 | -| **API端点定义** | `tenant-template.yaml` | `DefinitionBody.paths` | 定义具体的API路由 | -| **Lambda函数** | `tenant-template.yaml` | `GetOrdersFunction` | 业务逻辑处理 | -| **权限控制** | `tenant-template.yaml` | `GetOrdersLambdaApiGatewayExecutionPermission` | API Gateway调用权限 | -| **IAM角色** | `tenant-template.yaml` | `OrderFunctionExecutionRole` | Lambda执行权限 | -| **数据存储** | `tenant-template.yaml` | `OrderTable` | 订单数据存储 | +| **租户API Gateway** | `services/tenant-api/template.yaml` | `ApiGatewayTenantApi` | 创建租户专用API网关 | +| **API端点定义** | `services/tenant-api/template.yaml` | `DefinitionBody.paths` | 定义具体的API路由 | +| **Lambda函数** | `services/order-service/template.yaml` | `GetOrdersFunction` | 业务逻辑处理 | +| **权限控制** | `services/tenant-api/template.yaml` | `GetOrdersLambdaApiGatewayExecutionPermission` | API Gateway调用权限 | +| **IAM角色** | `services/order-service/template.yaml` | `OrderFunctionExecutionRole` | Lambda执行权限 | +| **数据存储** | `services/order-service/template.yaml` | `OrderTable` | 订单数据存储 | ### 🔄 调用流程 diff --git a/docs/zh-CN/THROTTLING_AND_MONITORING.md b/docs/zh-CN/THROTTLING_AND_MONITORING.md index 6b1a988..06d94e6 100644 --- a/docs/zh-CN/THROTTLING_AND_MONITORING.md +++ b/docs/zh-CN/THROTTLING_AND_MONITORING.md @@ -223,8 +223,8 @@ ThrottlingLimitExceeded: #### 📍 定义位置 -**文件**: `server/tenant-template.yaml` -**行号**: 375-384 +**文件**: `server/services/tenant-api/template.yaml` +**行号**: 109-119 #### 🔧 Metric Filter 配置 diff --git a/scripts/DEPLOYMENT_GUIDE.md b/scripts/DEPLOYMENT_GUIDE.md index 6f4e646..d7cc69d 100644 --- a/scripts/DEPLOYMENT_GUIDE.md +++ b/scripts/DEPLOYMENT_GUIDE.md @@ -1,218 +1,72 @@ -# SAM部署指南 - -## 使用单一配置文件的多环境配置 - -### 配置文件结构 - -```toml -version = 0.1 - -# 生产环境 -[prod] -[prod.deploy] -[prod.deploy.parameters] -stack_name = "saas-control-stack-prod" -parameter_overrides = "Environment=prod" - -# 开发环境 -[dev] -[dev.deploy] -[dev.deploy.parameters] -stack_name = "saas-control-stack-dev" -parameter_overrides = "Environment=dev" - -# 测试环境 -[stage] -[stage.deploy] -[stage.deploy.parameters] -stack_name = "saas-control-stack-stage" -parameter_overrides = "Environment=stage" -``` - -### 部署命令 +# 部署指南 -```bash -# 部署到生产环境 -sam deploy --config-env prod +本指南介绍 **AWS Serverless SaaS Quick Start** 的真实部署流程。整个部署由 +`scripts/` 目录下的脚本驱动;本项目只有一套环境(单一 `[default]` 配置), +不存在 prod/dev/stage 多环境,也没有 `deploy-all.sh`。 -# 部署到开发环境 -sam deploy --config-env dev +## 先决条件 -# 部署到测试环境 -sam deploy --config-env stage -``` +- **AWS CLI**:已安装并完成 `aws configure`,且默认区域设置为 `us-east-1`。 + (部署脚本通过 `aws configure get region` 读取该区域。) +- **AWS SAM CLI**:用于构建并部署后端两套栈。 +- **Docker**:正在运行,供 `sam build --use-container` 使用。 +- **Node.js**:20.19+ 或 22.12+(Angular 20 要求),用于构建三个前端应用及运行辅助脚本。 +- **Python 3.13**:后端语言;部署前脚本会用 `pylint` 校验 `server/` 下的 Python 代码。 +- **AWS CDK**:用于部署 Platinum(silo)租户的 `TenantPipeline`。 -## 环境配置差异 - -### 生产环境 (prod) -- Stack名称: `saas-control-stack-prod` -- S3前缀: `saas-control-prod` -- 参数: `Environment=prod` -- 确认变更集: `false` - -### 开发环境 (dev) -- Stack名称: `saas-control-stack-dev` -- S3前缀: `saas-control-dev` -- 参数: `Environment=dev` -- 确认变更集: `false` - -### 测试环境 (stage) -- Stack名称: `saas-control-stack-stage` -- S3前缀: `saas-control-stage` -- 参数: `Environment=stage` -- 确认变更集: `false` - -## 环境特定参数 - -### 在template.yaml中使用环境参数 - -```yaml -Parameters: - Environment: - Type: String - Default: prod - AllowedValues: - - prod - - dev - - stage - Description: Environment name - -Resources: - MyFunction: - Type: AWS::Serverless::Function - Properties: - FunctionName: !Sub "my-function-${Environment}" - Environment: - Variables: - ENVIRONMENT: !Ref Environment -``` - -### 条件资源 - -```yaml -Conditions: - IsProd: !Equals [!Ref Environment, prod] - IsDev: !Equals [!Ref Environment, dev] - -Resources: - # 只在生产环境创建 - ProdOnlyResource: - Type: AWS::S3::Bucket - Condition: IsProd - Properties: - BucketName: !Sub "prod-only-bucket-${Environment}" - - # 只在开发环境创建 - DevOnlyResource: - Type: AWS::S3::Bucket - Condition: IsDev - Properties: - BucketName: !Sub "dev-only-bucket-${Environment}" -``` - -## CI/CD集成 - -### GitHub Actions示例 - -```yaml -name: Deploy to Environment - -on: - push: - branches: - - main - - develop - - staging - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v1 - with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: us-east-1 - - - name: Deploy to environment - run: | - if [[ ${{ github.ref }} == 'refs/heads/main' ]]; then - ./scripts/deploy-all.sh prod - elif [[ ${{ github.ref }} == 'refs/heads/develop' ]]; then - ./scripts/deploy-all.sh dev - elif [[ ${{ github.ref }} == 'refs/heads/staging' ]]; then - ./scripts/deploy-all.sh stage - fi -``` - -### AWS CodePipeline示例 - -```yaml -version: 0.2 - -phases: - build: - commands: - - echo "Deploying to $ENVIRONMENT" - - ./scripts/deploy-all.sh $ENVIRONMENT -``` +> 详见 [`../CONTRIBUTING.md`](../CONTRIBUTING.md) 与 [`../README.md`](../README.md)。 -## 最佳实践 +## 部署步骤 -### 1. 环境隔离 -- 使用不同的Stack名称 -- 使用不同的S3前缀 -- 使用不同的参数值 - -### 2. 安全性 -- 生产环境使用更严格的IAM权限 -- 开发环境可以更宽松以便调试 - -### 3. 成本控制 -- 开发环境使用较小的实例 -- 生产环境使用适当的实例大小 - -### 4. 监控 -- 为每个环境设置不同的CloudWatch日志组 -- 使用不同的SNS主题进行通知 - -## 故障排除 - -### 常见问题 - -1. **Stack名称冲突** - ```bash - # 确保每个环境使用不同的Stack名称 - sam deploy --stack-name my-stack-prod - ``` - -2. **S3前缀冲突** - ```bash - # 使用环境特定的S3前缀 - sam deploy --s3-prefix my-app-prod - ``` - -3. **参数覆盖问题** - ```bash - # 确保参数值正确 - sam deploy --parameter-overrides Environment=prod - ``` - -### 调试命令 +进入 `scripts/` 目录后依次执行两个脚本: ```bash -# 查看当前配置 -sam config list - -# 验证模板 -sam validate - -# 查看变更集 -sam deploy --no-execute-changeset +cd scripts +./deployment.sh +./geturl.sh +``` -# 查看Stack状态 -aws cloudformation describe-stacks --stack-name my-stack-prod -``` \ No newline at end of file +### `./deployment.sh` + +一键完成整套部署,依次执行: + +1. 用 `pylint` 校验 `server/` 下的 Python 代码,出错则中止。 +2. 通过 `aws configure get region` 读取部署区域。 +3. **部署共享(控制平面)栈**:在 `server/shared` 下 `sam build` 后执行 + `sam deploy --config-file samconfig.toml`,栈名为 `saas-control-stack` + (S3 前缀 `saas-control`,单一 `[default]` 配置,不使用 `--config-env`)。 +4. **部署租户(应用平面)pooled 栈**:在 `server/services` 下 `sam build` 后 + 执行 `sam deploy --config-file samconfig.toml`,栈名为 `stack-pooled`。 +5. **部署 TenantPipeline**:在 `server/TenantPipeline` 下 `npm install`、 + `cdk bootstrap`、`cdk deploy`,用于 Platinum 层 silo 租户的自动化置备。 +6. 从 `saas-control-stack` 的输出读取三个前端站点的 S3 桶名与访问 URL。 +7. **构建并发布三个前端**:分别在 `client/Admin`、`client/Application`、 + `client/Landing` 下执行 `npm install && npm run build`,再 + `aws s3 sync dist/ s3://<对应桶>/ --delete`。 + +### `./geturl.sh` + +打印已部署站点的访问地址(Admin / Landing / App)。它会自动适配运行环境: + +- 在 Workshop Studio 中,从 CloudFormation Exports 读取 `Serverless-SaaS-*`。 +- 否则,从栈 `saas-control-stack` 的输出 + (`AdminAppSite` / `LandingApplicationSite` / `ApplicationSite`)读取。 + +## 辅助脚本 + +`scripts/` 目录下还提供以下辅助脚本: + +- **`serve-clients.sh`**:在本地分别用 4200/4201/4202 端口同时启动 Landing、 + Application、Admin 三个 Angular 应用,便于本地验证(Ctrl-C 全部停止)。 +- **`get-login-info.js`**:从 `saas-control-stack` 输出汇总登录信息 + (Admin 站点 URL、AdminApi、Cognito 用户池/客户端 ID)及默认凭据。 +- **`create-admin-user.js`**:交互式在运营用户池中创建一个新的管理员用户 + (用户名/邮箱/密码),并设置为永久密码。 +- **`set-admin-password.js`**:交互式为默认 `admin` 用户设置新的永久密码。 +- **`manage-users.js`**:交互式用户管理菜单(列出/创建/改密/删除用户、查看登录信息)。 +- **`local-test-product.sh`**:完全本地、零 AWS 账号的 product-service 测试, + 使用 Docker 版 DynamoDB Local 配合 `sam local invoke` 跑一遍 CRUD。 +- **`test-basic-tier-throttling.sh`**:向 `stack-pooled` 的 `TenantAPI` 并发发起 + 大量 `/products` 请求,用于验证 Basic 层的 API 限流。 +- **`generate-env-config.js`**:从 `saas-control-stack` 的 `AdminApi` 输出生成 + 指定前端应用的 `environment.ts`。 diff --git a/scripts/generate-env-config.js b/scripts/generate-env-config.js index 13763de..4ff132f 100644 --- a/scripts/generate-env-config.js +++ b/scripts/generate-env-config.js @@ -18,27 +18,18 @@ if (!projectName) { console.log(`Generating environment config for ${projectName} (${environment})...`); try { - // 获取API Gateway URL - let apiGatewayUrl = ''; - - if (environment === 'prod') { - // 查询生产环境的API Gateway - const result = execSync('aws apigateway get-rest-apis --query "items[?name==\'saas-quickstart-shared\'].id" --output text', { encoding: 'utf8' }); - const apiId = result.trim(); - if (apiId) { - apiGatewayUrl = `https://${apiId}.execute-api.us-east-1.amazonaws.com/prod`; - } - } else { - // 查询开发环境的API Gateway - const result = execSync('aws apigateway get-rest-apis --query "items[?name==\'saas-quickstart-shared-dev\'].id" --output text', { encoding: 'utf8' }); - const apiId = result.trim(); - if (apiId) { - apiGatewayUrl = `https://${apiId}.execute-api.us-east-1.amazonaws.com/dev`; - } - } - - if (!apiGatewayUrl) { - console.error('Could not find API Gateway URL'); + // 从共享(控制平面)栈 saas-control-stack 读取 AdminApi 输出。 + // 该输出本身就是完整的 API Gateway URL,例如: + // https://.execute-api.us-east-1.amazonaws.com/prod + const result = execSync( + 'aws cloudformation describe-stacks --stack-name saas-control-stack --query "Stacks[0].Outputs[?OutputKey==\'AdminApi\'].OutputValue" --output text', + { encoding: 'utf8' } + ); + const apiGatewayUrl = result.trim(); + + if (!apiGatewayUrl || apiGatewayUrl === 'None') { + console.error('Could not find API Gateway URL (AdminApi output) on stack saas-control-stack'); + console.error('Make sure the shared stack is deployed (cd scripts && ./deployment.sh)'); process.exit(1); }