Not static! HTML is rendered on-demand
Completely required for building an API
Database backed (full stack!)
Model
View
Controller
Model
View
Controller
3 tier logical architecture is not the same as the physical infra
Difficult to deploy when infra is not versioned
Slow rolling deployment
Servers are difficult to scale horizontally
Slow to render HTML because traditional databases are slow
Maintenance cost is huge and not fun (patching, updating, debugging, etc)
Background jobs are required for performance but a clunky bolt on
pub/sub, queues, cron
⭐⭐⭐⭐⭐
Pre-rendering HTML and served via CDN. Dynamic functionality initiated by JavaScript at runtime.
Authortime build of all presentation/business logic delivered as static assets
Empowers the frontend web developers
Slow build
Dynamic is a second class citizen
Complex ecosystem of libraries, tooling and services need glue
In conservation and energy economics, the rebound effect (or take-back effect) is the reduction in expected gains from new technologies that increase the efficiency of resource use, because of behavioral or other systemic responses. These responses diminish the beneficial effects of the new technology or other measures taken.
The expected gains from a new JavaScript framework is always less because of the amount of JavaScript being deployed on the client. This additional JavaScript diminishes the beneficial effects of the new technology and in some cases makes things worse.
HTML first (dynamic personalization and a11y is the priority)
Cloud function centric mode
On-demand database
Declarative deployment (explicitly defined Infra as Code)
Power: build with a full-stack
Way more fun! Less maintaining, more shipping
Inclusive, fast, and accessible web consumer experience
Coldstart *
Database story still emergent (DynamoDB, Cosmos, Planetscale, FaunaDB)
Declarative Infra-as-Code solutions are oft complex
All-in with managed services means all-in with a cloud vendor
* there are two solutions to coldstart for AWS Lambda: write small functions or pre-provision capacity
AWS SAM
Azure Functions
Begin
Cloudflare
Deno Deploy
GCP
A modern architectural pattern for dynamic apps:
HTML-first progressive enhancement
Cloud functions centered development model
Managed database built-in
Explicit declarative deployment (aka Infra-as-Code or IaC)
@app
arc-fnh
@static
fingerprint true
folder public
@http
get /
get /components/*
get /players
get /players/add
post /players
post /players/:id/delete
post /players/delete
get /games
get /games/add
get /games/:id
post /games
post /games/:id
post /games/:id/delete
post /games/delete
get /import
post /import
get /invite/:id
get /seasons
get /seasons/add
post /seasons
post /seasons/delete
post /seasons/:id/delete
@events
find-spares
send-email
@scheduled
weekly-reminder cron(0 8 ? * WED *)
weekly-roster cron(0 10 ? * FRI *)
@plugins
arc-plugin-oauth
architect/plugin-lambda-invoker
@oauth
use-mock true
allow-list allow.mjs
@tables
players
email *String
games
gamedate *String
seasons
seasonID *String
invites
inviteID *String
expiresAt TTL
@tables-indexes
players
fulltime *String
name playersByFulltime
games
gamedate *String
name gamesByDate
seasons
seasonID *String
name seasonsByID
# invites
# email *String
# name invitesByEmail
@aws
# profile default
region us-west-2
architecture arm64
runtime nodejs16.x
{
"AWSTemplateFormatVersion": "2010-09-09",
"Transform": "AWS::Serverless-2016-10-31",
"Description": "Exported by architect/package@8.0.3 on 2022-04-01T20:25:58.390Z",
"Resources": {
"Role": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
},
"Policies": [
{
"PolicyName": "ArcGlobalPolicy",
"PolicyDocument": {
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogStreams"
],
"Resource": "arn:aws:logs:*:*:*"
}
]
}
},
{
"PolicyName": "ArcStaticBucketPolicy",
"PolicyDocument": {
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:PutObjectAcl",
"s3:DeleteObject",
"s3:ListBucket"
],
"Resource": [
{
"Fn::Sub": [
"arn:aws:s3:::${bukkit}",
{
"bukkit": {
"Ref": "StaticBucket"
}
}
]
},
{
"Fn::Sub": [
"arn:aws:s3:::${bukkit}/*",
{
"bukkit": {
"Ref": "StaticBucket"
}
}
]
}
]
}
]
}
},
{
"PolicyName": "ArcDynamoPolicy",
"PolicyDocument": {
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:BatchGetItem",
"dynamodb:BatchWriteItem",
"dynamodb:PutItem",
"dynamodb:DeleteItem",
"dynamodb:GetItem",
"dynamodb:Query",
"dynamodb:Scan",
"dynamodb:UpdateItem",
"dynamodb:GetRecords",
"dynamodb:GetShardIterator",
"dynamodb:DescribeStream",
"dynamodb:ListStreams"
],
"Resource": [
{
"Fn::Sub": [
"arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}",
{
"tablename": {
"Ref": "PlayersTable"
}
}
]
},
{
"Fn::Sub": [
"arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}/*",
{
"tablename": {
"Ref": "PlayersTable"
}
}
]
},
{
"Fn::Sub": [
"arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}/stream/*",
{
"tablename": {
"Ref": "PlayersTable"
}
}
]
},
{
"Fn::Sub": [
"arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}",
{
"tablename": {
"Ref": "GamesTable"
}
}
]
},
{
"Fn::Sub": [
"arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}/*",
{
"tablename": {
"Ref": "GamesTable"
}
}
]
},
{
"Fn::Sub": [
"arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}/stream/*",
{
"tablename": {
"Ref": "GamesTable"
}
}
]
}
]
}
]
}
}
]
}
},
"PlayersParam": {
"Type": "AWS::SSM::Parameter",
"Properties": {
"Type": "String",
"Name": {
"Fn::Sub": [
"/${AWS::StackName}/tables/${tablename}",
{
"tablename": "players"
}
]
},
"Value": {
"Ref": "PlayersTable"
}
}
},
"GamesParam": {
"Type": "AWS::SSM::Parameter",
"Properties": {
"Type": "String",
"Name": {
"Fn::Sub": [
"/${AWS::StackName}/tables/${tablename}",
{
"tablename": "games"
}
]
},
"Value": {
"Ref": "GamesTable"
}
}
},
"StaticBucketParam": {
"Type": "AWS::SSM::Parameter",
"Properties": {
"Type": "String",
"Name": {
"Fn::Sub": [
"/${AWS::StackName}/static/${key}",
{
"key": "bucket"
}
]
},
"Value": {
"Ref": "StaticBucket"
}
}
},
"StaticFingerprintParam": {
"Type": "AWS::SSM::Parameter",
"Properties": {
"Type": "String",
"Name": {
"Fn::Sub": [
"/${AWS::StackName}/static/${key}",
{
"key": "fingerprint"
}
]
},
"Value": "true"
}
},
"ParameterStorePolicy": {
"Type": "AWS::IAM::Policy",
"DependsOn": "Role",
"Properties": {
"PolicyName": "ArcParameterStorePolicy",
"PolicyDocument": {
"Statement": [
{
"Effect": "Allow",
"Action": [
"ssm:GetParametersByPath",
"ssm:GetParameter"
],
"Resource": {
"Fn::Sub": [
"arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${AWS::StackName}",
{}
]
}
},
{
"Effect": "Allow",
"Action": [
"ssm:GetParametersByPath",
"ssm:GetParameter"
],
"Resource": {
"Fn::Sub": [
"arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${AWS::StackName}/*",
{}
]
}
},
{
"Effect": "Allow",
"Action": [
"ssm:GetParametersByPath",
"ssm:GetParameter"
],
"Resource": {
"Fn::Sub": [
"arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${AWS::StackName}/*/*",
{}
]
}
}
]
},
"Roles": [
{
"Ref": "Role"
}
]
}
},
"HTTP": {
"Type": "AWS::Serverless::HttpApi",
"Properties": {
"StageName": "$default",
"DefinitionBody": {
"openapi": "3.0.1",
"info": {
"title": {
"Ref": "AWS::StackName"
}
},
"paths": {
"/games/add": {
"get": {
"x-amazon-apigateway-integration": {
"payloadFormatVersion": "2.0",
"type": "aws_proxy",
"httpMethod": "POST",
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetGamesAddHTTPLambda.Arn}/invocations"
},
"connectionType": "INTERNET"
}
}
},
"/players/add": {
"get": {
"x-amazon-apigateway-integration": {
"payloadFormatVersion": "2.0",
"type": "aws_proxy",
"httpMethod": "POST",
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetPlayersAddHTTPLambda.Arn}/invocations"
},
"connectionType": "INTERNET"
}
}
},
"/components/{proxy+}": {
"get": {
"x-amazon-apigateway-integration": {
"payloadFormatVersion": "2.0",
"type": "aws_proxy",
"httpMethod": "POST",
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetComponentsCatchallHTTPLambda.Arn}/invocations"
},
"connectionType": "INTERNET"
}
}
},
"/games/{id}": {
"get": {
"x-amazon-apigateway-integration": {
"payloadFormatVersion": "2.0",
"type": "aws_proxy",
"httpMethod": "POST",
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetGamesIdHTTPLambda.Arn}/invocations"
},
"connectionType": "INTERNET"
}
},
"post": {
"x-amazon-apigateway-integration": {
"payloadFormatVersion": "2.0",
"type": "aws_proxy",
"httpMethod": "POST",
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PostGamesIdHTTPLambda.Arn}/invocations"
},
"connectionType": "INTERNET"
}
}
},
"/auth": {
"get": {
"x-amazon-apigateway-integration": {
"payloadFormatVersion": "2.0",
"type": "aws_proxy",
"httpMethod": "POST",
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetAuthHTTPLambda.Arn}/invocations"
},
"connectionType": "INTERNET"
}
}
},
"/games": {
"get": {
"x-amazon-apigateway-integration": {
"payloadFormatVersion": "2.0",
"type": "aws_proxy",
"httpMethod": "POST",
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetGamesHTTPLambda.Arn}/invocations"
},
"connectionType": "INTERNET"
}
},
"post": {
"x-amazon-apigateway-integration": {
"payloadFormatVersion": "2.0",
"type": "aws_proxy",
"httpMethod": "POST",
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PostGamesHTTPLambda.Arn}/invocations"
},
"connectionType": "INTERNET"
}
}
},
"/login": {
"get": {
"x-amazon-apigateway-integration": {
"payloadFormatVersion": "2.0",
"type": "aws_proxy",
"httpMethod": "POST",
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetLoginHTTPLambda.Arn}/invocations"
},
"connectionType": "INTERNET"
}
}
},
"/players": {
"get": {
"x-amazon-apigateway-integration": {
"payloadFormatVersion": "2.0",
"type": "aws_proxy",
"httpMethod": "POST",
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetPlayersHTTPLambda.Arn}/invocations"
},
"connectionType": "INTERNET"
}
},
"post": {
"x-amazon-apigateway-integration": {
"payloadFormatVersion": "2.0",
"type": "aws_proxy",
"httpMethod": "POST",
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PostPlayersHTTPLambda.Arn}/invocations"
},
"connectionType": "INTERNET"
}
}
},
"/": {
"get": {
"x-amazon-apigateway-integration": {
"payloadFormatVersion": "2.0",
"type": "aws_proxy",
"httpMethod": "POST",
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetIndexHTTPLambda.Arn}/invocations"
},
"connectionType": "INTERNET"
}
}
},
"/players/{id}/delete": {
"post": {
"x-amazon-apigateway-integration": {
"payloadFormatVersion": "2.0",
"type": "aws_proxy",
"httpMethod": "POST",
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PostPlayersIdDeleteHTTPLambda.Arn}/invocations"
},
"connectionType": "INTERNET"
}
}
},
"/games/{id}/delete": {
"post": {
"x-amazon-apigateway-integration": {
"payloadFormatVersion": "2.0",
"type": "aws_proxy",
"httpMethod": "POST",
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PostGamesIdDeleteHTTPLambda.Arn}/invocations"
},
"connectionType": "INTERNET"
}
}
},
"/players/{id}": {
"post": {
"x-amazon-apigateway-integration": {
"payloadFormatVersion": "2.0",
"type": "aws_proxy",
"httpMethod": "POST",
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PostPlayersIdHTTPLambda.Arn}/invocations"
},
"connectionType": "INTERNET"
}
}
},
"/logout": {
"post": {
"x-amazon-apigateway-integration": {
"payloadFormatVersion": "2.0",
"type": "aws_proxy",
"httpMethod": "POST",
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PostLogoutHTTPLambda.Arn}/invocations"
},
"connectionType": "INTERNET"
}
}
},
"/mock/auth/{part}": {
"x-amazon-apigateway-any-method": {
"x-amazon-apigateway-integration": {
"payloadFormatVersion": "2.0",
"type": "aws_proxy",
"httpMethod": "POST",
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${AnyMockAuthPartHTTPLambda.Arn}/invocations"
},
"connectionType": "INTERNET"
}
}
},
"/_static/{proxy+}": {
"get": {
"x-amazon-apigateway-integration": {
"payloadFormatVersion": "1.0",
"type": "http_proxy",
"httpMethod": "GET",
"uri": {
"Fn::Sub": [
"http://${bukkit}.s3.${AWS::Region}.amazonaws.com/{proxy}",
{
"bukkit": {
"Ref": "StaticBucket"
}
}
]
},
"connectionType": "INTERNET",
"timeoutInMillis": 30000
}
}
}
}
}
}
},
"GetGamesAddHTTPLambda": {
"Type": "AWS::Serverless::Function",
"Properties": {
"Handler": "index.handler",
"CodeUri": "/Users/simonmacdonald/Developer/macdonst/arc-fnh/src/http/get-games-add",
"Runtime": "nodejs14.x",
"Architectures": [
"arm64"
],
"MemorySize": 1152,
"Timeout": 5,
"Environment": {
"Variables": {
"ARC_APP_NAME": "arc-fnh",
"ARC_ENV": "staging",
"ARC_ROLE": {
"Ref": "Role"
},
"ARC_SESSION_TABLE_NAME": "jwe",
"ARC_STACK_NAME": {
"Ref": "AWS::StackName"
},
"ARC_STATIC_BUCKET": {
"Ref": "StaticBucket"
},
"ARC_OAUTH_CLIENT_ID": "74528a8f82952e0346c9",
"ARC_OAUTH_CLIENT_SECRET": "3d54da78c796bc6bb59eda5fff1f9a1167b665e6",
"ARC_OAUTH_REDIRECT_URL": "https://bb2smqni8c.execute-api.us-west-2.amazonaws.com/auth",
"ARC_OAUTH_INCLUDE_PROPERTIES": "[\"login\"]",
"ARC_OAUTH_CUSTOM_AUTHORIZE": "",
"ARC_OAUTH_MATCH_PROPERTY": "login",
"ARC_OAUTH_AFTER_AUTH": "/",
"ARC_OAUTH_UN_AUTH_REDIRECT": "/login",
"ARC_OAUTH_USE_ALLOW_LIST": "true",
"ARC_OAUTH_ALLOW_LIST": "allow.mjs",
"ARC_OAUTH_TOKEN_URI": "https://github.com/login/oauth/access_token",
"ARC_OAUTH_USER_INFO_URI": "https://api.github.com/user"
}
},
"Role": {
"Fn::Sub": [
"arn:aws:iam::${AWS::AccountId}:role/${roleName}",
{
"roleName": {
"Ref": "Role"
}
}
]
},
"Events": {
"GetGamesAddHTTPEvent": {
"Type": "HttpApi",
"Properties": {
"Path": "/games/add",
"Method": "GET",
"ApiId": {
"Ref": "HTTP"
}
}
}
}
}
},
"GetPlayersAddHTTPLambda": {
"Type": "AWS::Serverless::Function",
"Properties": {
"Handler": "index.handler",
"CodeUri": "/Users/simonmacdonald/Developer/macdonst/arc-fnh/src/http/get-players-add",
"Runtime": "nodejs14.x",
"Architectures": [
"arm64"
],
"MemorySize": 1152,
"Timeout": 5,
"Environment": {
"Variables": {
"ARC_APP_NAME": "arc-fnh",
"ARC_ENV": "staging",
"ARC_ROLE": {
"Ref": "Role"
},
"ARC_SESSION_TABLE_NAME": "jwe",
"ARC_STACK_NAME": {
"Ref": "AWS::StackName"
},
"ARC_STATIC_BUCKET": {
"Ref": "StaticBucket"
},
"ARC_OAUTH_CLIENT_ID": "74528a8f82952e0346c9",
"ARC_OAUTH_CLIENT_SECRET": "3d54da78c796bc6bb59eda5fff1f9a1167b665e6",
"ARC_OAUTH_REDIRECT_URL": "https://bb2smqni8c.execute-api.us-west-2.amazonaws.com/auth",
"ARC_OAUTH_INCLUDE_PROPERTIES": "[\"login\"]",
"ARC_OAUTH_CUSTOM_AUTHORIZE": "",
"ARC_OAUTH_MATCH_PROPERTY": "login",
"ARC_OAUTH_AFTER_AUTH": "/",
"ARC_OAUTH_UN_AUTH_REDIRECT": "/login",
"ARC_OAUTH_USE_ALLOW_LIST": "true",
"ARC_OAUTH_ALLOW_LIST": "allow.mjs",
"ARC_OAUTH_TOKEN_URI": "https://github.com/login/oauth/access_token",
"ARC_OAUTH_USER_INFO_URI": "https://api.github.com/user"
}
},
"Role": {
"Fn::Sub": [
"arn:aws:iam::${AWS::AccountId}:role/${roleName}",
{
"roleName": {
"Ref": "Role"
}
}
]
},
"Events": {
"GetPlayersAddHTTPEvent": {
"Type": "HttpApi",
"Properties": {
"Path": "/players/add",
"Method": "GET",
"ApiId": {
"Ref": "HTTP"
}
}
}
}
}
},
"GetComponentsCatchallHTTPLambda": {
"Type": "AWS::Serverless::Function",
"Properties": {
"Handler": "index.handler",
"CodeUri": "/Users/simonmacdonald/Developer/macdonst/arc-fnh/src/http/get-components-catchall",
"Runtime": "nodejs14.x",
"Architectures": [
"arm64"
],
"MemorySize": 1152,
"Timeout": 5,
"Environment": {
"Variables": {
"ARC_APP_NAME": "arc-fnh",
"ARC_ENV": "staging",
"ARC_ROLE": {
"Ref": "Role"
},
"ARC_SESSION_TABLE_NAME": "jwe",
"ARC_STACK_NAME": {
"Ref": "AWS::StackName"
},
"ARC_STATIC_BUCKET": {
"Ref": "StaticBucket"
},
"ARC_OAUTH_CLIENT_ID": "74528a8f82952e0346c9",
"ARC_OAUTH_CLIENT_SECRET": "3d54da78c796bc6bb59eda5fff1f9a1167b665e6",
"ARC_OAUTH_REDIRECT_URL": "https://bb2smqni8c.execute-api.us-west-2.amazonaws.com/auth",
"ARC_OAUTH_INCLUDE_PROPERTIES": "[\"login\"]",
"ARC_OAUTH_CUSTOM_AUTHORIZE": "",
"ARC_OAUTH_MATCH_PROPERTY": "login",
"ARC_OAUTH_AFTER_AUTH": "/",
"ARC_OAUTH_UN_AUTH_REDIRECT": "/login",
"ARC_OAUTH_USE_ALLOW_LIST": "true",
"ARC_OAUTH_ALLOW_LIST": "allow.mjs",
"ARC_OAUTH_TOKEN_URI": "https://github.com/login/oauth/access_token",
"ARC_OAUTH_USER_INFO_URI": "https://api.github.com/user"
}
},
"Role": {
"Fn::Sub": [
"arn:aws:iam::${AWS::AccountId}:role/${roleName}",
{
"roleName": {
"Ref": "Role"
}
}
]
},
"Events": {
"GetComponentsCatchallHTTPEvent": {
"Type": "HttpApi",
"Properties": {
"Path": "/components/{proxy+}",
"Method": "GET",
"ApiId": {
"Ref": "HTTP"
}
}
}
}
}
},
"GetGamesIdHTTPLambda": {
"Type": "AWS::Serverless::Function",
"Properties": {
"Handler": "index.handler",
"CodeUri": "/Users/simonmacdonald/Developer/macdonst/arc-fnh/src/http/get-games-000id",
"Runtime": "nodejs14.x",
"Architectures": [
"arm64"
],
"MemorySize": 1152,
"Timeout": 5,
"Environment": {
"Variables": {
"ARC_APP_NAME": "arc-fnh",
"ARC_ENV": "staging",
"ARC_ROLE": {
"Ref": "Role"
},
"ARC_SESSION_TABLE_NAME": "jwe",
"ARC_STACK_NAME": {
"Ref": "AWS::StackName"
},
"ARC_STATIC_BUCKET": {
"Ref": "StaticBucket"
},
"ARC_OAUTH_CLIENT_ID": "74528a8f82952e0346c9",
"ARC_OAUTH_CLIENT_SECRET": "3d54da78c796bc6bb59eda5fff1f9a1167b665e6",
"ARC_OAUTH_REDIRECT_URL": "https://bb2smqni8c.execute-api.us-west-2.amazonaws.com/auth",
"ARC_OAUTH_INCLUDE_PROPERTIES": "[\"login\"]",
"ARC_OAUTH_CUSTOM_AUTHORIZE": "",
"ARC_OAUTH_MATCH_PROPERTY": "login",
"ARC_OAUTH_AFTER_AUTH": "/",
"ARC_OAUTH_UN_AUTH_REDIRECT": "/login",
"ARC_OAUTH_USE_ALLOW_LIST": "true",
"ARC_OAUTH_ALLOW_LIST": "allow.mjs",
"ARC_OAUTH_TOKEN_URI": "https://github.com/login/oauth/access_token",
"ARC_OAUTH_USER_INFO_URI": "https://api.github.com/user"
}
},
"Role": {
"Fn::Sub": [
"arn:aws:iam::${AWS::AccountId}:role/${roleName}",
{
"roleName": {
"Ref": "Role"
}
}
]
},
"Events": {
"GetGamesIdHTTPEvent": {
"Type": "HttpApi",
"Properties": {
"Path": "/games/{id}",
"Method": "GET",
"ApiId": {
"Ref": "HTTP"
}
}
}
}
}
},
"GetAuthHTTPLambda": {
"Type": "AWS::Serverless::Function",
"Properties": {
"Handler": "index.handler",
"CodeUri": "/Users/simonmacdonald/Developer/macdonst/arc-fnh/node_modules/arc-plugin-oauth/src/src/http/get-auth",
"Runtime": "nodejs14.x",
"Architectures": [
"arm64"
],
"MemorySize": 1152,
"Timeout": 5,
"Environment": {
"Variables": {
"ARC_APP_NAME": "arc-fnh",
"ARC_ENV": "staging",
"ARC_ROLE": {
"Ref": "Role"
},
"ARC_SESSION_TABLE_NAME": "jwe",
"ARC_STACK_NAME": {
"Ref": "AWS::StackName"
},
"ARC_STATIC_BUCKET": {
"Ref": "StaticBucket"
},
"ARC_OAUTH_CLIENT_ID": "74528a8f82952e0346c9",
"ARC_OAUTH_CLIENT_SECRET": "3d54da78c796bc6bb59eda5fff1f9a1167b665e6",
"ARC_OAUTH_REDIRECT_URL": "https://bb2smqni8c.execute-api.us-west-2.amazonaws.com/auth",
"ARC_OAUTH_INCLUDE_PROPERTIES": "[\"login\"]",
"ARC_OAUTH_CUSTOM_AUTHORIZE": "",
"ARC_OAUTH_MATCH_PROPERTY": "login",
"ARC_OAUTH_AFTER_AUTH": "/",
"ARC_OAUTH_UN_AUTH_REDIRECT": "/login",
"ARC_OAUTH_USE_ALLOW_LIST": "true",
"ARC_OAUTH_ALLOW_LIST": "allow.mjs",
"ARC_OAUTH_TOKEN_URI": "https://github.com/login/oauth/access_token",
"ARC_OAUTH_USER_INFO_URI": "https://api.github.com/user"
}
},
"Role": {
"Fn::Sub": [
"arn:aws:iam::${AWS::AccountId}:role/${roleName}",
{
"roleName": {
"Ref": "Role"
}
}
]
},
"Events": {
"GetAuthHTTPEvent": {
"Type": "HttpApi",
"Properties": {
"Path": "/auth",
"Method": "GET",
"ApiId": {
"Ref": "HTTP"
}
}
}
}
}
},
"GetGamesHTTPLambda": {
"Type": "AWS::Serverless::Function",
"Properties": {
"Handler": "index.handler",
"CodeUri": "/Users/simonmacdonald/Developer/macdonst/arc-fnh/src/http/get-games",
"Runtime": "nodejs14.x",
"Architectures": [
"arm64"
],
"MemorySize": 1152,
"Timeout": 5,
"Environment": {
"Variables": {
"ARC_APP_NAME": "arc-fnh",
"ARC_ENV": "staging",
"ARC_ROLE": {
"Ref": "Role"
},
"ARC_SESSION_TABLE_NAME": "jwe",
"ARC_STACK_NAME": {
"Ref": "AWS::StackName"
},
"ARC_STATIC_BUCKET": {
"Ref": "StaticBucket"
},
"ARC_OAUTH_CLIENT_ID": "74528a8f82952e0346c9",
"ARC_OAUTH_CLIENT_SECRET": "3d54da78c796bc6bb59eda5fff1f9a1167b665e6",
"ARC_OAUTH_REDIRECT_URL": "https://bb2smqni8c.execute-api.us-west-2.amazonaws.com/auth",
"ARC_OAUTH_INCLUDE_PROPERTIES": "[\"login\"]",
"ARC_OAUTH_CUSTOM_AUTHORIZE": "",
"ARC_OAUTH_MATCH_PROPERTY": "login",
"ARC_OAUTH_AFTER_AUTH": "/",
"ARC_OAUTH_UN_AUTH_REDIRECT": "/login",
"ARC_OAUTH_USE_ALLOW_LIST": "true",
"ARC_OAUTH_ALLOW_LIST": "allow.mjs",
"ARC_OAUTH_TOKEN_URI": "https://github.com/login/oauth/access_token",
"ARC_OAUTH_USER_INFO_URI": "https://api.github.com/user"
}
},
"Role": {
"Fn::Sub": [
"arn:aws:iam::${AWS::AccountId}:role/${roleName}",
{
"roleName": {
"Ref": "Role"
}
}
]
},
"Events": {
"GetGamesHTTPEvent": {
"Type": "HttpApi",
"Properties": {
"Path": "/games",
"Method": "GET",
"ApiId": {
"Ref": "HTTP"
}
}
}
}
}
},
"GetLoginHTTPLambda": {
"Type": "AWS::Serverless::Function",
"Properties": {
"Handler": "index.handler",
"CodeUri": "/Users/simonmacdonald/Developer/macdonst/arc-fnh/node_modules/arc-plugin-oauth/src/src/http/get-login",
"Runtime": "nodejs14.x",
"Architectures": [
"arm64"
],
"MemorySize": 1152,
"Timeout": 5,
"Environment": {
"Variables": {
"ARC_APP_NAME": "arc-fnh",
"ARC_ENV": "staging",
"ARC_ROLE": {
"Ref": "Role"
},
"ARC_SESSION_TABLE_NAME": "jwe",
"ARC_STACK_NAME": {
"Ref": "AWS::StackName"
},
"ARC_STATIC_BUCKET": {
"Ref": "StaticBucket"
},
"ARC_OAUTH_CLIENT_ID": "74528a8f82952e0346c9",
"ARC_OAUTH_CLIENT_SECRET": "3d54da78c796bc6bb59eda5fff1f9a1167b665e6",
"ARC_OAUTH_REDIRECT_URL": "https://bb2smqni8c.execute-api.us-west-2.amazonaws.com/auth",
"ARC_OAUTH_INCLUDE_PROPERTIES": "[\"login\"]",
"ARC_OAUTH_CUSTOM_AUTHORIZE": "",
"ARC_OAUTH_MATCH_PROPERTY": "login",
"ARC_OAUTH_AFTER_AUTH": "/",
"ARC_OAUTH_UN_AUTH_REDIRECT": "/login",
"ARC_OAUTH_USE_ALLOW_LIST": "true",
"ARC_OAUTH_ALLOW_LIST": "allow.mjs",
"ARC_OAUTH_TOKEN_URI": "https://github.com/login/oauth/access_token",
"ARC_OAUTH_USER_INFO_URI": "https://api.github.com/user"
}
},
"Role": {
"Fn::Sub": [
"arn:aws:iam::${AWS::AccountId}:role/${roleName}",
{
"roleName": {
"Ref": "Role"
}
}
]
},
"Events": {
"GetLoginHTTPEvent": {
"Type": "HttpApi",
"Properties": {
"Path": "/login",
"Method": "GET",
"ApiId": {
"Ref": "HTTP"
}
}
}
}
}
},
"GetPlayersHTTPLambda": {
"Type": "AWS::Serverless::Function",
"Properties": {
"Handler": "index.handler",
"CodeUri": "/Users/simonmacdonald/Developer/macdonst/arc-fnh/src/http/get-players",
"Runtime": "nodejs14.x",
"Architectures": [
"arm64"
],
"MemorySize": 1152,
"Timeout": 5,
"Environment": {
"Variables": {
"ARC_APP_NAME": "arc-fnh",
"ARC_ENV": "staging",
"ARC_ROLE": {
"Ref": "Role"
},
"ARC_SESSION_TABLE_NAME": "jwe",
"ARC_STACK_NAME": {
"Ref": "AWS::StackName"
},
"ARC_STATIC_BUCKET": {
"Ref": "StaticBucket"
},
"ARC_OAUTH_CLIENT_ID": "74528a8f82952e0346c9",
"ARC_OAUTH_CLIENT_SECRET": "3d54da78c796bc6bb59eda5fff1f9a1167b665e6",
"ARC_OAUTH_REDIRECT_URL": "https://bb2smqni8c.execute-api.us-west-2.amazonaws.com/auth",
"ARC_OAUTH_INCLUDE_PROPERTIES": "[\"login\"]",
"ARC_OAUTH_CUSTOM_AUTHORIZE": "",
"ARC_OAUTH_MATCH_PROPERTY": "login",
"ARC_OAUTH_AFTER_AUTH": "/",
"ARC_OAUTH_UN_AUTH_REDIRECT": "/login",
"ARC_OAUTH_USE_ALLOW_LIST": "true",
"ARC_OAUTH_ALLOW_LIST": "allow.mjs",
"ARC_OAUTH_TOKEN_URI": "https://github.com/login/oauth/access_token",
"ARC_OAUTH_USER_INFO_URI": "https://api.github.com/user"
}
},
"Role": {
"Fn::Sub": [
"arn:aws:iam::${AWS::AccountId}:role/${roleName}",
{
"roleName": {
"Ref": "Role"
}
}
]
},
"Events": {
"GetPlayersHTTPEvent": {
"Type": "HttpApi",
"Properties": {
"Path": "/players",
"Method": "GET",
"ApiId": {
"Ref": "HTTP"
}
}
}
}
}
},
"GetIndexHTTPLambda": {
"Type": "AWS::Serverless::Function",
"Properties": {
"Handler": "index.handler",
"CodeUri": "/Users/simonmacdonald/Developer/macdonst/arc-fnh/src/http/get-index",
"Runtime": "nodejs14.x",
"Architectures": [
"arm64"
],
"MemorySize": 1152,
"Timeout": 5,
"Environment": {
"Variables": {
"ARC_APP_NAME": "arc-fnh",
"ARC_ENV": "staging",
"ARC_ROLE": {
"Ref": "Role"
},
"ARC_SESSION_TABLE_NAME": "jwe",
"ARC_STACK_NAME": {
"Ref": "AWS::StackName"
},
"ARC_STATIC_BUCKET": {
"Ref": "StaticBucket"
},
"ARC_STATIC_SPA": false,
"ARC_OAUTH_CLIENT_ID": "74528a8f82952e0346c9",
"ARC_OAUTH_CLIENT_SECRET": "3d54da78c796bc6bb59eda5fff1f9a1167b665e6",
"ARC_OAUTH_REDIRECT_URL": "https://bb2smqni8c.execute-api.us-west-2.amazonaws.com/auth",
"ARC_OAUTH_INCLUDE_PROPERTIES": "[\"login\"]",
"ARC_OAUTH_CUSTOM_AUTHORIZE": "",
"ARC_OAUTH_MATCH_PROPERTY": "login",
"ARC_OAUTH_AFTER_AUTH": "/",
"ARC_OAUTH_UN_AUTH_REDIRECT": "/login",
"ARC_OAUTH_USE_ALLOW_LIST": "true",
"ARC_OAUTH_ALLOW_LIST": "allow.mjs",
"ARC_OAUTH_TOKEN_URI": "https://github.com/login/oauth/access_token",
"ARC_OAUTH_USER_INFO_URI": "https://api.github.com/user"
}
},
"Role": {
"Fn::Sub": [
"arn:aws:iam::${AWS::AccountId}:role/${roleName}",
{
"roleName": {
"Ref": "Role"
}
}
]
},
"Events": {
"GetIndexHTTPEvent": {
"Type": "HttpApi",
"Properties": {
"Path": "/",
"Method": "GET",
"ApiId": {
"Ref": "HTTP"
}
}
}
}
}
},
"PostPlayersIdDeleteHTTPLambda": {
"Type": "AWS::Serverless::Function",
"Properties": {
"Handler": "index.handler",
"CodeUri": "/Users/simonmacdonald/Developer/macdonst/arc-fnh/src/http/post-players-000id-delete",
"Runtime": "nodejs14.x",
"Architectures": [
"arm64"
],
"MemorySize": 1152,
"Timeout": 5,
"Environment": {
"Variables": {
"ARC_APP_NAME": "arc-fnh",
"ARC_ENV": "staging",
"ARC_ROLE": {
"Ref": "Role"
},
"ARC_SESSION_TABLE_NAME": "jwe",
"ARC_STACK_NAME": {
"Ref": "AWS::StackName"
},
"ARC_STATIC_BUCKET": {
"Ref": "StaticBucket"
},
"ARC_OAUTH_CLIENT_ID": "74528a8f82952e0346c9",
"ARC_OAUTH_CLIENT_SECRET": "3d54da78c796bc6bb59eda5fff1f9a1167b665e6",
"ARC_OAUTH_REDIRECT_URL": "https://bb2smqni8c.execute-api.us-west-2.amazonaws.com/auth",
"ARC_OAUTH_INCLUDE_PROPERTIES": "[\"login\"]",
"ARC_OAUTH_CUSTOM_AUTHORIZE": "",
"ARC_OAUTH_MATCH_PROPERTY": "login",
"ARC_OAUTH_AFTER_AUTH": "/",
"ARC_OAUTH_UN_AUTH_REDIRECT": "/login",
"ARC_OAUTH_USE_ALLOW_LIST": "true",
"ARC_OAUTH_ALLOW_LIST": "allow.mjs",
"ARC_OAUTH_TOKEN_URI": "https://github.com/login/oauth/access_token",
"ARC_OAUTH_USER_INFO_URI": "https://api.github.com/user"
}
},
"Role": {
"Fn::Sub": [
"arn:aws:iam::${AWS::AccountId}:role/${roleName}",
{
"roleName": {
"Ref": "Role"
}
}
]
},
"Events": {
"PostPlayersIdDeleteHTTPEvent": {
"Type": "HttpApi",
"Properties": {
"Path": "/players/{id}/delete",
"Method": "POST",
"ApiId": {
"Ref": "HTTP"
}
}
}
}
}
},
"PostGamesIdDeleteHTTPLambda": {
"Type": "AWS::Serverless::Function",
"Properties": {
"Handler": "index.handler",
"CodeUri": "/Users/simonmacdonald/Developer/macdonst/arc-fnh/src/http/post-games-000id-delete",
"Runtime": "nodejs14.x",
"Architectures": [
"arm64"
],
"MemorySize": 1152,
"Timeout": 5,
"Environment": {
"Variables": {
"ARC_APP_NAME": "arc-fnh",
"ARC_ENV": "staging",
"ARC_ROLE": {
"Ref": "Role"
},
"ARC_SESSION_TABLE_NAME": "jwe",
"ARC_STACK_NAME": {
"Ref": "AWS::StackName"
},
"ARC_STATIC_BUCKET": {
"Ref": "StaticBucket"
},
"ARC_OAUTH_CLIENT_ID": "74528a8f82952e0346c9",
"ARC_OAUTH_CLIENT_SECRET": "3d54da78c796bc6bb59eda5fff1f9a1167b665e6",
"ARC_OAUTH_REDIRECT_URL": "https://bb2smqni8c.execute-api.us-west-2.amazonaws.com/auth",
"ARC_OAUTH_INCLUDE_PROPERTIES": "[\"login\"]",
"ARC_OAUTH_CUSTOM_AUTHORIZE": "",
"ARC_OAUTH_MATCH_PROPERTY": "login",
"ARC_OAUTH_AFTER_AUTH": "/",
"ARC_OAUTH_UN_AUTH_REDIRECT": "/login",
"ARC_OAUTH_USE_ALLOW_LIST": "true",
"ARC_OAUTH_ALLOW_LIST": "allow.mjs",
"ARC_OAUTH_TOKEN_URI": "https://github.com/login/oauth/access_token",
"ARC_OAUTH_USER_INFO_URI": "https://api.github.com/user"
}
},
"Role": {
"Fn::Sub": [
"arn:aws:iam::${AWS::AccountId}:role/${roleName}",
{
"roleName": {
"Ref": "Role"
}
}
]
},
"Events": {
"PostGamesIdDeleteHTTPEvent": {
"Type": "HttpApi",
"Properties": {
"Path": "/games/{id}/delete",
"Method": "POST",
"ApiId": {
"Ref": "HTTP"
}
}
}
}
}
},
"PostPlayersIdHTTPLambda": {
"Type": "AWS::Serverless::Function",
"Properties": {
"Handler": "index.handler",
"CodeUri": "/Users/simonmacdonald/Developer/macdonst/arc-fnh/src/http/post-players-000id",
"Runtime": "nodejs14.x",
"Architectures": [
"arm64"
],
"MemorySize": 1152,
"Timeout": 5,
"Environment": {
"Variables": {
"ARC_APP_NAME": "arc-fnh",
"ARC_ENV": "staging",
"ARC_ROLE": {
"Ref": "Role"
},
"ARC_SESSION_TABLE_NAME": "jwe",
"ARC_STACK_NAME": {
"Ref": "AWS::StackName"
},
"ARC_STATIC_BUCKET": {
"Ref": "StaticBucket"
},
"ARC_OAUTH_CLIENT_ID": "74528a8f82952e0346c9",
"ARC_OAUTH_CLIENT_SECRET": "3d54da78c796bc6bb59eda5fff1f9a1167b665e6",
"ARC_OAUTH_REDIRECT_URL": "https://bb2smqni8c.execute-api.us-west-2.amazonaws.com/auth",
"ARC_OAUTH_INCLUDE_PROPERTIES": "[\"login\"]",
"ARC_OAUTH_CUSTOM_AUTHORIZE": "",
"ARC_OAUTH_MATCH_PROPERTY": "login",
"ARC_OAUTH_AFTER_AUTH": "/",
"ARC_OAUTH_UN_AUTH_REDIRECT": "/login",
"ARC_OAUTH_USE_ALLOW_LIST": "true",
"ARC_OAUTH_ALLOW_LIST": "allow.mjs",
"ARC_OAUTH_TOKEN_URI": "https://github.com/login/oauth/access_token",
"ARC_OAUTH_USER_INFO_URI": "https://api.github.com/user"
}
},
"Role": {
"Fn::Sub": [
"arn:aws:iam::${AWS::AccountId}:role/${roleName}",
{
"roleName": {
"Ref": "Role"
}
}
]
},
"Events": {
"PostPlayersIdHTTPEvent": {
"Type": "HttpApi",
"Properties": {
"Path": "/players/{id}",
"Method": "POST",
"ApiId": {
"Ref": "HTTP"
}
}
}
}
}
},
"PostGamesIdHTTPLambda": {
"Type": "AWS::Serverless::Function",
"Properties": {
"Handler": "index.handler",
"CodeUri": "/Users/simonmacdonald/Developer/macdonst/arc-fnh/src/http/post-games-000id",
"Runtime": "nodejs14.x",
"Architectures": [
"arm64"
],
"MemorySize": 1152,
"Timeout": 5,
"Environment": {
"Variables": {
"ARC_APP_NAME": "arc-fnh",
"ARC_ENV": "staging",
"ARC_ROLE": {
"Ref": "Role"
},
"ARC_SESSION_TABLE_NAME": "jwe",
"ARC_STACK_NAME": {
"Ref": "AWS::StackName"
},
"ARC_STATIC_BUCKET": {
"Ref": "StaticBucket"
},
"ARC_OAUTH_CLIENT_ID": "74528a8f82952e0346c9",
"ARC_OAUTH_CLIENT_SECRET": "3d54da78c796bc6bb59eda5fff1f9a1167b665e6",
"ARC_OAUTH_REDIRECT_URL": "https://bb2smqni8c.execute-api.us-west-2.amazonaws.com/auth",
"ARC_OAUTH_INCLUDE_PROPERTIES": "[\"login\"]",
"ARC_OAUTH_CUSTOM_AUTHORIZE": "",
"ARC_OAUTH_MATCH_PROPERTY": "login",
"ARC_OAUTH_AFTER_AUTH": "/",
"ARC_OAUTH_UN_AUTH_REDIRECT": "/login",
"ARC_OAUTH_USE_ALLOW_LIST": "true",
"ARC_OAUTH_ALLOW_LIST": "allow.mjs",
"ARC_OAUTH_TOKEN_URI": "https://github.com/login/oauth/access_token",
"ARC_OAUTH_USER_INFO_URI": "https://api.github.com/user"
}
},
"Role": {
"Fn::Sub": [
"arn:aws:iam::${AWS::AccountId}:role/${roleName}",
{
"roleName": {
"Ref": "Role"
}
}
]
},
"Events": {
"PostGamesIdHTTPEvent": {
"Type": "HttpApi",
"Properties": {
"Path": "/games/{id}",
"Method": "POST",
"ApiId": {
"Ref": "HTTP"
}
}
}
}
}
},
"PostGamesHTTPLambda": {
"Type": "AWS::Serverless::Function",
"Properties": {
"Handler": "index.handler",
"CodeUri": "/Users/simonmacdonald/Developer/macdonst/arc-fnh/src/http/post-games",
"Runtime": "nodejs14.x",
"Architectures": [
"arm64"
],
"MemorySize": 1152,
"Timeout": 5,
"Environment": {
"Variables": {
"ARC_APP_NAME": "arc-fnh",
"ARC_ENV": "staging",
"ARC_ROLE": {
"Ref": "Role"
},
"ARC_SESSION_TABLE_NAME": "jwe",
"ARC_STACK_NAME": {
"Ref": "AWS::StackName"
},
"ARC_STATIC_BUCKET": {
"Ref": "StaticBucket"
},
"ARC_OAUTH_CLIENT_ID": "74528a8f82952e0346c9",
"ARC_OAUTH_CLIENT_SECRET": "3d54da78c796bc6bb59eda5fff1f9a1167b665e6",
"ARC_OAUTH_REDIRECT_URL": "https://bb2smqni8c.execute-api.us-west-2.amazonaws.com/auth",
"ARC_OAUTH_INCLUDE_PROPERTIES": "[\"login\"]",
"ARC_OAUTH_CUSTOM_AUTHORIZE": "",
"ARC_OAUTH_MATCH_PROPERTY": "login",
"ARC_OAUTH_AFTER_AUTH": "/",
"ARC_OAUTH_UN_AUTH_REDIRECT": "/login",
"ARC_OAUTH_USE_ALLOW_LIST": "true",
"ARC_OAUTH_ALLOW_LIST": "allow.mjs",
"ARC_OAUTH_TOKEN_URI": "https://github.com/login/oauth/access_token",
"ARC_OAUTH_USER_INFO_URI": "https://api.github.com/user"
}
},
"Role": {
"Fn::Sub": [
"arn:aws:iam::${AWS::AccountId}:role/${roleName}",
{
"roleName": {
"Ref": "Role"
}
}
]
},
"Events": {
"PostGamesHTTPEvent": {
"Type": "HttpApi",
"Properties": {
"Path": "/games",
"Method": "POST",
"ApiId": {
"Ref": "HTTP"
}
}
}
}
}
},
"PostLogoutHTTPLambda": {
"Type": "AWS::Serverless::Function",
"Properties": {
"Handler": "index.handler",
"CodeUri": "/Users/simonmacdonald/Developer/macdonst/arc-fnh/node_modules/arc-plugin-oauth/src/src/http/post-logout",
"Runtime": "nodejs14.x",
"Architectures": [
"arm64"
],
"MemorySize": 1152,
"Timeout": 5,
"Environment": {
"Variables": {
"ARC_APP_NAME": "arc-fnh",
"ARC_ENV": "staging",
"ARC_ROLE": {
"Ref": "Role"
},
"ARC_SESSION_TABLE_NAME": "jwe",
"ARC_STACK_NAME": {
"Ref": "AWS::StackName"
},
"ARC_STATIC_BUCKET": {
"Ref": "StaticBucket"
},
"ARC_OAUTH_CLIENT_ID": "74528a8f82952e0346c9",
"ARC_OAUTH_CLIENT_SECRET": "3d54da78c796bc6bb59eda5fff1f9a1167b665e6",
"ARC_OAUTH_REDIRECT_URL": "https://bb2smqni8c.execute-api.us-west-2.amazonaws.com/auth",
"ARC_OAUTH_INCLUDE_PROPERTIES": "[\"login\"]",
"ARC_OAUTH_CUSTOM_AUTHORIZE": "",
"ARC_OAUTH_MATCH_PROPERTY": "login",
"ARC_OAUTH_AFTER_AUTH": "/",
"ARC_OAUTH_UN_AUTH_REDIRECT": "/login",
"ARC_OAUTH_USE_ALLOW_LIST": "true",
"ARC_OAUTH_ALLOW_LIST": "allow.mjs",
"ARC_OAUTH_TOKEN_URI": "https://github.com/login/oauth/access_token",
"ARC_OAUTH_USER_INFO_URI": "https://api.github.com/user"
}
},
"Role": {
"Fn::Sub": [
"arn:aws:iam::${AWS::AccountId}:role/${roleName}",
{
"roleName": {
"Ref": "Role"
}
}
]
},
"Events": {
"PostLogoutHTTPEvent": {
"Type": "HttpApi",
"Properties": {
"Path": "/logout",
"Method": "POST",
"ApiId": {
"Ref": "HTTP"
}
}
}
}
}
},
"PostPlayersHTTPLambda": {
"Type": "AWS::Serverless::Function",
"Properties": {
"Handler": "index.handler",
"CodeUri": "/Users/simonmacdonald/Developer/macdonst/arc-fnh/src/http/post-players",
"Runtime": "nodejs14.x",
"Architectures": [
"arm64"
],
"MemorySize": 1152,
"Timeout": 5,
"Environment": {
"Variables": {
"ARC_APP_NAME": "arc-fnh",
"ARC_ENV": "staging",
"ARC_ROLE": {
"Ref": "Role"
},
"ARC_SESSION_TABLE_NAME": "jwe",
"ARC_STACK_NAME": {
"Ref": "AWS::StackName"
},
"ARC_STATIC_BUCKET": {
"Ref": "StaticBucket"
},
"ARC_OAUTH_CLIENT_ID": "74528a8f82952e0346c9",
"ARC_OAUTH_CLIENT_SECRET": "3d54da78c796bc6bb59eda5fff1f9a1167b665e6",
"ARC_OAUTH_REDIRECT_URL": "https://bb2smqni8c.execute-api.us-west-2.amazonaws.com/auth",
"ARC_OAUTH_INCLUDE_PROPERTIES": "[\"login\"]",
"ARC_OAUTH_CUSTOM_AUTHORIZE": "",
"ARC_OAUTH_MATCH_PROPERTY": "login",
"ARC_OAUTH_AFTER_AUTH": "/",
"ARC_OAUTH_UN_AUTH_REDIRECT": "/login",
"ARC_OAUTH_USE_ALLOW_LIST": "true",
"ARC_OAUTH_ALLOW_LIST": "allow.mjs",
"ARC_OAUTH_TOKEN_URI": "https://github.com/login/oauth/access_token",
"ARC_OAUTH_USER_INFO_URI": "https://api.github.com/user"
}
},
"Role": {
"Fn::Sub": [
"arn:aws:iam::${AWS::AccountId}:role/${roleName}",
{
"roleName": {
"Ref": "Role"
}
}
]
},
"Events": {
"PostPlayersHTTPEvent": {
"Type": "HttpApi",
"Properties": {
"Path": "/players",
"Method": "POST",
"ApiId": {
"Ref": "HTTP"
}
}
}
}
}
},
"AnyMockAuthPartHTTPLambda": {
"Type": "AWS::Serverless::Function",
"Properties": {
"Handler": "index.handler",
"CodeUri": "/Users/simonmacdonald/Developer/macdonst/arc-fnh/node_modules/arc-plugin-oauth/src/src/http/get-mock-auth-000part",
"Runtime": "nodejs14.x",
"Architectures": [
"arm64"
],
"MemorySize": 1152,
"Timeout": 5,
"Environment": {
"Variables": {
"ARC_APP_NAME": "arc-fnh",
"ARC_ENV": "staging",
"ARC_ROLE": {
"Ref": "Role"
},
"ARC_SESSION_TABLE_NAME": "jwe",
"ARC_STACK_NAME": {
"Ref": "AWS::StackName"
},
"ARC_STATIC_BUCKET": {
"Ref": "StaticBucket"
},
"ARC_OAUTH_CLIENT_ID": "74528a8f82952e0346c9",
"ARC_OAUTH_CLIENT_SECRET": "3d54da78c796bc6bb59eda5fff1f9a1167b665e6",
"ARC_OAUTH_REDIRECT_URL": "https://bb2smqni8c.execute-api.us-west-2.amazonaws.com/auth",
"ARC_OAUTH_INCLUDE_PROPERTIES": "[\"login\"]",
"ARC_OAUTH_CUSTOM_AUTHORIZE": "",
"ARC_OAUTH_MATCH_PROPERTY": "login",
"ARC_OAUTH_AFTER_AUTH": "/",
"ARC_OAUTH_UN_AUTH_REDIRECT": "/login",
"ARC_OAUTH_USE_ALLOW_LIST": "true",
"ARC_OAUTH_ALLOW_LIST": "allow.mjs",
"ARC_OAUTH_TOKEN_URI": "https://github.com/login/oauth/access_token",
"ARC_OAUTH_USER_INFO_URI": "https://api.github.com/user"
}
},
"Role": {
"Fn::Sub": [
"arn:aws:iam::${AWS::AccountId}:role/${roleName}",
{
"roleName": {
"Ref": "Role"
}
}
]
},
"Events": {
"AnyMockAuthPartHTTPEvent": {
"Type": "HttpApi",
"Properties": {
"Path": "/mock/auth/{part}",
"Method": "ANY",
"ApiId": {
"Ref": "HTTP"
}
}
}
}
}
},
"PlayersTable": {
"Type": "AWS::DynamoDB::Table",
"Properties": {
"KeySchema": [
{
"AttributeName": "email",
"KeyType": "HASH"
}
],
"AttributeDefinitions": [
{
"AttributeName": "email",
"AttributeType": "S"
},
{
"AttributeName": "fulltime",
"AttributeType": "S"
}
],
"BillingMode": "PAY_PER_REQUEST",
"GlobalSecondaryIndexes": [
{
"IndexName": "playersByFulltime",
"KeySchema": [
{
"AttributeName": "fulltime",
"KeyType": "HASH"
}
],
"Projection": {
"ProjectionType": "ALL"
}
}
]
}
},
"GamesTable": {
"Type": "AWS::DynamoDB::Table",
"Properties": {
"KeySchema": [
{
"AttributeName": "gamedate",
"KeyType": "HASH"
}
],
"AttributeDefinitions": [
{
"AttributeName": "gamedate",
"AttributeType": "S"
}
],
"BillingMode": "PAY_PER_REQUEST",
"GlobalSecondaryIndexes": [
{
"IndexName": "gamesByDate",
"KeySchema": [
{
"AttributeName": "gamedate",
"KeyType": "HASH"
}
],
"Projection": {
"ProjectionType": "ALL"
}
}
]
}
},
"StaticBucket": {
"Type": "AWS::S3::Bucket",
"Properties": {
"OwnershipControls": {
"Rules": [
{
"ObjectOwnership": "BucketOwnerEnforced"
}
]
},
"WebsiteConfiguration": {
"IndexDocument": "index.html",
"ErrorDocument": "404.html"
}
}
},
"StaticBucketPolicy": {
"Type": "AWS::S3::BucketPolicy",
"Properties": {
"Bucket": {
"Ref": "StaticBucket"
},
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:GetObject"
],
"Effect": "Allow",
"Principal": "*",
"Resource": [
{
"Fn::Sub": [
"arn:aws:s3:::${bukkit}/*",
{
"bukkit": {
"Ref": "StaticBucket"
}
}
]
}
],
"Sid": "PublicReadGetObject"
}
]
}
}
}
},
"Outputs": {
"API": {
"Description": "API Gateway (HTTP)",
"Value": {
"Fn::Sub": [
"https://${ApiId}.execute-api.${AWS::Region}.amazonaws.com",
{
"ApiId": {
"Ref": "HTTP"
}
}
]
}
},
"ApiId": {
"Description": "API ID (ApiId)",
"Value": {
"Ref": "HTTP"
}
},
"BucketURL": {
"Description": "Bucket URL",
"Value": {
"Fn::Sub": [
"http://${bukkit}.s3-website-${AWS::Region}.amazonaws.com",
{
"bukkit": {
"Ref": "StaticBucket"
}
}
]
}
}
}
}
GET /players
import arc from '@architect/functions'
import render from '@architect/views/render.mjs'
import { getFulltimePlayers, getSpares } from '@architect/shared/db/players.mjs'
import arcOauth from 'arc-plugin-oauth'
const auth = arcOauth.auth
export const handler = arc.http.async(auth, players)
async function players(req) {
const { type = 'fulltime' } = req.query
const players =
type === 'fulltime' ? await getFulltimePlayers() : await getSpares()
const initialState = { account: req.session?.account }
return {
html: render(
`
<form method="POST" action="/players/delete">
<hockey-page>
<hockey-action-buttons direction="row-reverse">
<hockey-button icon="delete">Delete</hockey-button>
<hockey-action-button action="/players/add" icon="plus" label="Add" type="link" variant="default"></hockey-action-button>
</hockey-action-buttons>
<enhance-table>
<enhance-thead>
<enhance-tr><enhance-th width="1rem"> </enhance-th><enhance-th>Name</enhance-th><enhance-th class="unseen">Email</enhance-th><enhance-th class="unseen">Phone</enhance-th><enhance-th>Position</enhance-th></enhance-tr>
</enhance-thead>
<enhance-tbody>
${players
.map(
(player) =>
`<enhance-tr>
<enhance-td>
<input type="checkbox" name="todelete" class="leading5-l pt-3 pb-3 pl-1 pr-1 radius2 shadow-2" value="${
player.email
}"/>
</enhance-td>
<enhance-td><a class="color-blue" href="/players/add?id=${
player.email
}">${player.name} ${
player.preferred === 'true'
? `<hockey-icon icon="star" style="width: 1rem; height: 1rem; display: inline;"></hockey-icon>`
: ''
}</a></enhance-td>
<enhance-td class="unseen">${player.email}</enhance-td>
<enhance-td class="unseen">${player.phone}</enhance-td>
<enhance-td class="capitalize">${
player.position
}</enhance-td>
</enhance-tr>`
)
.join('')}
</enhance-tbody>
</enhance-table>
</hockey-page>
</form>
`,
initialState
)
}
}
POST /players
import arc from '@architect/functions'
import { upsertPlayer } from '@architect/shared/db/players.mjs'
import arcOauth from 'arc-plugin-oauth'
const auth = arcOauth.auth
export const handler = arc.http.async(auth, http)
async function http(req) {
await upsertPlayer(req.body)
const location =
req.body.fulltime === 'true' ? '/players' : '/players?type=spares'
return {
location
}
}
Enhance allows developers to write components as pure functions that return HTML. Then render them on the server to deliver complete HTML immediately available to the end user.
Enhance takes care of the tedious parts, allowing you to use today’s Web Platform standards more efficiently. As new standards and platform features become generally available, Enhance will make way for them.
Enhance allows for easy progressive enhancement so that working HTML can be further developed to add additional functionality with JavaScript.
Enhance is a web standards-based HTML framework. It’s designed to provide a dependable foundation for building lightweight, flexible, and future-proof web applications.
✔ File based routing with plain HTML
✔ Reuse markup with custom elements
✔ Built-in utility CSS based on scales rather than absolute values
✔ API routes without manually wiring props
✔ Progressively enhance with standard JS; no special syntax
✔ Fullstack FWA under the hood
Demo
Check out https://github.com/architect/architect
Server side render web components with https://enhance.dev
Read https://fwa.dev
Tell me how wrong I am