mirror of
https://github.com/GenderDysphoria/GenderDysphoria.fyi.git
synced 2025-01-31 07:16:17 +00:00
Updated terraform config for newer aws provider, deleted old lambdas
This commit is contained in:
parent
bb52a23297
commit
77bd70819a
1
.gitignore
vendored
1
.gitignore
vendored
@ -23,3 +23,4 @@ node_modules
|
|||||||
/analytics/database.sqlite
|
/analytics/database.sqlite
|
||||||
/analytics/RAW
|
/analytics/RAW
|
||||||
/public/gdb.zip
|
/public/gdb.zip
|
||||||
|
/terraform/secret.auto.tfvars
|
||||||
|
99
terraform/.terraform.lock.hcl
generated
99
terraform/.terraform.lock.hcl
generated
@ -2,39 +2,82 @@
|
|||||||
# Manual edits may be lost in future updates.
|
# Manual edits may be lost in future updates.
|
||||||
|
|
||||||
provider "registry.terraform.io/hashicorp/archive" {
|
provider "registry.terraform.io/hashicorp/archive" {
|
||||||
version = "2.1.0"
|
version = "2.3.0"
|
||||||
hashes = [
|
hashes = [
|
||||||
"h1:Rjd4bHMA69V+16tiriAUTW8vvqoljzNLmEaRBCgzpUs=",
|
"h1:NaDbOqAcA9d8DiAS5/6+5smXwN3/+twJGb3QRiz6pNw=",
|
||||||
"h1:f3WXKM/FBu5EMY6j2BGt982hzVMNicrxTyEAz5EsrOU=",
|
"zh:0869128d13abe12b297b0cd13b8767f10d6bf047f5afc4215615aabc39c2eb4f",
|
||||||
"zh:033279ecbf60f565303222e9a6d26b50fdebe43aa1c6e8f565f09bb64d67c3fd",
|
"zh:481ed837d63ba3aa45dd8736da83e911e3509dee0e7961bf5c00ed2644f807b3",
|
||||||
"zh:0af998e42eb421c92e87202df5bfee436b3cfe553214394f08d786c72a9e3f70",
|
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
|
||||||
"zh:1183b661c692f38409a61eefb5d412167c246fcd9e49d4d61d6d910012d206ba",
|
"zh:9f08fe2977e2166849be24fb9f394e4d2697414d463f7996fd0d7beb4e19a29c",
|
||||||
"zh:5febb66f4a8207117f71dcd460fb9c81d3afb7b600b5e598cf517cf6e27cf4b2",
|
"zh:9fe566deeafd460d27999ca0bbfd85426a5fcfcb40007b23884deb76da127b6f",
|
||||||
"zh:66135ce46d29d0ccf0e3b6a119423754ca334dbf4266bc989cce5b0b667b5fde",
|
"zh:a1bd9a60925d9769e0da322e4523330ee86af9dc2e770cba1d0247a999ef29cb",
|
||||||
"zh:6b9dc1a4f0a680bb650a7191784927f99675a8c8dd3c155ba821185f630db604",
|
"zh:bb4094c8149f74308b22a87e1ac19bcccca76e8ef021b571074d9bccf1c0c6f0",
|
||||||
"zh:91e249482c016ecf6bf8b83849964005cd2d0b4396688419cd1752809b46b23e",
|
"zh:c8984c9def239041ce41ec8e19bbd76a49e74ed2024ff736dad60429dee89bcc",
|
||||||
"zh:a6a2e5f2f010c511e66174cb84ea18899e8bcfc1354c4b9fed972fdb131ffffc",
|
"zh:ea4bb5ae73db1de3a586e62f39106f5e56770804a55aa5e6b4f642df973e0e75",
|
||||||
"zh:bb1f6abc76552a883732caff897ff7b07a91977a9b4bb97915f6aac54116bb65",
|
"zh:f44a9d596ecc3a8c5653f56ba0cd202ad93b49f76767f4608daf7260b813289e",
|
||||||
"zh:f05a9a63607f85719fde705f58d82ee16fa67f9158a5c3424c0216507631eddf",
|
"zh:f5c5e6cc9f7f070020ab7d95fcc9ed8e20d5cf219978295a71236e22cbb6d508",
|
||||||
"zh:fc603a05a06814387ffa4a054d1baee8ea6b5ab32c53cb73e90a5bf9a2616777",
|
"zh:fd2273f51dcc8f43403bf1e425ba9db08a57c3ddcba5ad7a51742ccde21ca611",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
provider "registry.terraform.io/hashicorp/aws" {
|
provider "registry.terraform.io/hashicorp/aws" {
|
||||||
version = "3.30.0"
|
version = "4.55.0"
|
||||||
hashes = [
|
hashes = [
|
||||||
"h1:PmKa3uxO2mDA5FJfGmpX+4e0x70vFLV5Ka9NxkuMpUo=",
|
"h1:vSVjfh4GIrca2Z3YPjWMaac5hOEBc1U3xOOwFmo7HZc=",
|
||||||
"h1:z9kdXY2A/+dIZrPy9hNlg/B5I/AuETQsp0jz9EgprIQ=",
|
"zh:0866f25575bad3b9c313cd778c94fc65e79d335af2d20a3480f79d7731d93b7b",
|
||||||
"zh:01f562a6a31fe46a8ca74804f360e3452b26f71abc549ce1f0ab5a8af2484cdf",
|
"zh:2c05c16155cbc054622cf83e4b6614fef35935b00b238e4c21ee225e6c896770",
|
||||||
"zh:25bacc5ed725051f0ab1f7d575e45c901e5b8e1d50da4156a31dda92b2b7e481",
|
"zh:2efba66649fb12af0492c6cce4e2361fe9139df648734264f61a9a1ef754df53",
|
||||||
"zh:349b79979d9169db614d8ebd1bc2e0caeb7a38dc816e261b8b2b4b5204615519",
|
"zh:3c60bb53e3b65d7f86699fae0797a55a9aa41b8ba377aaff4daf23d1661393a9",
|
||||||
"zh:5e41446acc54c6fc15e82c3fa14b72174b30eba81e0711ede297e5620c55a628",
|
"zh:41f6dcd90b54b623d523df8fb4a30779cfe22e9ab59516bc05b29291a7af0946",
|
||||||
"zh:68ad98f6d612bdc35a65d48950abc8e75c69decb49db28258ce8eeb5458586b7",
|
"zh:4b8330b154e9e2d035dd5488abcac25efec1fa6055d3a70894a8c0384f0579d6",
|
||||||
"zh:704603d65e8bac17d203b57c2db142c3134a91076e1b4a31c40f75eb3257dde8",
|
"zh:595f263706cf1fb6b8447e2ec343638de4360841a15e6bff6ccbb0ff86c7ce74",
|
||||||
"zh:a362c700032b2db047d16007d52f28b3f216d32671b6b355d23bdaa082c66a4b",
|
"zh:5dfc5b858a43cf45fde5542eb673f6104c14cdc3d73843d1b87a9e44545cbad4",
|
||||||
"zh:bd197797b41268de3c93cad02b7c655dc0c4d8661abb37544ca049e6b1eccae6",
|
"zh:7bbe05cf30521f0110603bb84995a4025ce7810626010276600e4b402143df27",
|
||||||
"zh:deb12ef0e3396a71d485977ddc14b695775f7937097ebf2b2f53ed348a4365e7",
|
"zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
|
||||||
"zh:ec8a7d0f02738f290107d39bf401d68ddce82a95cd9d998003f7e04b3a196411",
|
"zh:a490e68c63504d3301d6dcb700c95778d93bb2baa6632a46c5a1d62862a7067c",
|
||||||
"zh:ffcc43b6c5e7f26c55e2a8c539d7370fca8042722400a3e06bdce4240bd7088a",
|
"zh:c4f9f6659148528375c8a822163925c9aae490ccce2e6301cefbbab009531971",
|
||||||
|
"zh:ef66070f957408f1c924ddfd5dbd0d34bce16efd9e36ccecbf699de72beb131f",
|
||||||
|
"zh:f7ba5e3e62a2b51b24e326797a89fdd86bafaea7d1912738d514c9903c14d7f2",
|
||||||
|
"zh:ffc20b7d9f7bd331fb6451d0fc92c68196383d7115e69380de6566cc268cb9b9",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "registry.terraform.io/hashicorp/http" {
|
||||||
|
version = "3.2.1"
|
||||||
|
hashes = [
|
||||||
|
"h1:Q2YQZzEhHQVlkQCQVpMzFVs0Gg+eXzISbOwaOYqpflc=",
|
||||||
|
"zh:088b3b3128034485e11dff8da16e857d316fbefeaaf5bef24cceda34c6980641",
|
||||||
|
"zh:09ed1f2462ea4590b112e048c4af556f0b6eafc7cf2c75bb2ac21cd87ca59377",
|
||||||
|
"zh:39c6b0b4d3f0f65e783c467d3f634e2394820b8aef907fcc24493f21dcf73ca3",
|
||||||
|
"zh:47aab45327daecd33158a36c1a36004180a518bf1620cdd5cfc5e1fe77d5a86f",
|
||||||
|
"zh:4d70a990aa48116ab6f194eef393082c21cf58bece933b63575c63c1d2b66818",
|
||||||
|
"zh:65470c43fda950c7e9ac89417303c470146de984201fff6ef84299ea29e02d30",
|
||||||
|
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
|
||||||
|
"zh:842b4dd63e438f5cd5fdfba1c09b8fdf268e8766e6690988ee24e8b25bfd9e8d",
|
||||||
|
"zh:a167a057f7e2d80c78d4b4057538588131fceb983d5c93b07675ad9eb1aa5790",
|
||||||
|
"zh:d0ba69b62b6db788cfe3cf8f7dc6e9a0eabe2927dc119d7fe3fe6573ee559e66",
|
||||||
|
"zh:e28d24c1d5ff24b1d1cc6f0074a1f41a6974f473f4ff7a37e55c7b6dca68308a",
|
||||||
|
"zh:fde8a50554960e5366fd0e1ca330a7c1d24ae6bbb2888137a5c83d83ce14fd18",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "registry.terraform.io/namecheap/namecheap" {
|
||||||
|
version = "2.1.0"
|
||||||
|
constraints = ">= 2.0.0"
|
||||||
|
hashes = [
|
||||||
|
"h1:wYjp1Gs7wf2J+VtkLmTRO6mALieCFhS0HYsQUwAbAUI=",
|
||||||
|
"zh:3731f5f14a0958cd27a589ef7daa9be786b6490f2309c429eb2e9862aa4ac5f7",
|
||||||
|
"zh:3cbceb12ec3521d9dfbd890eee731a40f4e1f42de30d28fc1d1e524091148caa",
|
||||||
|
"zh:44095af1b1d1ee6d4b930e21e3c5bf0f81d9df65fe04f6f1e55d46713c240b21",
|
||||||
|
"zh:693e169228fe0c5fb1989425b1ad42c1206f8187c9932b4daee5a5c5e851a28e",
|
||||||
|
"zh:6b04c3c2666db3050f49bc85151496fe33cf852db9ad8fc6f455d1daf0a2bba6",
|
||||||
|
"zh:85fd126a573cc468f8d5d1b90f4a94f5977ea40623b1c5cd7c799bb95ef233bd",
|
||||||
|
"zh:99014437ef4e96161b0029efa12f05fa1ab63ff9bc0a255b0a249e17b4f8587a",
|
||||||
|
"zh:a4d8288ef01d4002a5aa07d1e64e4504757f07d6ada24fbf7d3670ceb24d2871",
|
||||||
|
"zh:d27f7798cbe1957294bb08459b1fbabe68721cc9cc50afee80bda87ce674dab8",
|
||||||
|
"zh:d85483f90380829d05b8a2725ce50bf2ee766d6c1cbef223b388d19c5a92dce2",
|
||||||
|
"zh:ddfecfbefd32e40386b482a2610e4173a52591afea3861f041041439d51d34da",
|
||||||
|
"zh:f9a10edfe11dbc4947cbb2f0db8935050693d5fff3b6559096288c689c2dd847",
|
||||||
|
"zh:fae14a74781a94bcaac07b6d533dd9eb1e40c1d152eb6ee49b2a44cdf5740cfe",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,90 +0,0 @@
|
|||||||
/* eslint no-console:0 */
|
|
||||||
|
|
||||||
const AWS = require('aws-sdk');
|
|
||||||
const zlib = require('zlib');
|
|
||||||
const util = require('util');
|
|
||||||
const path = require('path');
|
|
||||||
const { URL } = require('url');
|
|
||||||
const s3 = new AWS.S3();
|
|
||||||
const { parse: parseLog } = require('cloudfront-log-parser');
|
|
||||||
const parseUA = require('ua-parser-js');
|
|
||||||
const format = require('date-fns/format');
|
|
||||||
const gunzip = util.promisify(zlib.gunzip);
|
|
||||||
const gzip = util.promisify(zlib.gzip);
|
|
||||||
|
|
||||||
function url (input) {
|
|
||||||
const { hash, host, hostname, href, origin, password, pathname, port, protocol, search, searchParams, username } = new URL(input);
|
|
||||||
return { hash, host, hostname, href, origin, password, pathname, port, protocol, search, searchParams, username };
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.handler = async (event) => {
|
|
||||||
// Read options from the event.
|
|
||||||
console.log('Reading options from event:\n', JSON.stringify(event, null, 2));
|
|
||||||
|
|
||||||
const Bucket = event.Records[0].s3.bucket.name;
|
|
||||||
const inputKey = event.Records[0].s3.object.key;
|
|
||||||
|
|
||||||
const file = path.parse(inputKey);
|
|
||||||
const outputKey = path.format({ ...file, dir: 'Converted', ext: '.json.gz' });
|
|
||||||
|
|
||||||
const response = await s3.getObject({ Bucket, Key: inputKey }).promise();
|
|
||||||
const input = (await gunzip(response.Body)).toString('utf8');
|
|
||||||
|
|
||||||
const entries = parseLog(input, { format: 'web' });
|
|
||||||
|
|
||||||
console.log(`Found ${entries.length} rows`);
|
|
||||||
|
|
||||||
const results = entries.map((row) => {
|
|
||||||
// filter out OPTIONS calls
|
|
||||||
if (row['cs-method'] === 'OPTIONS') return null;
|
|
||||||
|
|
||||||
// I only care about the pixel hits, nothing else.
|
|
||||||
if (row['cs-uri-stem'] !== '/i') return null;
|
|
||||||
|
|
||||||
// this isn't an analytics event
|
|
||||||
if (row['cs-referer'] === '-') return null;
|
|
||||||
|
|
||||||
row = Object.fromEntries(Object.entries(row).map(([ k, v ]) => [ k.replace(/-/g, '_'), v ]));
|
|
||||||
|
|
||||||
const query = (row.cs_uri_query === '-')
|
|
||||||
? {}
|
|
||||||
: Object.fromEntries(new URLSearchParams(row.cs_uri_query))
|
|
||||||
;
|
|
||||||
|
|
||||||
// we didn't get analytics data from this load, ignore it
|
|
||||||
if (!query.start) return null;
|
|
||||||
|
|
||||||
const useragent = parseUA(row.cs_user_agent);
|
|
||||||
|
|
||||||
const sessionStart = Number(query.start);
|
|
||||||
const sessionEnd = query.end === 'null' ? 0 : Number(query.end);
|
|
||||||
const duration = sessionEnd > sessionStart ? Math.floor((sessionEnd - sessionStart) / 1000) : null;
|
|
||||||
|
|
||||||
return JSON.stringify({
|
|
||||||
dts: `${row.date} ${row.time}`,
|
|
||||||
url: url(row.cs_referer),
|
|
||||||
referer: url(query.referer),
|
|
||||||
client_start: format(new Date(sessionStart), 'yyyy-MM-dd HH:mm:ss'),
|
|
||||||
client_end: sessionEnd ? format(new Date(sessionStart), 'yyyy-MM-dd HH:mm:ss') : null,
|
|
||||||
duration,
|
|
||||||
useragent,
|
|
||||||
query,
|
|
||||||
original: row,
|
|
||||||
});
|
|
||||||
}).filter(Boolean);
|
|
||||||
|
|
||||||
if (!results.length) {
|
|
||||||
console.log('No results to save');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Writing new file to ' + outputKey);
|
|
||||||
|
|
||||||
await s3.putObject({
|
|
||||||
Bucket,
|
|
||||||
Key: outputKey,
|
|
||||||
Body: await gzip(Buffer.from(results.join('\n'))),
|
|
||||||
ContentType: 'application/gzip',
|
|
||||||
}).promise();
|
|
||||||
|
|
||||||
};
|
|
117
terraform/files/decorate/package-lock.json
generated
117
terraform/files/decorate/package-lock.json
generated
@ -1,117 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "decorate",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"lockfileVersion": 1,
|
|
||||||
"requires": true,
|
|
||||||
"dependencies": {
|
|
||||||
"aws-sdk": {
|
|
||||||
"version": "2.975.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.975.0.tgz",
|
|
||||||
"integrity": "sha512-cmRcM+gU+rfW1RZjca3bdbRTQV6WHuCI6NuqnDgd5HoqufylCrNI/wtysPK1IMJLsAhrcsY0U0YRx+Y/hxQNoQ==",
|
|
||||||
"requires": {
|
|
||||||
"buffer": "4.9.2",
|
|
||||||
"events": "1.1.1",
|
|
||||||
"ieee754": "1.1.13",
|
|
||||||
"jmespath": "0.15.0",
|
|
||||||
"querystring": "0.2.0",
|
|
||||||
"sax": "1.2.1",
|
|
||||||
"url": "0.10.3",
|
|
||||||
"uuid": "3.3.2",
|
|
||||||
"xml2js": "0.4.19"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"base64-js": {
|
|
||||||
"version": "1.5.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
|
||||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
|
|
||||||
},
|
|
||||||
"buffer": {
|
|
||||||
"version": "4.9.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
|
|
||||||
"integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
|
|
||||||
"requires": {
|
|
||||||
"base64-js": "^1.0.2",
|
|
||||||
"ieee754": "^1.1.4",
|
|
||||||
"isarray": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"cloudfront-log-parser": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/cloudfront-log-parser/-/cloudfront-log-parser-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-g1lxh8aW5ZrQ7/loX2/vLzz4SWefQhSvZw++wgoIx3aEugXHKyfYaWOXGS4pNNp9hi7JcXITxLYqBI2FY+jtgA=="
|
|
||||||
},
|
|
||||||
"date-fns": {
|
|
||||||
"version": "2.9.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.9.0.tgz",
|
|
||||||
"integrity": "sha512-khbFLu/MlzLjEzy9Gh8oY1hNt/Dvxw3J6Rbc28cVoYWQaC1S3YI4xwkF9ZWcjDLscbZlY9hISMr66RFzZagLsA=="
|
|
||||||
},
|
|
||||||
"events": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
|
|
||||||
"integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ="
|
|
||||||
},
|
|
||||||
"ieee754": {
|
|
||||||
"version": "1.1.13",
|
|
||||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
|
|
||||||
"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
|
|
||||||
},
|
|
||||||
"isarray": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
|
||||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
|
||||||
},
|
|
||||||
"jmespath": {
|
|
||||||
"version": "0.15.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz",
|
|
||||||
"integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc="
|
|
||||||
},
|
|
||||||
"punycode": {
|
|
||||||
"version": "1.3.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
|
|
||||||
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
|
|
||||||
},
|
|
||||||
"querystring": {
|
|
||||||
"version": "0.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
|
|
||||||
"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
|
|
||||||
},
|
|
||||||
"sax": {
|
|
||||||
"version": "1.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz",
|
|
||||||
"integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o="
|
|
||||||
},
|
|
||||||
"ua-parser-js": {
|
|
||||||
"version": "0.7.21",
|
|
||||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz",
|
|
||||||
"integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ=="
|
|
||||||
},
|
|
||||||
"url": {
|
|
||||||
"version": "0.10.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
|
|
||||||
"integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=",
|
|
||||||
"requires": {
|
|
||||||
"punycode": "1.3.2",
|
|
||||||
"querystring": "0.2.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"uuid": {
|
|
||||||
"version": "3.3.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
|
|
||||||
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
|
|
||||||
},
|
|
||||||
"xml2js": {
|
|
||||||
"version": "0.4.19",
|
|
||||||
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
|
|
||||||
"integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
|
|
||||||
"requires": {
|
|
||||||
"sax": ">=0.6.0",
|
|
||||||
"xmlbuilder": "~9.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"xmlbuilder": {
|
|
||||||
"version": "9.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
|
|
||||||
"integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "decorate",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
|
||||||
},
|
|
||||||
"author": "Jocelyn Badgley <joc@twipped.com> (http://twipped.com/)",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"aws-sdk": "~2.975.0",
|
|
||||||
"cloudfront-log-parser": "~1.1.0",
|
|
||||||
"date-fns": "~2.9.0",
|
|
||||||
"ua-parser-js": "~0.7.21"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12.14.0"
|
|
||||||
}
|
|
||||||
}
|
|
@ -26,62 +26,73 @@ resource "aws_iam_role" "lambda_redirect" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#######################################################
|
||||||
|
# LOGGING POLICY
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------
|
data "aws_iam_policy_document" "lambda_logging" {
|
||||||
# IAM Role for Log Parsing Lambda
|
|
||||||
|
|
||||||
data "aws_iam_policy_document" "s3_bucket_access" {
|
|
||||||
statement {
|
statement {
|
||||||
actions = [
|
actions = [
|
||||||
"s3:*",
|
"logs:CreateLogStream",
|
||||||
|
"logs:PutLogEvents",
|
||||||
|
"logs:CreateLogGroup"
|
||||||
]
|
]
|
||||||
|
|
||||||
resources = [
|
resources = [ "arn:aws:logs:*:*:*" ]
|
||||||
aws_s3_bucket.ipixel_logs.arn,
|
|
||||||
"${aws_s3_bucket.ipixel_logs.arn}/*",
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data "aws_iam_policy_document" "lambda_assume_role" {
|
resource "aws_iam_policy" "lambda_logging" {
|
||||||
statement {
|
name = "${var.site}_lambda_logging"
|
||||||
actions = ["sts:AssumeRole"]
|
path = "/"
|
||||||
|
description = "IAM policy for logging from a lambda"
|
||||||
|
|
||||||
principals {
|
policy = data.aws_iam_policy_document.lambda_logging.json
|
||||||
type = "Service"
|
}
|
||||||
identifiers = [
|
|
||||||
"edgelambda.amazonaws.com",
|
resource "aws_iam_role_policy_attachment" "lambda_logs" {
|
||||||
"lambda.amazonaws.com"
|
role = aws_iam_role.lambda_redirect.name
|
||||||
]
|
policy_arn = aws_iam_policy.lambda_logging.arn
|
||||||
|
}
|
||||||
|
|
||||||
|
#######################################################
|
||||||
|
# REPLICATION POLICY
|
||||||
|
|
||||||
|
# aws_iam_policy_document.lambda_replication
|
||||||
|
data "aws_iam_policy_document" "lambda_replication" {
|
||||||
|
statement {
|
||||||
|
actions = [
|
||||||
|
"lambda:EnableReplication*",
|
||||||
|
]
|
||||||
|
resources = [
|
||||||
|
"*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
statement {
|
||||||
|
actions = [
|
||||||
|
"iam:CreateServiceLinkedRole"
|
||||||
|
]
|
||||||
|
resources = [
|
||||||
|
"arn:aws:iam::*:role/aws-service-role/events.amazonaws.com/AWSServiceRoleForCloudWatchEvents*"
|
||||||
|
]
|
||||||
|
condition {
|
||||||
|
test = "StringLike"
|
||||||
|
variable = "iam:AWSServiceName"
|
||||||
|
values = ["events.amazonaws.com"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_iam_role" "ipixel_parser" {
|
resource "aws_iam_policy" "lambda_replication" {
|
||||||
name = "lambda-${var.site}-ipixel"
|
name = "${var.site}_lambda_replication"
|
||||||
|
path = "/"
|
||||||
|
description = "IAM policy for replication by lambda@edge"
|
||||||
|
|
||||||
assume_role_policy = data.aws_iam_policy_document.lambda_assume_role.json
|
policy = data.aws_iam_policy_document.lambda_replication.json
|
||||||
|
|
||||||
tags = {
|
|
||||||
Site = var.site,
|
|
||||||
Role = "ipixel"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_iam_role_policy_attachment" "ipixel_parser" {
|
resource "aws_iam_role_policy_attachment" "lambda_replication" {
|
||||||
role = aws_iam_role.ipixel_parser.name
|
role = aws_iam_role.lambda_redirect.name
|
||||||
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
|
policy_arn = aws_iam_policy.lambda_replication.arn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
resource "aws_iam_role_policy" "ipixel_parser_cloudwatch_log_group" {
|
|
||||||
name = "cloudwatch-log-group"
|
|
||||||
role = aws_iam_role.ipixel_parser.name
|
|
||||||
policy = data.aws_iam_policy_document.ipixel_parser_cloudwatch_log_group.json
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "aws_iam_role_policy" "lambda_s3_bucket_readonly" {
|
|
||||||
name = "s3-bucket-readonly"
|
|
||||||
role = aws_iam_role.ipixel_parser.name
|
|
||||||
policy = data.aws_iam_policy_document.s3_bucket_access.json
|
|
||||||
}
|
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "@twipped/eslint-config/node-cjs",
|
|
||||||
"rules": {
|
|
||||||
"node/no-unpublished-require": 0,
|
|
||||||
"indent": [ 2, 2, {
|
|
||||||
"MemberExpression": 1
|
|
||||||
} ]
|
|
||||||
}
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
12.13
|
|
@ -1,2 +0,0 @@
|
|||||||
|
|
||||||
module.exports = exports = require('./src/index');
|
|
2226
terraform/lambda/package-lock.json
generated
2226
terraform/lambda/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "cloudfront-logs",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"dependencies": {
|
|
||||||
"aws-sdk": "~2.966.0",
|
|
||||||
"date-fns": "~2.23.0",
|
|
||||||
"ua-parser-js": "~0.7.28"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"eslint": "~7.32.0",
|
|
||||||
"eslint-config-airbnb-base": "*",
|
|
||||||
"eslint-plugin-import": "~2.24.0"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"lint": "eslint ."
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12.14.0"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,165 +0,0 @@
|
|||||||
/* eslint no-console: 0 */
|
|
||||||
|
|
||||||
const { gunzip } = require('zlib');
|
|
||||||
const { promisify } = require('util');
|
|
||||||
const { S3 } = require('aws-sdk');
|
|
||||||
const { unescape } = require('querystring');
|
|
||||||
const parseUA = require('ua-parser-js');
|
|
||||||
const format = require('date-fns/format');
|
|
||||||
const { URL } = require('url');
|
|
||||||
|
|
||||||
const gunzipAsync = promisify(gunzip);
|
|
||||||
|
|
||||||
function url (input) {
|
|
||||||
try {
|
|
||||||
const { hash, host, hostname, href, origin, password, pathname, port, protocol, search, searchParams, username } = new URL(input); // eslint-disable-line max-len
|
|
||||||
return { hash, host, hostname, href, origin, password, pathname, port, protocol, search, searchParams, username };
|
|
||||||
} catch (e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parsing the line containing the version.
|
|
||||||
//
|
|
||||||
// Format:
|
|
||||||
//
|
|
||||||
// #Version: 1.0
|
|
||||||
//
|
|
||||||
const parseVersion = (line) => {
|
|
||||||
if (!line.startsWith('#Version:')) {
|
|
||||||
throw new Error(`Invalid version line '${line}'`);
|
|
||||||
} else {
|
|
||||||
return line.match(/[\d.]+$/);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parsing the line containinge the fields format and use kebab case.
|
|
||||||
// https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html#LogFileFormat
|
|
||||||
//
|
|
||||||
// Format:
|
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
// #Fields: date time x-edge-location sc-bytes c-ip cs-method cs(Host) cs-uri-stem sc-status cs(Referer) cs(User-Agent) cs-uri-query cs(Cookie) x-edge-result-type x-edge-request-id x-host-header cs-protocol cs-bytes time-taken x-forwarded-for ssl-protocol ssl-cipher x-edge-response-result-type cs-protocol-version fle-status fle-encrypted-fields
|
|
||||||
//
|
|
||||||
const parseFields = (line) => {
|
|
||||||
if (!line.startsWith('#Fields:')) {
|
|
||||||
throw new Error(`Invalid fields line '${line}'`);
|
|
||||||
} else {
|
|
||||||
return line.match(/[\w()-]+(\s|$)/g).map((field) => (
|
|
||||||
// Strip parentheses and remove unecessary abbreviations in field names
|
|
||||||
field.replace(/\(([^)]+)\)/, '-$1').replace(/^(c-|cs-|sc-)/, '').trim().toLowerCase()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Unescape value twice (because fuck you that's why).
|
|
||||||
// https://forums.aws.amazon.com/thread.jspa?threadID=134017
|
|
||||||
//
|
|
||||||
const decode = (value) => unescape(unescape(value));
|
|
||||||
|
|
||||||
// Split up line and assign to corresponding field.
|
|
||||||
//
|
|
||||||
const parseLine = (line, fields) => {
|
|
||||||
if (line.startsWith('#')) {
|
|
||||||
throw new Error(`Invalid log line '${line}'`);
|
|
||||||
} else {
|
|
||||||
let row = line.split('\t').reduce((object, section, index) => {
|
|
||||||
const result = object;
|
|
||||||
if (section !== '-') result[fields[index]] = decode(section); // Skip missing fields
|
|
||||||
return result;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
|
|
||||||
// filter out OPTIONS calls
|
|
||||||
if (row.method === 'OPTIONS') return;
|
|
||||||
|
|
||||||
// I only care about the pixel hits, nothing else.
|
|
||||||
if (row['uri-stem'] !== '/i') return;
|
|
||||||
|
|
||||||
// this isn't an analytics event
|
|
||||||
if (!row.referer) return;
|
|
||||||
|
|
||||||
row = Object.fromEntries(Object.entries(row).map(([ k, v ]) => [ k.replace(/-/g, '_'), v ]));
|
|
||||||
|
|
||||||
const query = (row.uri_query)
|
|
||||||
? Object.fromEntries(new URLSearchParams(row.uri_query))
|
|
||||||
: {}
|
|
||||||
;
|
|
||||||
|
|
||||||
const useragent = parseUA(row.user_agent);
|
|
||||||
|
|
||||||
const sessionStart = Number(query.start);
|
|
||||||
const sessionEnd = query.end === 'null' ? 0 : Number(query.end);
|
|
||||||
const duration = sessionEnd > sessionStart ? Math.floor((sessionEnd - sessionStart) / 1000) : null;
|
|
||||||
|
|
||||||
let {
|
|
||||||
language,
|
|
||||||
viewed,
|
|
||||||
max_scroll,
|
|
||||||
page_height,
|
|
||||||
viewport_height,
|
|
||||||
} = query;
|
|
||||||
|
|
||||||
max_scroll = parseInt(max_scroll, 10) || 0;
|
|
||||||
page_height = parseInt(page_height, 10) || 0;
|
|
||||||
viewport_height = parseInt(viewport_height, 10) || 0;
|
|
||||||
|
|
||||||
const { pathname } = url(row.referer) || {};
|
|
||||||
const { hostname: referrer_host, href: referrer } = url(query.referrer) || {};
|
|
||||||
|
|
||||||
const result = {
|
|
||||||
dts: `${row.date} ${row.time}`,
|
|
||||||
ip: row.ip,
|
|
||||||
tid: query.tid !== 'false' ? query.tid : null,
|
|
||||||
url: pathname,
|
|
||||||
referrer,
|
|
||||||
referrer_host,
|
|
||||||
client_start: format(new Date(sessionStart), 'yyyy-MM-dd HH:mm:ss'),
|
|
||||||
client_end: sessionEnd ? format(new Date(sessionStart), 'yyyy-MM-dd HH:mm:ss') : null,
|
|
||||||
duration,
|
|
||||||
language,
|
|
||||||
viewed,
|
|
||||||
max_scroll,
|
|
||||||
page_height,
|
|
||||||
viewport_height,
|
|
||||||
browser: useragent.browser.name,
|
|
||||||
browser_version: useragent.browser.major,
|
|
||||||
os: useragent.os.name + ' ' + useragent.os.version,
|
|
||||||
device_type: useragent.device && useragent.device.type || null,
|
|
||||||
device: useragent.device && useragent.device.vendor && useragent.device.vendor + ' ' + useragent.device.model || null,
|
|
||||||
useragent,
|
|
||||||
query,
|
|
||||||
original: row,
|
|
||||||
};
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get log file from S3 and unzip it.
|
|
||||||
//
|
|
||||||
const getLogFile = async ({ bucket, key, region }) => {
|
|
||||||
const s3 = new S3({ region });
|
|
||||||
|
|
||||||
const zippedObject = await s3.getObject({ Bucket: bucket, Key: key }).promise();
|
|
||||||
const logFile = await gunzipAsync(zippedObject.Body);
|
|
||||||
return logFile.toString().trim();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse log file and return a list of log events.
|
|
||||||
//
|
|
||||||
exports.parseLogFile = async ({ bucket, key, region }) => {
|
|
||||||
const file = await getLogFile({ bucket, key, region });
|
|
||||||
|
|
||||||
const lines = file.split('\n');
|
|
||||||
|
|
||||||
// Shift first line which contains the version and parse it for validation
|
|
||||||
parseVersion(lines.shift());
|
|
||||||
// Shift next line containing fields format and parse it for validation
|
|
||||||
const fields = parseFields(lines.shift());
|
|
||||||
|
|
||||||
console.log(`Found ${lines.length} rows to parse`); // eslint-disable-line no-console
|
|
||||||
const rows = lines.map((line) => parseLine(line, fields)).filter(Boolean);
|
|
||||||
console.log(`Produced ${rows.length} results`);
|
|
||||||
console.log('Sample', rows[0]);
|
|
||||||
return rows;
|
|
||||||
};
|
|
@ -1,88 +0,0 @@
|
|||||||
const { CloudWatchLogs } = require('aws-sdk');
|
|
||||||
|
|
||||||
// Split up ARN like "arn:aws:logs:eu-west-1:123456789012:log-group:example-group:*"
|
|
||||||
const [ ,,, region,,, logGroupName ] = process.env.CLOUDWATCH_LOGS_GROUP_ARN.split(':');
|
|
||||||
|
|
||||||
const cloudwatchlogs = new CloudWatchLogs({ region });
|
|
||||||
|
|
||||||
|
|
||||||
// Group array of hashes by defined key.
|
|
||||||
//
|
|
||||||
const groupBy = (array, key) => (
|
|
||||||
array.reduce((object, item) => {
|
|
||||||
const result = object;
|
|
||||||
|
|
||||||
if (result[item[key]]) {
|
|
||||||
result[item[key]].push(item);
|
|
||||||
} else if (item[key]) {
|
|
||||||
result[item[key]] = [ item ];
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}, {})
|
|
||||||
);
|
|
||||||
|
|
||||||
// Find log stream by prefix.
|
|
||||||
//
|
|
||||||
const findLogStream = async (logStreamNamePrefix) => {
|
|
||||||
const params = { logGroupName, logStreamNamePrefix };
|
|
||||||
|
|
||||||
const { logStreams } = await cloudwatchlogs.describeLogStreams(params).promise();
|
|
||||||
|
|
||||||
if (logStreams.length > 1) {
|
|
||||||
throw new Error(`Found '${logStreams.length}' matching CloudWatch Logs streams but expected only one.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return logStreams[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get log stream or creting it if not present yet.
|
|
||||||
//
|
|
||||||
// Name format:
|
|
||||||
// 2000-01-01
|
|
||||||
//
|
|
||||||
const describeLogStream = async (logStreamName) => {
|
|
||||||
let logStream = await findLogStream(logStreamName);
|
|
||||||
|
|
||||||
if (!logStream) {
|
|
||||||
await cloudwatchlogs.createLogStream({ logGroupName, logStreamName }).promise();
|
|
||||||
logStream = await findLogStream(logStreamName);
|
|
||||||
}
|
|
||||||
|
|
||||||
return logStream;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Extend the original record with some additional fields
|
|
||||||
// and encapsule records into CloudWatch Logs event.
|
|
||||||
//
|
|
||||||
const buildlogEvents = (records) => (
|
|
||||||
records.map((record) => {
|
|
||||||
const payload = record;
|
|
||||||
payload.name = 'logs:cloudfront';
|
|
||||||
|
|
||||||
return {
|
|
||||||
message: JSON.stringify(payload),
|
|
||||||
timestamp: new Date(`${payload.date} ${payload.time} UTC`).getTime(),
|
|
||||||
};
|
|
||||||
}).sort((a, b) => a.timestamp - b.timestamp) // Events in a request must be chronological ordered
|
|
||||||
);
|
|
||||||
|
|
||||||
// Send the given documents to CloudWatch Logs group.
|
|
||||||
//
|
|
||||||
exports.putLogEvents = async (records) => {
|
|
||||||
const groupedRecords = groupBy(records, 'date');
|
|
||||||
|
|
||||||
const putLogEventsCalls = Object.keys(groupedRecords).map(async (key) => {
|
|
||||||
const logStream = await describeLogStream(key);
|
|
||||||
|
|
||||||
const params = {
|
|
||||||
logEvents: buildlogEvents(groupedRecords[key]),
|
|
||||||
logGroupName,
|
|
||||||
logStreamName: logStream.logStreamName,
|
|
||||||
sequenceToken: logStream.uploadSequenceToken,
|
|
||||||
};
|
|
||||||
|
|
||||||
return cloudwatchlogs.putLogEvents(params).promise();
|
|
||||||
});
|
|
||||||
|
|
||||||
return Promise.all(putLogEventsCalls);
|
|
||||||
};
|
|
@ -1,18 +0,0 @@
|
|||||||
const { parseLogFile } = require('./cloudfront');
|
|
||||||
const { putLogEvents } = require('./cloudwatch-logs');
|
|
||||||
|
|
||||||
// Lambda handler.
|
|
||||||
//
|
|
||||||
exports.handler = async (event) => {
|
|
||||||
if (event.Records.length !== 1) {
|
|
||||||
throw new Error(`Wrong length of events.Records, expected: '1', got: '${event.Records.length}'`);
|
|
||||||
} else {
|
|
||||||
const params = {
|
|
||||||
bucket: event.Records[0].s3.bucket.name,
|
|
||||||
key: decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' ')),
|
|
||||||
region: event.Records[0].awsRegion,
|
|
||||||
};
|
|
||||||
|
|
||||||
return putLogEvents(await parseLogFile(params));
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,66 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------
|
|
||||||
# Grant the log parsing lambda access to the logs bucket
|
|
||||||
|
|
||||||
resource "aws_lambda_permission" "s3_bucket_invoke_function" {
|
|
||||||
statement_id = "AllowExecutionFromS3Bucket"
|
|
||||||
action = "lambda:InvokeFunction"
|
|
||||||
function_name = aws_lambda_function.ipixel_parser.arn
|
|
||||||
principal = "s3.amazonaws.com"
|
|
||||||
source_arn = aws_s3_bucket.ipixel_logs.arn
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------
|
|
||||||
# Log Parsing Lambda
|
|
||||||
|
|
||||||
|
|
||||||
resource "aws_s3_bucket_notification" "ipixel_logs" {
|
|
||||||
bucket = aws_s3_bucket.ipixel_logs.bucket
|
|
||||||
|
|
||||||
lambda_function {
|
|
||||||
lambda_function_arn = aws_lambda_function.ipixel_parser.arn
|
|
||||||
events = ["s3:ObjectCreated:*"]
|
|
||||||
filter_prefix = "RAW/"
|
|
||||||
filter_suffix = ".gz"
|
|
||||||
}
|
|
||||||
|
|
||||||
depends_on = [aws_lambda_permission.s3_bucket_invoke_function]
|
|
||||||
}
|
|
||||||
|
|
||||||
data "archive_file" "ipixel_parser" {
|
|
||||||
type = "zip"
|
|
||||||
source_dir = "${path.module}/lambda"
|
|
||||||
output_path = ".terraform/tmp/lambda/ipixel_parser.zip"
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "aws_lambda_function" "ipixel_parser" {
|
|
||||||
function_name = "ipixel-parser-${var.site}"
|
|
||||||
|
|
||||||
runtime = "nodejs12.x"
|
|
||||||
handler = "index.handler"
|
|
||||||
timeout = 5
|
|
||||||
reserved_concurrent_executions = 3
|
|
||||||
|
|
||||||
environment {
|
|
||||||
variables = {
|
|
||||||
CLOUDWATCH_LOGS_GROUP_ARN = aws_cloudwatch_log_group.ipixel_results.arn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
role = aws_iam_role.ipixel_parser.arn
|
|
||||||
|
|
||||||
filename = data.archive_file.ipixel_parser.output_path
|
|
||||||
source_code_hash = data.archive_file.ipixel_parser.output_base64sha256
|
|
||||||
|
|
||||||
tags = {
|
|
||||||
Site = var.site,
|
|
||||||
Role = "ipixel"
|
|
||||||
}
|
|
||||||
|
|
||||||
depends_on = [
|
|
||||||
aws_cloudwatch_log_group.ipixel_parser_logs,
|
|
||||||
aws_cloudwatch_log_group.ipixel_results,
|
|
||||||
]
|
|
||||||
}
|
|
@ -60,12 +60,6 @@ resource "aws_iam_access_key" "s3" {
|
|||||||
|
|
||||||
resource "aws_s3_bucket" "src" {
|
resource "aws_s3_bucket" "src" {
|
||||||
bucket = var.domain
|
bucket = var.domain
|
||||||
acl = "public-read"
|
|
||||||
|
|
||||||
website {
|
|
||||||
index_document = "index.html"
|
|
||||||
error_document = "404.html"
|
|
||||||
}
|
|
||||||
|
|
||||||
tags = {
|
tags = {
|
||||||
Name = "Site Source"
|
Name = "Site Source"
|
||||||
@ -73,19 +67,23 @@ resource "aws_s3_bucket" "src" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_s3_bucket" "uat" {
|
|
||||||
bucket = "uat.${var.domain}"
|
resource "aws_s3_bucket_acl" "src" {
|
||||||
|
bucket = aws_s3_bucket.src.id
|
||||||
acl = "public-read"
|
acl = "public-read"
|
||||||
|
}
|
||||||
|
|
||||||
website {
|
resource "aws_s3_bucket_website_configuration" "src" {
|
||||||
index_document = "index.html"
|
bucket = aws_s3_bucket.src.bucket
|
||||||
error_document = "404.html"
|
|
||||||
|
index_document {
|
||||||
|
suffix = "index.html"
|
||||||
}
|
}
|
||||||
|
|
||||||
tags = {
|
error_document {
|
||||||
Name = "Site Source UAT"
|
key = "404.html"
|
||||||
Site = var.site
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -131,58 +129,3 @@ resource "aws_s3_bucket_policy" "src" {
|
|||||||
POLICY
|
POLICY
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
resource "aws_s3_bucket_policy" "uat" {
|
|
||||||
bucket = aws_s3_bucket.uat.bucket
|
|
||||||
policy = <<POLICY
|
|
||||||
{
|
|
||||||
"Version": "2012-10-17",
|
|
||||||
"Statement": [
|
|
||||||
{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Principal": {
|
|
||||||
"AWS": "${aws_iam_user.s3.arn}"
|
|
||||||
},
|
|
||||||
"Action": "s3:ListBucket",
|
|
||||||
"Resource": "${aws_s3_bucket.uat.arn}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Principal": {
|
|
||||||
"AWS": "${aws_iam_user.s3.arn}"
|
|
||||||
},
|
|
||||||
"Action": [
|
|
||||||
"s3:PutObject",
|
|
||||||
"s3:PutObjectAcl",
|
|
||||||
"s3:GetObject",
|
|
||||||
"s3:GetObjectAcl",
|
|
||||||
"s3:DeleteObject",
|
|
||||||
"s3:ListMultipartUploadParts",
|
|
||||||
"s3:AbortMultipartUpload"
|
|
||||||
],
|
|
||||||
"Resource": "${aws_s3_bucket.uat.arn}/*"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Sid": "PublicReadGetObject",
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Principal": "*",
|
|
||||||
"Action": "s3:GetObject",
|
|
||||||
"Resource": "${aws_s3_bucket.uat.arn}/*"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
POLICY
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "aws_route53_record" "uat" {
|
|
||||||
name = "uat.${var.domain}"
|
|
||||||
zone_id = aws_route53_zone.zone.zone_id
|
|
||||||
type = "A"
|
|
||||||
|
|
||||||
alias {
|
|
||||||
name = aws_s3_bucket.uat.website_domain
|
|
||||||
zone_id = aws_s3_bucket.uat.hosted_zone_id
|
|
||||||
evaluate_target_health = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@ -5,7 +5,20 @@
|
|||||||
|
|
||||||
resource "aws_s3_bucket" "pixel" {
|
resource "aws_s3_bucket" "pixel" {
|
||||||
bucket = "t.${var.domain}"
|
bucket = "t.${var.domain}"
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "Tracking Pixel"
|
||||||
|
Site = var.site
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_s3_bucket_acl" "pixel" {
|
||||||
|
bucket = aws_s3_bucket.pixel.id
|
||||||
acl = "public-read"
|
acl = "public-read"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_s3_bucket_cors_configuration" "pixel" {
|
||||||
|
bucket = aws_s3_bucket.pixel.id
|
||||||
|
|
||||||
cors_rule {
|
cors_rule {
|
||||||
allowed_headers = ["*"]
|
allowed_headers = ["*"]
|
||||||
@ -15,13 +28,9 @@ resource "aws_s3_bucket" "pixel" {
|
|||||||
max_age_seconds = 3000
|
max_age_seconds = 3000
|
||||||
}
|
}
|
||||||
|
|
||||||
tags = {
|
|
||||||
Name = "Tracking Pixel"
|
|
||||||
Site = var.site
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_s3_bucket_object" "ipixel" {
|
resource "aws_s3_object" "ipixel" {
|
||||||
bucket = aws_s3_bucket.pixel.bucket
|
bucket = aws_s3_bucket.pixel.bucket
|
||||||
key = "i"
|
key = "i"
|
||||||
source = "${path.module}/files/i.gif"
|
source = "${path.module}/files/i.gif"
|
||||||
@ -30,49 +39,67 @@ resource "aws_s3_bucket_object" "ipixel" {
|
|||||||
content_type = "image/gif"
|
content_type = "image/gif"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
data "aws_canonical_user_id" "current" {}
|
data "aws_canonical_user_id" "current" {}
|
||||||
|
|
||||||
resource "aws_s3_bucket" "ipixel_logs" {
|
resource "aws_s3_bucket" "ipixel_logs" {
|
||||||
bucket = "${var.site}-analytics"
|
bucket = "${var.site}-analytics"
|
||||||
|
|
||||||
grant {
|
tags = {
|
||||||
id = data.aws_canonical_user_id.current.id
|
Name = "iPixel Logs Storage"
|
||||||
permissions = ["FULL_CONTROL"]
|
Site = var.site
|
||||||
type = "CanonicalUser"
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
grant {
|
resource "aws_s3_bucket_acl" "ipixel_logs" {
|
||||||
# Grant CloudFront awslogsdelivery logs access to your Amazon S3 Bucket
|
bucket = aws_s3_bucket.ipixel_logs.id
|
||||||
# https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html#AccessLogsBucketAndFileOwnership
|
|
||||||
id = "c4c1ede66af53448b93c283ce9448c4ba468c9432aa01d700d3878632f77d2d0"
|
access_control_policy {
|
||||||
permissions = ["FULL_CONTROL"]
|
|
||||||
type = "CanonicalUser"
|
owner {
|
||||||
|
id = data.aws_canonical_user_id.current.id
|
||||||
|
}
|
||||||
|
|
||||||
|
grant {
|
||||||
|
grantee {
|
||||||
|
id = data.aws_canonical_user_id.current.id
|
||||||
|
type = "CanonicalUser"
|
||||||
|
}
|
||||||
|
|
||||||
|
permission = "FULL_CONTROL"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
grant {
|
||||||
|
grantee {
|
||||||
|
uri = "http://acs.amazonaws.com/groups/s3/LogDelivery"
|
||||||
|
type = "Group"
|
||||||
|
}
|
||||||
|
|
||||||
|
permission = "FULL_CONTROL"
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lifecycle_rule {
|
resource "aws_s3_bucket_lifecycle_configuration" "example" {
|
||||||
id = "logfiles"
|
bucket = aws_s3_bucket.ipixel_logs.id
|
||||||
enabled = true
|
|
||||||
|
|
||||||
prefix = "RAW/"
|
rule {
|
||||||
|
id = "logfiles"
|
||||||
|
|
||||||
|
filter {
|
||||||
|
prefix = "RAW/"
|
||||||
|
}
|
||||||
|
|
||||||
transition {
|
transition {
|
||||||
days = 30
|
days = 30
|
||||||
storage_class = "STANDARD_IA" # or "ONEZONE_IA"
|
storage_class = "STANDARD_IA" # or "ONEZONE_IA"
|
||||||
}
|
}
|
||||||
|
|
||||||
# transition {
|
# ... other transition/expiration actions ...
|
||||||
# days = 30
|
|
||||||
# storage_class = "GLACIER"
|
|
||||||
# }
|
|
||||||
|
|
||||||
# expiration {
|
status = "Enabled"
|
||||||
# days = 90
|
|
||||||
# }
|
|
||||||
}
|
|
||||||
|
|
||||||
tags = {
|
|
||||||
Name = "iPixel Logs Storage"
|
|
||||||
Site = var.site
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,10 @@
|
|||||||
|
|
||||||
|
variable "region" {
|
||||||
|
type = string
|
||||||
|
description = "AWS Hosting Region"
|
||||||
|
default = "us-east-1"
|
||||||
|
}
|
||||||
|
|
||||||
variable "site" {
|
variable "site" {
|
||||||
type = string
|
type = string
|
||||||
description = "The name of the site"
|
description = "The name of the site"
|
||||||
@ -19,7 +25,16 @@ variable "subdomains" {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
provider "aws" {
|
variable "namecheap" {
|
||||||
profile = "default"
|
type = map
|
||||||
region = "us-east-1"
|
description = "Namecheap Credentials"
|
||||||
}
|
default = {
|
||||||
|
username = ""
|
||||||
|
apikey = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
validation {
|
||||||
|
condition = length(var.namecheap.username) > 0
|
||||||
|
error_message = "Must provide a namecheap configuration."
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,26 @@ terraform {
|
|||||||
aws = {
|
aws = {
|
||||||
source = "hashicorp/aws"
|
source = "hashicorp/aws"
|
||||||
}
|
}
|
||||||
|
namecheap = {
|
||||||
|
source = "namecheap/namecheap"
|
||||||
|
version = ">= 2.0.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
required_version = ">= 0.13"
|
required_version = ">= 0.13"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
provider "aws" {
|
||||||
|
region = var.region
|
||||||
|
}
|
||||||
|
|
||||||
|
data "http" "externalip" {
|
||||||
|
url = "http://ipv4.icanhazip.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "namecheap" {
|
||||||
|
user_name = var.namecheap.username
|
||||||
|
api_user = var.namecheap.username
|
||||||
|
api_key = var.namecheap.apikey
|
||||||
|
client_ip = chomp(data.http.externalip.body)
|
||||||
|
use_sandbox = false
|
||||||
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
|
|
||||||
|
resource "aws_cloudfront_origin_access_identity" "origin_access_identity" {
|
||||||
|
}
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------
|
||||||
# Cloudfront Configuration
|
# Cloudfront Configuration
|
||||||
|
|
||||||
@ -112,10 +115,10 @@ resource "aws_lambda_function" "index_redirect" {
|
|||||||
filename = "${path.module}/files/index_redirect.js.zip"
|
filename = "${path.module}/files/index_redirect.js.zip"
|
||||||
function_name = "${var.site}-index-redirect"
|
function_name = "${var.site}-index-redirect"
|
||||||
handler = "index_redirect.handler"
|
handler = "index_redirect.handler"
|
||||||
source_code_hash = data.archive_file.index_redirect.output_base64sha256
|
# source_code_hash = data.archive_file.index_redirect.output_base64sha256
|
||||||
publish = true
|
publish = true
|
||||||
role = aws_iam_role.lambda_redirect.arn
|
role = aws_iam_role.lambda_redirect.arn
|
||||||
runtime = "nodejs10.x"
|
runtime = "nodejs16.x"
|
||||||
|
|
||||||
tags = {
|
tags = {
|
||||||
Name = "${var.site}-index-redirect"
|
Name = "${var.site}-index-redirect"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user