Skip to content

Commit

Permalink
Initial form and AWS lambda backend
Browse files Browse the repository at this point in the history
* Update infra dependencies

* Remove Astro from root package.json

* Update dependencies

* Split Docassemble out of AppStack; rename root ID, to avoid resource naming restrictions (names that start with a number)

* Add AWS lambda for a forms service backend.

* Wire a sample HTML form to the lambda form service backend.

* Remove layout from page components.
  • Loading branch information
danielnaab authored Oct 27, 2023
1 parent 11b2745 commit 5c10f32
Show file tree
Hide file tree
Showing 20 changed files with 890 additions and 1,245 deletions.
2 changes: 1 addition & 1 deletion apps/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@
"@atj/interviews": "workspace:*",
"@atj/dependency-graph": "workspace:*",
"@atj/docassemble": "workspace:*",
"commander": "^11.0.0"
"commander": "^11.1.0"
}
}
1 change: 1 addition & 0 deletions apps/form-service/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dist/
18 changes: 18 additions & 0 deletions apps/form-service/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "@atj/form-service",
"private": true,
"description": "backend service for handling submitted forms",
"main": "src/index.ts",
"scripts": {
"build": "esbuild src/index.ts --bundle --minify --sourcemap --platform=node --target=es2020 --outfile=dist/index.js",
"build:client": "tsup src/* --env.NODE_ENV production --dts-resolve",
"clean": "rm -rf dist tsconfig.tsbuildinfo"
},
"dependencies": {
"@atj/interviews": "workspace:*"
},
"devDependencies": {
"@types/aws-lambda": "^8.10.109",
"esbuild": "^0.19.5"
}
}
33 changes: 33 additions & 0 deletions apps/form-service/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { APIGatewayEvent, APIGatewayProxyResult, Context } from 'aws-lambda';

export const lambdaHandler = async (
event: APIGatewayEvent,
context: Context
): Promise<APIGatewayProxyResult> => {
if (!event.body) {
const response: APIGatewayProxyResult = {
statusCode: 400,
body: JSON.stringify({ message: 'Request body is missing.' }),
};
return response;
}

const body = Buffer.from(event.body, 'base64').toString('utf-8');
const formData = new URLSearchParams(body);
const fullName = formData.get('full_name');
if (!fullName) {
const response: APIGatewayProxyResult = {
statusCode: 400,
body: JSON.stringify({
message: 'Full name is required.',
}),
};
return response;
}

const response: APIGatewayProxyResult = {
statusCode: 200,
body: JSON.stringify({ message: `Hello, ${fullName}!` }),
};
return response;
};
16 changes: 16 additions & 0 deletions apps/form-service/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"emitDeclarationOnly": true,
"rootDir": "src"
},
"include": [
"src/**/*"
],
"references": [
{
"path": "../../packages/interviews"
}
]
}
5 changes: 2 additions & 3 deletions apps/spotlight/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,12 @@
"@atj/docassemble": "workspace:*",
"@atj/documents": "workspace:*",
"@atj/interviews": "workspace:*",
"@uswds/uswds": "^3.6.0",
"astro": "^3.3.0",
"astro": "^3.3.3",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@astrojs/check": "^0.2.1",
"@types/react": "^18.2.21"
"@types/react": "^18.2.33"
}
}
28 changes: 28 additions & 0 deletions apps/spotlight/src/layouts/ContentLayout.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
import { type AppContext } from '../context';
import Layout from '../layouts/Layout.astro';
interface Props {
title: string;
context: AppContext;
}
const { context, title } = Astro.props;
---

<Layout context={context} title={title}>
<main id="main-content">
<div class="bg-base-lightest">
<section class="grid-container usa-section">
<div class="grid-row flex-justify-center">
<div class="grid-col-12 tablet:grid-col-12 desktop:grid-col-12">
<div
class="bg-white padding-y-3 padding-x-5 border border-base-lighter"
>
<slot />
</div>
</div>
</div>
</section>
</div>
</main>
</Layout>
38 changes: 28 additions & 10 deletions apps/spotlight/src/layouts/Layout.astro
Original file line number Diff line number Diff line change
@@ -1,34 +1,52 @@
---
import '../styles.css'
import '../styles.css';
import Footer from '../components/Footer.astro';
import UsaBanner from '../components/UsaBanner.astro';
import { type GithubRepository } from '../lib/github';
import { type AppContext } from '../context';
interface Props {
title: string;
github: GithubRepository;
context: AppContext;
}
const { github, title } = Astro.props;
const { context, title } = Astro.props;
---

<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="description" content="10x Access to Justice Spotlight" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script async type="text/javascript" src="https://dap.digitalgov.gov/Universal-Federated-Analytics-Min.js?agency=GSA" id="_fed_an_ua_tag"></script>
<link href="/favicon/apple-touch-icon.png" rel="apple-touch-icon" sizes="180x180">
<link href="/favicon/favicon-32x32.png" rel="icon" sizes="32x32" type="image/png">
<link href="/favicon/favicon-16x16.png" rel="icon" sizes="16x16" type="image/png">
<link href="/favicon/favicon.ico" rel="shortcut icon">
<script
async
type="text/javascript"
src="https://dap.digitalgov.gov/Universal-Federated-Analytics-Min.js?agency=GSA"
id="_fed_an_ua_tag"></script>
<link
href="/favicon/apple-touch-icon.png"
rel="apple-touch-icon"
sizes="180x180"
/>
<link
href="/favicon/favicon-32x32.png"
rel="icon"
sizes="32x32"
type="image/png"
/>
<link
href="/favicon/favicon-16x16.png"
rel="icon"
sizes="16x16"
type="image/png"
/>
<link href="/favicon/favicon.ico" rel="shortcut icon" />
<meta name="generator" content={Astro.generator} />
<script src="../lib/initialize.ts"></script>
<title>{title}</title>
</head>
<body>
<UsaBanner />
<slot />
<Footer github={github} />
<Footer github={context.github} />
</body>
</html>
31 changes: 31 additions & 0 deletions apps/spotlight/src/pages/form-sample.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
import { getAppContext } from '../context';
import ContentLayout from '../layouts/ContentLayout.astro';
const context = getAppContext(import.meta.env);
---

<ContentLayout context={context} title="10x Access to Justice Spotlight">
<h1>10x Access to Justice Spotlight</h1>
<form
action="https://yaawr84uu7.execute-api.us-east-2.amazonaws.com"
method="post"
class="usa-form usa-form--large"
>
<fieldset class="usa-fieldset">
<legend class="usa-legend usa-legend--large"> Sample form</legend>
<p>
Required fields are marked with an asterisk (<abbr
title="required"
class="usa-hint usa-hint--required">*</abbr
>).
</p>
<label class="usa-label" for="full_name"
>Full Name
<abbr title="required" class="usa-hint usa-hint--required">*</abbr>
<input class="usa-input" id="full_name" name="full_name" type="text" />
</label>
<input class="usa-button" type="submit" value="Submit" />
</fieldset>
</form>
</ContentLayout>
33 changes: 13 additions & 20 deletions apps/spotlight/src/pages/index.astro
Original file line number Diff line number Diff line change
@@ -1,27 +1,20 @@
---
import { DocumentAssembler } from '../components/react/document-assembler';
import { getAppContext } from '../context';
import Layout from '../layouts/Layout.astro';
import ContentLayout from '../layouts/ContentLayout.astro';
const context = getAppContext(import.meta.env);
---

<Layout github={context.github} title="10x Access to Justice Spotlight">
<main id="main-content">
<div class="bg-base-lightest">
<section class="grid-container usa-section">
<div class="grid-row flex-justify-center">
<div class="grid-col-12 tablet:grid-col-12 desktop:grid-col-12">
<div class="bg-white padding-y-3 padding-x-5 border border-base-lighter">
<h1>10x Access to Justice Spotlight</h1>
<ul>
<li><a href="https://trello.com/c/25Jl6NwJ/207-digital-access-to-justice-platform">Project overview</a></li>
</ul>
<DocumentAssembler client:load />
</div>
</div>
</div>
</section>
</div>
</main>
</Layout>
<ContentLayout context={context} title="10x Access to Justice Spotlight">
<h1>10x Access to Justice Spotlight</h1>
<ul>
<li>
<a
href="https://trello.com/c/25Jl6NwJ/207-digital-access-to-justice-platform"
>Project overview</a
>
</li>
</ul>
<DocumentAssembler client:load />
</ContentLayout>
12 changes: 6 additions & 6 deletions infra/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
"test:watch": "jest --watch"
},
"dependencies": {
"@cdktf/provider-aws": "17.0.8",
"cdktf": "^0.18.1",
"cdktf-cli": "^0.18.1",
"constructs": "^10.1.176"
"@cdktf/provider-aws": "18.0.1",
"cdktf": "^0.19.0",
"cdktf-cli": "^0.19.0",
"constructs": "^10.3.0"
},
"devDependencies": {
"@types/jest": "^29.5.5",
"@types/node": "^20.8.2",
"@types/jest": "^29.5.6",
"@types/node": "^20.8.7",
"jest": "^29.7.0",
"ts-jest": "^29.1.1"
}
Expand Down
82 changes: 5 additions & 77 deletions infra/src/app-stack.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { App, TerraformOutput, TerraformStack } from 'cdktf';
import { App, TerraformStack } from 'cdktf';
import { Construct } from 'constructs';
import { AwsProvider } from '@cdktf/provider-aws/lib/provider';

import { withBackend } from './backend';
import { LightsailInstance } from '@cdktf/provider-aws/lib/lightsail-instance';
import { LightsailStaticIp } from '@cdktf/provider-aws/lib/lightsail-static-ip';
import { LightsailInstancePublicPorts } from '@cdktf/provider-aws/lib/lightsail-instance-public-ports';
import { LightsailStaticIpAttachment } from '@cdktf/provider-aws/lib/lightsail-static-ip-attachment';
import { Docassemble } from './docassemble';
import { FormService } from './form-service';

export const registerAppStack = (stackPrefix: string) => {
const app = new App();
Expand All @@ -23,77 +21,7 @@ class AppStack extends TerraformStack {
region: 'us-east-2',
});

const lightsailInstance = new LightsailInstance(
this,
'docassemble_lightsail',
{
name: `${id}-docassemble`,
availabilityZone: 'us-east-2a',
blueprintId: 'amazon_linux_2',
bundleId: 'medium_2_0',
// Preliminary logic to spin up a configured docassemble instance:
userData: USER_DATA_COMMAND,
}
);
const staticIp = new LightsailStaticIp(
this,
'docassemble_lightsail_static_ip',
{
name: `${id}-docassemble-static-ip`,
}
);
new LightsailStaticIpAttachment(this, 'test_2', {
instanceName: lightsailInstance.id,
staticIpName: staticIp.name,
});
new LightsailInstancePublicPorts(
this,
`docassemble_lightsail_public_ports`,
{
instanceName: lightsailInstance.name,
portInfo: [
{
fromPort: 80,
protocol: 'tcp',
toPort: 80,
},
{
fromPort: 443,
protocol: 'tcp',
toPort: 443,
},
{
fromPort: 22,
protocol: 'tcp',
toPort: 22,
},
],
}
);

new TerraformOutput(this, 'docassemble_ip_address', {
value: staticIp.ipAddress,
});
new Docassemble(this, `${id}-docassemble`);
new FormService(this, `${id}-form-service`);
}
}

const USER_DATA_COMMAND = `
sudo yum -y update && \
sudo yum -y install docker && \
sudo systemctl enable docker && \
sudo systemctl start docker && \
sudo usermod -a -G docker ec2-user && \
echo "DAHOSTNAME=docassemble.atj.10x.gov
TIMEZONE=America/New_York
USEHTTPS=true
USELETSENCRYPT=true
[email protected]" > /home/ec2-user/env.list && \
sudo chown ec2-user:ec2-user /home/ec2-user/env.list && \
sudo docker run -d \
--env-file /home/ec2-user/env.list \
--volume dabackup:/usr/share/docassemble/backup \
--publish 80:80 \
--publish 443:443 \
--stop-timeout 600 \
jhpyle/docassemble
`;
Loading

0 comments on commit 5c10f32

Please sign in to comment.