1
0
Fork 0

Implement serverless pun api

This commit is contained in:
Andrew Tomaka 2017-03-22 21:35:46 -04:00
parent a21be0fa91
commit 8233572d2b
No known key found for this signature in database
GPG key ID: C78D1A81582BAC86
8 changed files with 2732 additions and 0 deletions

3
.env.sample Normal file
View file

@ -0,0 +1,3 @@
export AWS_CERTIFICATE=arn:aws:acm:REGION:ACCOUNT:certificate/HASH
export DNS_ZONE=example.com.
export DOMAIN=puns.example.com

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
node_modules/
.serverless/
.env
.env.production

4
Makefile Normal file
View file

@ -0,0 +1,4 @@
yarnbin = $(shell yarn bin)
deploy:
sh -c "source .env.production; $(yarnbin)/serverless deploy --stage production"

20
README.md Normal file
View file

@ -0,0 +1,20 @@
# punaday-api
JSON version of http://www.punoftheday.com/
## Description
Server that takes Pun of the Day puns and converts it into JSON. Currently
living as a serverless application in AWS Lamda. It is frontended by AWS
Cloudfront because AWS Cloudformation does not yet have resources for AWS API
Gateway domains. This script will also create a DNS record to point at the
AWS Cloudfront Distribution.
## Usage
* `cp .env.sample .env.production`
* `AWS_CERTIFICATE`: arn to aws certificate that you generated for a domain
* `DNS_ZONE`: Route 53 zone name (ex. `example.com.`)
* `DOMAIN`: Route 53 domain (ex. `puns.example.com`)
* Set AWS credentials via environment or credential file
* `make`

11
package.json Normal file
View file

@ -0,0 +1,11 @@
{
"devDependencies": {
"babel-preset-es2015": "^6.24.0",
"serverless": "^1.9.0",
"serverless-offline": "^3.10.3",
"serverless-plugin-optimize": "^1.0.0-rc.15"
},
"dependencies": {
"axios": "^0.15.3"
}
}

82
serverless.yml Normal file
View file

@ -0,0 +1,82 @@
service: punaday-api
provider:
name: aws
runtime: nodejs4.3
memorySize: 128
plugins:
- serverless-plugin-optimize
- serverless-offline
package:
individually: true
resources:
Resources:
CloudfrontDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Aliases:
- ${env:DOMAIN}
Origins:
- DomainName: { "Fn::Join": [".", [ { "Ref": "ApiGatewayRestApi" }, "execute-api", { "Ref": "AWS::Region" }, "amazonaws.com"] ] }
OriginPath: "/production"
Id: APIGW
CustomOriginConfig:
HTTPSPort: 443
OriginProtocolPolicy: https-only
DefaultCacheBehavior:
AllowedMethods:
- HEAD
- DELETE
- POST
- GET
- OPTIONS
- PUT
- PATCH
TargetOriginId: APIGW
ForwardedValues:
QueryString: true
Cookies:
Forward: none
ViewerProtocolPolicy: redirect-to-https
DefaultTTL: 0
Enabled: true
ViewerCertificate:
AcmCertificateArn: ${env:AWS_CERTIFICATE}
SslSupportMethod: sni-only
Route53RecordSet:
Type: AWS::Route53::RecordSetGroup
Properties:
HostedZoneName: ${env:DNS_ZONE}
RecordSets:
- Name: ${env:DOMAIN}
Type: A
AliasTarget:
DNSName: { "Fn::GetAtt" : [ "CloudfrontDistribution", "DomainName"] }
HostedZoneId: Z2FDTNDATAQYW2 # always use for cloudfront
functions:
punsShow:
handler: src/puns/show.show
events:
- http:
path: puns/{id}
method: get
cors: true
punsToday:
handler: src/puns/show.today
events:
- http:
path: puns/today
method: get
cors: true
punsRandom:
handler: src/puns/show.random
events:
- http:
path: puns/random
method: get
cors: true

53
src/puns/show.js Normal file
View file

@ -0,0 +1,53 @@
const axios = require('axios')
const PUN_BASE = 'http://www.punoftheday.com'
module.exports.today = ((event, context, callback) => {
respondWithPunFrom(PUN_BASE, callback)
})
module.exports.random = ((event, context, callback) => {
respondWithPunFrom(`${PUN_BASE}/cgi-bin/randompun.pl`, callback)
})
module.exports.show = ((event, context, callback) => {
const id = event.pathParameters.id
respondWithPunFrom(`${PUN_BASE}/pun/${id}`, callback)
})
function respondWithPunFrom(url, callback) {
return axios.get(url)
.then(response => {
const pun = parsePun(response.data)
callback(null, punResponse(pun))
})
.catch(error => {
callback(null, errorResponse())
})
}
function parsePun(html) {
const punMatches = html.match(/<p>(.*)<\/p>/)
const text = punMatches[1].replace('&#8220;', '').replace('&#8221;', '')
const idMatches = html.match(/name="PunID" value="(\d+)"/)
const id = idMatches[1]
const urlMatches = html.match(/class="fb-share-button" data-href="(.*)" d/)
const url = urlMatches[1]
return { id, text, url }
}
function punResponse (pun) {
delete pun['id']
return {
statusCode: 200,
body: JSON.stringify(pun)
}
}
function errorResponse () {
return { statusCode: 500 }
}

2555
yarn.lock Normal file

File diff suppressed because it is too large Load diff