diff --git a/.dockerignore b/.dockerignore index f40afbfe..9d50837e 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,66 +1,17 @@ -.env -build/ -dist/ - -# Created by https://www.toptal.com/developers/gitignore/api/react,macos - -# Edit at https://www.toptal.com/developers/gitignore?templates=react,macos - -### macOS - -# General - -**/.DS_Store -**/.AppleDouble -\*\*/.LSOverride - -# Icon must end with two \r - -\*\*/Icon - -# Thumbnails - -\*\_/.\_\_ - -# Files that might appear in the root of a volume - -**/.DocumentRevisions-V100 -**/.fseventsd -**/.Spotlight-V100 -**/.TemporaryItems -**/.Trashes -**/.VolumeIcon.icns -\*\*/.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share - -**/.AppleDB -**/.AppleDesktop -**/Network Trash Folder -**/Temporary Items -\*\*/.apdisk - -### macOS Patch - -# iCloud generated files - -\*_/_.icloud - -### react - -**/.DS\_\* -**/_.log -**/logs -**/\*\*/_.backup._ -**/**/_.back.\* - -**/node_modules -**/bower_components - -\*_/_.sublime\* - -**/psd -**/thumb -\*\*/sketch - -# End of https://www.toptal.com/developers/gitignore/api/react,macos +.git/ +.github/ +.husky/ +cdk.out/ +deploy/ +.dockerignore +.eslintrc +.gitignore +.lintstagedrc +.prettierignore +.prettierrc +cdk.json + +**/dist/ +**/node_modules/ +# **/.env* +**/README.md diff --git a/.eslintrc b/.eslintrc index cf8306ee..ade5f3f3 100644 --- a/.eslintrc +++ b/.eslintrc @@ -28,7 +28,12 @@ "plugin:react-hooks/recommended", "plugin:@tanstack/eslint-plugin-query/recommended" ], - "ignorePatterns": ["node_modules/**/*", "**/build/**/*", "**/dist/**/*"], + "ignorePatterns": [ + "cdk.out/**/*", + "node_modules/**/*", + "**/build/**/*", + "**/dist/**/*" + ], "rules": { "@typescript-eslint/consistent-type-imports": "error", "react/react-in-jsx-scope": "off" diff --git a/.github/workflows/deploy-api.yml b/.github/workflows/deploy-api.yml new file mode 100644 index 00000000..c0e4c4d8 --- /dev/null +++ b/.github/workflows/deploy-api.yml @@ -0,0 +1,58 @@ +name: Deploy Login +on: + workflow_dispatch: + # inputs: + # environment: + # type: choice + # description: Select environment + # options: + # - development + # - production +env: + AWS_REGION: us-west-2 + AWS_ROLE_ARN: arn:aws:iam::665230337498:role/tdk-github-actions-role + ECR_REPOSITORY: tdk-ecr-stack-apic8550315-hzfii5kpayda + ECS_CLUSTER: tdk-api-stack-cluster611F8AFF-3mpR3WcbH0ni + ECS_SERVICE: tdk-api-stack-serviceService8587F09F-PDvooGO58iWa + ECS_TASK_DEFINITION: ./packages/api/.aws/task-definition.json + CONTAINER_NAME: web +permissions: + id-token: write + contents: read +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + role-to-assume: ${{ env.AWS_ROLE_ARN }} + aws-region: ${{ env.AWS_REGION }} + - name: Log in to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@62f4f872db3836360b72999f4b87f1ff13310f3a + - name: Build, tag, and push image to Amazon ECR + id: build-image + env: + ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} + IMAGE_TAG: ${{ github.sha }} + run: | + docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . + docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG + echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT + - name: Fill in the new image ID in the Amazon ECS task definition + id: task-def + uses: aws-actions/amazon-ecs-render-task-definition@c804dfbdd57f713b6c079302a4c01db7017a36fc + with: + task-definition: ${{ env.ECS_TASK_DEFINITION }} + container-name: ${{ env.CONTAINER_NAME }} + image: ${{ steps.build-image.outputs.image }} + - name: Deploy Amazon ECS task definition + uses: aws-actions/amazon-ecs-deploy-task-definition@df9643053eda01f169e64a0e60233aacca83799a + with: + task-definition: ${{ steps.task-def.outputs.task-definition }} + service: ${{ env.ECS_SERVICE }} + cluster: ${{ env.ECS_CLUSTER }} + wait-for-service-stability: true diff --git a/.github/workflows/deploy-login.yml b/.github/workflows/deploy-login.yml index ddccf0b0..4b473a49 100644 --- a/.github/workflows/deploy-login.yml +++ b/.github/workflows/deploy-login.yml @@ -10,28 +10,19 @@ on: - production jobs: deploy: - name: Deploy Login runs-on: ubuntu-latest steps: - - name: Cancel previous runs - uses: styfle/cancel-workflow-action@0.11.0 - name: Checkout repo - uses: actions/checkout@v3 - - name: Read app name - uses: SebRollen/toml-action@v1.0.0 - id: app_name - with: - file: "fly.login.toml" - field: "app" - - name: Setup deploy + uses: actions/checkout@v4 + - name: Setup flyctl uses: superfly/flyctl-actions/setup-flyctl@master - name: Deploy development if: ${{ inputs.environment == 'development' }} - run: flyctl deploy --config ./fly.login.toml --app ${{ steps.app_name.outputs.value }}-dev --remote-only --build-secret dotenv="${{ secrets.ENV_DEV }}" + run: flyctl deploy --config ./packages/login/fly.toml --app tdk-login-dev --remote-only --build-secret dotenv="${{ secrets.ENV_DEV }}" env: FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} - name: Deploy production if: ${{ inputs.environment == 'production' }} - run: flyctl deploy --config ./fly.login.toml --remote-only --build-secret dotenv="${{ secrets.ENV_PROD }}" + run: flyctl deploy --config ./packages/login/fly.toml --remote-only --build-secret dotenv="${{ secrets.ENV_PROD }}" env: FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} diff --git a/.gitignore b/.gitignore index 421265c4..7e6566bb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,218 @@ -node_modules/ -dist/ +cdk.out/ + +### macOS ### +# General .DS_Store -.vscode -.idea -.cache -.env +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +### Node Patch ### +# Serverless Webpack directories +.webpack/ + +# Optional stylelint cache + +# SvelteKit build / generate output +.svelte-kit + +### react ### +.DS_* +**/*.backup.* +**/*.back.* + +node_modules + +*.sublime* + +psd +thumb +sketch + +### Remix ### +.cache +build/ +public/build/ + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk diff --git a/.prettierignore b/.prettierignore index 2b011975..4cc5c18d 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,7 +1,10 @@ .husky/ build/ +cdk.out/ dist/ node_modules/ +.dockerignore +.eslintrc .gitignore .prettierignore package-lock.json \ No newline at end of file diff --git a/cdk.json b/cdk.json new file mode 100644 index 00000000..fcde7458 --- /dev/null +++ b/cdk.json @@ -0,0 +1,58 @@ +{ + "app": "npx ts-node --prefer-ts-exts ./deploy/cdk/bin/cdk.ts", + "watch": { + "include": ["./deploy/cdk/**"], + "exclude": [ + "README.md", + "cdk*.json", + "**/*.d.ts", + "**/*.js", + "tsconfig.json", + "package*.json", + "node_modules", + "test" + ] + }, + "context": { + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/core:target-partitions": ["aws", "aws-cn"], + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, + "@aws-cdk/core:enablePartitionLiterals": true, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, + "@aws-cdk/aws-iam:standardizedServicePrincipals": true, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, + "@aws-cdk/aws-route53-patters:useCertificate": true, + "@aws-cdk/customresources:installLatestAwsSdkDefault": false, + "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, + "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, + "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, + "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, + "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, + "@aws-cdk/aws-redshift:columnId": true, + "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, + "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, + "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, + "@aws-cdk/aws-kms:aliasNameRef": true, + "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true, + "@aws-cdk/core:includePrefixInUniqueNameGeneration": true, + "@aws-cdk/aws-efs:denyAnonymousAccess": true, + "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true, + "@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true, + "@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true, + "@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true, + "@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true, + "@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true, + "@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true + } +} diff --git a/deploy/cdk/bin/cdk.ts b/deploy/cdk/bin/cdk.ts new file mode 100644 index 00000000..d3b4055a --- /dev/null +++ b/deploy/cdk/bin/cdk.ts @@ -0,0 +1,12 @@ +#!/usr/bin/env node +import * as cdk from "aws-cdk-lib"; +import "source-map-support/register"; + +import { TdkApiStack } from "../lib/tdk-api-stack"; +import { TdkEcrStack } from "../lib/tdk-ecr-stack"; + +const app = new cdk.App(); + +const ecrStack = new TdkEcrStack(app, "tdk-ecr-stack"); + +new TdkApiStack(app, "tdk-api-stack", { ecrRepo: ecrStack.repo }); diff --git a/deploy/cdk/lib/tdk-api-stack.ts b/deploy/cdk/lib/tdk-api-stack.ts new file mode 100644 index 00000000..9b3c671b --- /dev/null +++ b/deploy/cdk/lib/tdk-api-stack.ts @@ -0,0 +1,48 @@ +import * as cdk from "aws-cdk-lib"; +import { Certificate } from "aws-cdk-lib/aws-certificatemanager"; +import * as ec2 from "aws-cdk-lib/aws-ec2"; +import type * as ecr from "aws-cdk-lib/aws-ecr"; +import * as ecs from "aws-cdk-lib/aws-ecs"; +import * as ecs_patterns from "aws-cdk-lib/aws-ecs-patterns"; +import type { Construct } from "constructs"; + +interface StackProps extends cdk.StackProps { + ecrRepo: ecr.Repository; +} + +export class TdkApiStack extends cdk.Stack { + constructor(scope: Construct, id: string, props: StackProps) { + super(scope, id, props); + + const cluster = new ecs.Cluster(this, "cluster", { + vpc: new ec2.Vpc(this, "vpc", { + maxAzs: 2, + }), + }); + + const service = new ecs_patterns.ApplicationLoadBalancedFargateService( + this, + "service", + { + cluster, + cpu: 512, + desiredCount: 2, + taskImageOptions: { + image: ecs.ContainerImage.fromEcrRepository(props.ecrRepo), + containerPort: 8080, + }, + memoryLimitMiB: 2048, + publicLoadBalancer: true, + certificate: Certificate.fromCertificateArn( + this, + "CertificateImported", + "arn:aws:acm:us-west-2:665230337498:certificate/48316235-afa5-4ad7-91e1-b429f1da54e2", + ), + }, + ); + + service.targetGroup.configureHealthCheck({ + path: "/healthcheck", + }); + } +} diff --git a/deploy/cdk/lib/tdk-ecr-stack.ts b/deploy/cdk/lib/tdk-ecr-stack.ts new file mode 100644 index 00000000..26d18abf --- /dev/null +++ b/deploy/cdk/lib/tdk-ecr-stack.ts @@ -0,0 +1,69 @@ +import * as cdk from "aws-cdk-lib"; +import * as ecr from "aws-cdk-lib/aws-ecr"; +import * as iam from "aws-cdk-lib/aws-iam"; +import type { Construct } from "constructs"; + +export class TdkEcrStack extends cdk.Stack { + public readonly repo: ecr.Repository; + + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + this.repo = new ecr.Repository(this, "api"); + + const githubProvider = new iam.OpenIdConnectProvider( + this, + "githubProvider", + { + url: "https://token.actions.githubusercontent.com", + clientIds: ["sts.amazonaws.com"], + }, + ); + + const githubPrincipal = new iam.OpenIdConnectPrincipal( + githubProvider, + ).withConditions({ + StringLike: { + "token.actions.githubusercontent.com:sub": + "repo:treasureproject/treasure.js:*", + }, + StringEquals: { + "token.actions.githubusercontent.com:aud": "sts.amazonaws.com", + }, + }); + + const githubActionsRole = new iam.Role(this, "githubActionsRole", { + assumedBy: githubPrincipal, + description: "Role assumed by GitHub Actions for deploying to stack", + roleName: "tdk-github-actions-role", + maxSessionDuration: cdk.Duration.hours(1), + inlinePolicies: { + EcrPushPolicy: new iam.PolicyDocument({ + assignSids: true, + statements: [ + new iam.PolicyStatement({ + effect: iam.Effect.ALLOW, + actions: [ + "ecr:CompleteLayerUpload", + "ecr:GetAuthorizationToken", + "ecr:UploadLayerPart", + "ecr:InitiateLayerUpload", + "ecr:BatchCheckLayerAvailability", + "ecr:PutImage", + ], + resources: ["*"], + }), + ], + }), + }, + }); + + new cdk.CfnOutput(this, "repositoryUri", { + value: this.repo.repositoryUri, + }); + + new cdk.CfnOutput(this, "githubActionsRoleArn", { + value: githubActionsRole.roleArn, + }); + } +} diff --git a/deploy/cdk/package.json b/deploy/cdk/package.json new file mode 100644 index 00000000..10406c1f --- /dev/null +++ b/deploy/cdk/package.json @@ -0,0 +1,27 @@ +{ + "name": "cdk", + "version": "0.1.0", + "bin": { + "cdk": "bin/cdk.js" + }, + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "test": "jest", + "cdk": "cdk" + }, + "devDependencies": { + "@types/jest": "^29.5.11", + "@types/node": "20.11.5", + "jest": "^29.7.0", + "ts-jest": "^29.1.1", + "aws-cdk": "2.122.0", + "ts-node": "^10.9.2", + "typescript": "~5.3.3" + }, + "dependencies": { + "aws-cdk-lib": "2.122.0", + "constructs": "^10.0.0", + "source-map-support": "^0.5.21" + } +} diff --git a/deploy/cdk/tsconfig.json b/deploy/cdk/tsconfig.json new file mode 100644 index 00000000..cbeda969 --- /dev/null +++ b/deploy/cdk/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": ["es2020", "dom"], + "declaration": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": false, + "inlineSourceMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false, + "typeRoots": ["./node_modules/@types"], + }, + "exclude": ["node_modules", "cdk.out"], +} diff --git a/examples/harvester/.gitignore b/examples/harvester/.gitignore deleted file mode 100644 index a547bf36..00000000 --- a/examples/harvester/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -dist -dist-ssr -*.local - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? diff --git a/examples/payments/.gitignore b/examples/payments/.gitignore deleted file mode 100644 index a547bf36..00000000 --- a/examples/payments/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -dist -dist-ssr -*.local - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? diff --git a/package-lock.json b/package-lock.json index e0e23cba..cb91b17c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,10 @@ "@types/node": "^20.8.2", "@typescript-eslint/eslint-plugin": "^6.7.4", "@typescript-eslint/parser": "^6.7.4", + "aws-cdk": "^2.124.0", + "aws-cdk-lib": "^2.124.0", "concurrently": "^8.2.1", + "constructs": "^10.3.0", "esbuild-register": "^3.5.0", "eslint": "^8.50.0", "eslint-plugin-prettier": "^5.0.0", @@ -25,6 +28,7 @@ "prettier": "^3.0.3", "prettier-plugin-prisma": "^5.0.0", "prettier-plugin-tailwindcss": "^0.5.5", + "source-map-support": "^0.5.21", "tsup": "^8.0.1", "typescript": "^5.2.2", "vitest": "^1.0.1" @@ -118,6 +122,24 @@ "node": ">=6.0.0" } }, + "node_modules/@aws-cdk/asset-awscli-v1": { + "version": "2.2.202", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.202.tgz", + "integrity": "sha512-JqlF0D4+EVugnG5dAsNZMqhu3HW7ehOXm5SDMxMbXNDMdsF0pxtQKNHRl52z1U9igsHmaFpUgSGjbhAJ+0JONg==", + "dev": true + }, + "node_modules/@aws-cdk/asset-kubectl-v20": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.2.tgz", + "integrity": "sha512-3M2tELJOxQv0apCIiuKQ4pAbncz9GuLwnKFqxifWfe77wuMxyTRPmxssYHs42ePqzap1LT6GDcPygGs+hHstLg==", + "dev": true + }, + "node_modules/@aws-cdk/asset-node-proxy-agent-v6": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v6/-/asset-node-proxy-agent-v6-2.0.1.tgz", + "integrity": "sha512-DDt4SLdLOwWCjGtltH4VCST7hpOI5DzieuhGZsBpZ+AgJdSI2GCjklCXm0GCTwJG/SolkL5dtQXyUKgg9luBDg==", + "dev": true + }, "node_modules/@babel/code-frame": { "version": "7.23.5", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", @@ -9211,6 +9233,410 @@ "fastq": "^1.6.1" } }, + "node_modules/aws-cdk": { + "version": "2.124.0", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.124.0.tgz", + "integrity": "sha512-kUOfqwIAaTEx4ZozojZEhWa8G+O9KU+P0tERtDVmTw9ip4QXNMwTTkjj/IPtoH8qfXGdeibTQ9MJwRvHOR8kXQ==", + "dev": true, + "bin": { + "cdk": "bin/cdk" + }, + "engines": { + "node": ">= 14.15.0" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/aws-cdk-lib": { + "version": "2.124.0", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.124.0.tgz", + "integrity": "sha512-K/Tey8TMw30GO6UD0qb19CPhBMZhleGshz520ZnbDUJwNfFtejwZOnpmRMOdUP9f4tHc5BrXl1VGsZtXtUaGhg==", + "bundleDependencies": [ + "@balena/dockerignore", + "case", + "fs-extra", + "ignore", + "jsonschema", + "minimatch", + "punycode", + "semver", + "table", + "yaml" + ], + "dev": true, + "dependencies": { + "@aws-cdk/asset-awscli-v1": "^2.2.202", + "@aws-cdk/asset-kubectl-v20": "^2.1.2", + "@aws-cdk/asset-node-proxy-agent-v6": "^2.0.1", + "@balena/dockerignore": "^1.0.2", + "case": "1.6.3", + "fs-extra": "^11.2.0", + "ignore": "^5.3.0", + "jsonschema": "^1.4.1", + "minimatch": "^3.1.2", + "punycode": "^2.3.1", + "semver": "^7.5.4", + "table": "^6.8.1", + "yaml": "1.10.2" + }, + "engines": { + "node": ">= 14.15.0" + }, + "peerDependencies": { + "constructs": "^10.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/@balena/dockerignore": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/aws-cdk-lib/node_modules/ajv": { + "version": "8.12.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/aws-cdk-lib/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/aws-cdk-lib/node_modules/astral-regex": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/aws-cdk-lib/node_modules/case": { + "version": "1.6.3", + "dev": true, + "inBundle": true, + "license": "(MIT OR GPL-3.0-or-later)", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/fast-deep-equal": { + "version": "3.1.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/fs-extra": { + "version": "11.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/aws-cdk-lib/node_modules/graceful-fs": { + "version": "4.2.11", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/aws-cdk-lib/node_modules/ignore": { + "version": "5.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/aws-cdk-lib/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/json-schema-traverse": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/jsonfile": { + "version": "6.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/aws-cdk-lib/node_modules/jsonschema": { + "version": "1.4.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/aws-cdk-lib/node_modules/lodash.truncate": { + "version": "4.4.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/aws-cdk-lib/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/aws-cdk-lib/node_modules/punycode": { + "version": "2.3.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/aws-cdk-lib/node_modules/require-from-string": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/semver": { + "version": "7.5.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/aws-cdk-lib/node_modules/slice-ansi": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/aws-cdk-lib/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/table": { + "version": "6.8.1", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/universalify": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/uri-js": { + "version": "4.4.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/aws-cdk-lib/node_modules/yaml": { + "version": "1.10.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/aws-cdk/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/axios": { "version": "0.27.2", "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", @@ -10534,6 +10960,15 @@ "node": "^14.18.0 || >=16.10.0" } }, + "node_modules/constructs": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.3.0.tgz", + "integrity": "sha512-vbK8i3rIb/xwZxSpTjz3SagHn1qq9BChLEfy5Hf6fB3/2eFbrwt2n9kHwQcS0CPTRBesreeAcsJfMq2229FnbQ==", + "dev": true, + "engines": { + "node": ">= 16.14.0" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", diff --git a/package.json b/package.json index bcf97f5f..f69aac41 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,8 @@ "dev:react": "npm run dev --prefix ./packages/react", "dev:examples:harvester": "npm run dev --prefix ./examples/harvester", "dev:examples:payments": "npm run dev --prefix ./examples/payments", + "generate:api": "npm run generate --prefix ./packages/api", + "start:api": "npm start --prefix ./packages/api", "start:login": "npm start --prefix ./packages/login", "typecheck": "tsc --noEmit", "format": "eslint --fix . && prettier --write --ignore-unknown .", @@ -29,7 +31,10 @@ "@types/node": "^20.8.2", "@typescript-eslint/eslint-plugin": "^6.7.4", "@typescript-eslint/parser": "^6.7.4", + "aws-cdk": "^2.124.0", + "aws-cdk-lib": "^2.124.0", "concurrently": "^8.2.1", + "constructs": "^10.3.0", "esbuild-register": "^3.5.0", "eslint": "^8.50.0", "eslint-plugin-prettier": "^5.0.0", @@ -40,6 +45,7 @@ "prettier": "^3.0.3", "prettier-plugin-prisma": "^5.0.0", "prettier-plugin-tailwindcss": "^0.5.5", + "source-map-support": "^0.5.21", "tsup": "^8.0.1", "typescript": "^5.2.2", "vitest": "^1.0.1" diff --git a/packages/api/.aws/task-definition.json b/packages/api/.aws/task-definition.json new file mode 100644 index 00000000..35bc8a93 --- /dev/null +++ b/packages/api/.aws/task-definition.json @@ -0,0 +1,86 @@ +{ + "taskDefinitionArn": "arn:aws:ecs:us-west-2:665230337498:task-definition/tdkapistackserviceTaskDef68FC0C20:4", + "containerDefinitions": [ + { + "name": "web", + "image": "", + "cpu": 0, + "links": [], + "portMappings": [ + { + "containerPort": 8080, + "hostPort": 8080, + "protocol": "tcp" + } + ], + "essential": true, + "entryPoint": [], + "command": [], + "environment": [], + "environmentFiles": [], + "mountPoints": [], + "volumesFrom": [], + "secrets": [], + "dnsServers": [], + "dnsSearchDomains": [], + "extraHosts": [], + "dockerSecurityOptions": [], + "dockerLabels": {}, + "ulimits": [], + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-group": "tdk-api-stack-serviceTaskDefwebLogGroupCCBA392E-145mEt6ksIvG", + "awslogs-region": "us-west-2", + "awslogs-stream-prefix": "service" + }, + "secretOptions": [] + }, + "systemControls": [] + } + ], + "family": "tdkapistackserviceTaskDef68FC0C20", + "taskRoleArn": "arn:aws:iam::665230337498:role/tdk-api-stack-serviceTaskDefTaskRole43CA7BBB-HqfUrnxiMwHW", + "executionRoleArn": "arn:aws:iam::665230337498:role/tdk-api-stack-serviceTaskDefExecutionRole39FD5935-RGxFnR8ryRxY", + "networkMode": "awsvpc", + "revision": 4, + "volumes": [], + "status": "ACTIVE", + "requiresAttributes": [ + { + "name": "com.amazonaws.ecs.capability.logging-driver.awslogs" + }, + { + "name": "ecs.capability.execution-role-awslogs" + }, + { + "name": "com.amazonaws.ecs.capability.ecr-auth" + }, + { + "name": "com.amazonaws.ecs.capability.docker-remote-api.1.19" + }, + { + "name": "com.amazonaws.ecs.capability.docker-remote-api.1.17" + }, + { + "name": "com.amazonaws.ecs.capability.task-iam-role" + }, + { + "name": "ecs.capability.execution-role-ecr-pull" + }, + { + "name": "com.amazonaws.ecs.capability.docker-remote-api.1.18" + }, + { + "name": "ecs.capability.task-eni" + } + ], + "placementConstraints": [], + "compatibilities": ["EC2", "FARGATE"], + "requiresCompatibilities": ["FARGATE"], + "cpu": "512", + "memory": "2048", + "registeredAt": "2024-02-01T18:27:14.987Z", + "registeredBy": "arn:aws:sts::665230337498:assumed-role/cdk-hnb659fds-cfn-exec-role-665230337498-us-west-2/AWSCloudFormation", + "tags": [] +} diff --git a/packages/api/Dockerfile b/packages/api/Dockerfile new file mode 100644 index 00000000..d153491d --- /dev/null +++ b/packages/api/Dockerfile @@ -0,0 +1,32 @@ +# syntax = docker/dockerfile:1 + +FROM --platform=linux/amd64 node:20-slim as base + +# Set working directory +WORKDIR /app + +# Set production environment +ENV NODE_ENV="production" + +# Install packages needed to build node modules +RUN apt-get update -qq && \ + apt-get install -y build-essential pkg-config python-is-python3 + +# Copy application code +COPY . . + +# Install dependencies +RUN npm install --include=dev + +# Generate code +RUN npm run generate:api + +# Build application +RUN npm run build:api + +# Remove development dependencies +RUN npm prune --omit=dev + +# Start the server +EXPOSE 8080 +CMD [ "npm", "run", "start:api" ] diff --git a/packages/api/package.json b/packages/api/package.json index 8b412059..328f5867 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -6,6 +6,7 @@ "types": "./dist/sdk.d.ts", "scripts": { "build": "tsup", + "generate": "prisma generate", "dev": "npm run build && concurrently \"npm run build -- --watch\" \"nodemon -w ./dist/index.js ./dist/index.js\"", "start": "node ./dist/index.js", "db:seed": "npx ts-node --compiler-options '{\"module\":\"CommonJS\"}' ./prisma/seed.ts", diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts index be0b3a19..ec2c3450 100644 --- a/packages/api/src/index.ts +++ b/packages/api/src/index.ts @@ -34,7 +34,7 @@ const main = async () => { // Start server await app.ready(); - app.listen({ port: env.PORT }, (err, address) => { + app.listen({ host: "0.0.0.0", port: env.PORT }, (err, address) => { if (err) { console.error("Error starting server:", err); process.exit(1); diff --git a/packages/login/.gitignore b/packages/login/.gitignore deleted file mode 100644 index 3f7bf98d..00000000 --- a/packages/login/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -node_modules - -/.cache -/build -/public/build -.env diff --git a/Dockerfile.login b/packages/login/Dockerfile similarity index 84% rename from Dockerfile.login rename to packages/login/Dockerfile index 53c0ee27..b3c0dcc0 100644 --- a/Dockerfile.login +++ b/packages/login/Dockerfile @@ -1,7 +1,6 @@ # syntax = docker/dockerfile:1 -ARG NODE_VERSION=20.11.0 -FROM node:${NODE_VERSION}-slim as base +FROM node:20-slim as base LABEL fly_launch_runtime="Remix" @@ -16,7 +15,7 @@ RUN apt-get update -qq && \ apt-get install -y build-essential pkg-config python-is-python3 # Copy application code -COPY --link . . +COPY . . # Install dependencies RUN npm install --include=dev @@ -33,4 +32,4 @@ RUN npm prune --omit=dev # Start the server EXPOSE 3000 -CMD [ "npm", "run", "start:login" ] +CMD [ "npm", "run", "start:login" ] \ No newline at end of file diff --git a/packages/login/app/routes/healthcheck.tsx b/packages/login/app/routes/healthcheck.tsx index c5106b99..289e27fa 100644 --- a/packages/login/app/routes/healthcheck.tsx +++ b/packages/login/app/routes/healthcheck.tsx @@ -1,17 +1 @@ -import type { LoaderFunctionArgs } from "@remix-run/node"; - -export const loader = async ({ request }: LoaderFunctionArgs) => { - const host = - request.headers.get("X-Forwarded-Host") ?? request.headers.get("host"); - - try { - const url = new URL("/", `http://${host}`); - await fetch(url.toString(), { method: "HEAD" }).then((r) => { - if (!r.ok) return Promise.reject(r); - }); - return new Response("OK"); - } catch (error: unknown) { - console.log("healthcheck ❌", { error }); - return new Response("ERROR", { status: 500 }); - } -}; +export const loader = () => new Response("OK"); diff --git a/fly.login.toml b/packages/login/fly.toml similarity index 92% rename from fly.login.toml rename to packages/login/fly.toml index 1e2ac2c1..36430982 100644 --- a/fly.login.toml +++ b/packages/login/fly.toml @@ -2,7 +2,7 @@ app = "tdk-login" primary_region = "lax" [build] - dockerfile = "Dockerfile.login" + dockerfile = "Dockerfile" [http_service] internal_port = 3000