Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: Deploy

on:
push:
branches:
- main
workflow_dispatch:

env:
NODE_VERSION: 20

jobs:
build-and-deploy:
runs-on: ubuntu-latest

permissions:
id-token: write
contents: read

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm

- name: Install dependencies
run: npm install

- name: Build frontend and package Lambda
run: npm run build

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}

- name: Sync assets to S3
run: |
aws s3 sync web/dist "s3://${{ secrets.SITE_BUCKET_NAME }}" \
--delete \
--cache-control "public, max-age=31536000, immutable"

- name: Upload index.html with short cache
run: |
aws s3 cp web/dist/index.html "s3://${{ secrets.SITE_BUCKET_NAME }}/index.html" \
--cache-control "public, max-age=60" \
--content-type "text/html; charset=utf-8"

- name: Update Lambda function
run: |
aws lambda update-function-code \
--function-name "${{ secrets.LAMBDA_FUNCTION_NAME }}" \
--zip-file "fileb://api/lambda.zip"

- name: Invalidate CloudFront cache
run: |
aws cloudfront create-invalidation \
--distribution-id "${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }}" \
--paths "/*"

- name: Smoke test API
if: ${{ secrets.API_GATEWAY_ID != '' }}
env:
API_GATEWAY_ID: ${{ secrets.API_GATEWAY_ID }}
AWS_REGION: ${{ secrets.AWS_REGION }}
run: |
curl --fail "https://${API_GATEWAY_ID}.execute-api.${AWS_REGION}.amazonaws.com/helloworld"

27 changes: 27 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Node
node_modules/
*.log

# Frontend build
web/dist/

# API build artifacts
api/dist/
api/lambda.zip

# IDE
.DS_Store
Thumbs.db
.idea/
.vscode/

# Terraform
.terraform/
*.tfstate
*.tfstate.*
terraform.tfvars

# Environment
.env
.env.*

88 changes: 87 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,88 @@
# MemeOn
The website and service for MemeOn.ai

Foundational project for the MemeOn.ai marketing site and serverless API.

## Project layout

- `web/` – Vite-powered frontend that displays `Hello MemeOn.ai` and calls the serverless API.
- `api/` – TypeScript Lambda handler plus an Express dev server that serves `/api` routes on port `3001`, returning a JSON description (from `api/src/api-description.ts`) for any unimplemented path.
- `infra/terraform/` – Terraform configuration for S3, CloudFront, ACM, API Gateway, Lambda and Route53.
- `.github/workflows/` – CI/CD automation for build and deploy (see below).

## Prerequisites

- Node.js 20+
- npm 10+
- Terraform 1.8+
- AWS CLI v2 with credentials that can manage S3, CloudFront, Lambda, API Gateway, ACM and Route53.

## Local development

Install dependencies once:

```
npm install
```

Then launch both the Vite dev server and the API server locally:

```
npm run dev
```

The site runs on http://localhost:5173 and proxies `/api` requests to http://localhost:3001.

## Building

Build the frontend and package the Lambda bundle:

```
npm run build
```

The Lambda package is written to `api/lambda.zip` and is referenced by Terraform.

## Infrastructure

Terraform is organised under `infra/terraform`.

1. Generate the Lambda bundle: `npm run bundle:api`.
2. Ensure the S3 bucket `memeon.ai` exists. You can create it with the AWS CLI:
```
aws s3api create-bucket --bucket memeon.ai --region us-west-2 --create-bucket-configuration LocationConstraint=us-west-2
```
3. Initialise and plan:
```
cd infra/terraform
terraform init
terraform plan -var='route53_zone_id=Z123456789ABC'
```
4. Apply when ready:
```
terraform apply -var='route53_zone_id=Z123456789ABC'
```

The configuration provisions:

- S3 bucket for the static site with CORS set to mirror the Masky project.
- CloudFront distribution with behaviours for the site and `/api/*` requests.
- CloudFront function to redirect `www.memeon.ai` to `memeon.ai`.
- ACM certificate in `us-east-1` for `memeon.ai` and `www.memeon.ai`.
- API Gateway HTTP endpoint integrated with the `memeon-api` Lambda function.
- Route53 records for apex and `www`.

Certificate validation requires DNS. Terraform creates the CNAME records automatically in the hosted zone you supply.

## Deployment workflow

A GitHub Actions workflow (`.github/workflows/deploy.yml`) builds the frontend, uploads assets to S3, invalidates the CloudFront cache, and updates the Lambda function code using AWS CLI commands. Configure the following repository secrets before running the workflow:

- `AWS_ACCESS_KEY_ID`
- `AWS_SECRET_ACCESS_KEY`
- `AWS_REGION`
- `CLOUDFRONT_DISTRIBUTION_ID`
- `SITE_BUCKET_NAME`
- `LAMBDA_FUNCTION_NAME` (set to `memeon-api`)
- `API_GATEWAY_ID` *(optional – used for post-deploy smoke tests)*

Trigger the workflow with pushes to `main` or manually from the Actions tab.
Loading