Skip to content

Commit

Permalink
Add bootstrap script and documentation.
Browse files Browse the repository at this point in the history
  • Loading branch information
elisiariocouto committed Dec 27, 2022
1 parent 72af26a commit 36d2278
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 81 deletions.
71 changes: 12 additions & 59 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,66 +1,19 @@
# Getting started

1. Create a `.env` file in the root of the repository with the Cloudflare API Token and Account ID:
```
CLOUDFLARE_API_TOKEN=<token>
CLOUDFLARE_ACCOUNT_ID=<account_id>
```
2. Create a `.dev.vars` file in the root of the repository with the following content:
```
NORDIGEN_SECRET_ID=<secret>
NORDIGEN_SECRET_KEY=<secret>
NORDIGEN_ACCOUNT_ID=<account_id>
NOTIFY_PATTERN=<pattern>
DISCORD_URL=<discord-webhook-url>
```
3. Install the dependencies:
To run the project locally:
```shell
npm i
```
4. Create a `wrangler.toml` from the template:
```shell
cp wrangler.toml.template wrangler.toml
```
5. Create a KV for bankman:
```shell
wrangler kv:namespace create "BANKMAN_KV"
```
6. Copy the KV ID from previous command to your wrangler.toml:
```shell
sed -i wrangler.toml "s/KV_ID/$(wrangler kv:namespace list | grep BANKMAN_KV -B1 | head -n1 | cut -d'"' -f4)/"
```
7. Create a D1 for bankman:
```shell
wrangler d1 create bankmandb
```
8. Copy the database ID from previous command to your wrangler.toml:
```shell
sed -i wrangler.toml "s/DATABASE_ID/$(wrangler d1 list | grep bankman | awk '{print $2}')/"
```
9. Run database migrations:
```shell
wrangler d1 migrations apply bankman
```
10. Push secrets for this worker:
```shell
wrangler secret:bulk <(cat .dev.vars | sed 's/"//g' | sed "s/^/\"/g" | sed "s/=/\":\"/g" | sed "s/$/\",/g" | tr -d '\n' | sed "s/^/{/" | tr -s ',' | sed "s/,$/}/")
```
11. Run the project locally:
```shell
npm start
```

12. The project runs every minute, but if you want to manually trigger an event, run the following:
```shell
curl "http://localhost:8787/cdn-cgi/mf/scheduled"
```
You also need to run the database migrations:
```shell
for file in ./migrations/*.sql; do
npx wrangler d1 execute bankmandb --local --file="$file"
done
```

The project runs every hour by default, but if you want to manually trigger an event, run the following:
```shell
curl "http://localhost:8787/cdn-cgi/mf/scheduled"
```
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2022 Meltway Labs

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
48 changes: 47 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,51 @@
# Bankman
# $ bankman

A cron job running serverless on Cloudflare Workers.

This project uses Nordigen to connect to a bank account and searches for a specific transaction regex, alerting on Discord when this happens.

## Pre-Requisites
#### 1. Cloudflare Account
This project runs on Cloudflare Workers. You need a Cloudflare API token.

Use the `Edit Cloudflare Workers` template and add a new rule:
```
Account | D1 | Edit
```

#### 2. Nordigen Account
Nordigen connects to your Open Banking-compatible bank account.

Sign up and follow the quickstart guide: https://nordigen.com/en/account_information_documenation/integration/quickstart_guide/

Save the account ID from step 5, you will need it later.

#### 3. Discord Webhook
In order to send notifications, we currently only support Discord Webhooks.

## Getting started

1. Create a `.env` file in the root of the repository with the Cloudflare API Token and Account ID:
```
CLOUDFLARE_API_TOKEN=<token>
CLOUDFLARE_ACCOUNT_ID=<account_id>
```
2. Create a `.dev.vars` file in the root of the repository with the following content:
```
NORDIGEN_SECRET_ID=<secret>
NORDIGEN_SECRET_KEY=<secret>
NORDIGEN_ACCOUNT_ID=<account_id>
NOTIFY_PATTERN=<pattern>
DISCORD_URL=<discord-webhook-url>
```
3. Install the dependencies:
```shell
npm i
```
4. Run the bootstrap script to create all the resources and deploy:
```shell
npm run bootstrap
```
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "bankman",
"version": "0.0.0",
"version": "0.1.0",
"devDependencies": {
"@cloudflare/workers-types": "^4.20221111.1",
"better-sqlite3": "^7.6.2",
Expand All @@ -9,7 +9,8 @@
},
"private": true,
"scripts": {
"start": "wrangler dev --local",
"deploy": "wrangler publish"
"bootstrap": "./scripts/bootstrap.sh",
"deploy": "wrangler publish --name bankman",
"start": "wrangler dev --local"
}
}
84 changes: 84 additions & 0 deletions scripts/bootstrap.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/bin/bash

set -euf -o pipefail

NO_D1_WARNING=true
export NO_D1_WARNING

export RED="\033[1;31m"
export GREEN="\033[1;32m"
export YELLOW="\033[1;33m"
export BLUE="\033[1;34m"
export PURPLE="\033[1;35m"
export CYAN="\033[1;36m"
export GREY="\033[0;37m"
export RESET="\033[m"

check_command() {
if ! command -v "$1" &> /dev/null
then
echo "$1 could not be found."
echo "Exiting."
exit 1
fi
}

print_error() {
echo -e "${RED}$1${RESET}"
}

print_info() {
echo -e "${CYAN}$1${RESET}"
}
print_success() {
echo -e "${GREEN}$1${RESET}"
}

check_command jq
check_command npx
check_command envsubst
check_command sed
check_command tr
check_command grep
check_command awk

if ! npm exec --no -- wrangler --version &> /dev/null
then
# shellcheck disable=SC2016
print_error "wrangler could not be found. Did you run \`npm install\` ?"
print_error "Exiting."
exit 1
fi

cp wrangler.toml.template wrangler.toml
print_info "> Creating KV namespace"
npx wrangler kv:namespace create "KV"
# shellcheck disable=SC2034
KV_ID=$(npx wrangler kv:namespace list | jq -r '.[] | select(.title == "bankman-KV") | .id')
export KV_ID

print_info "> Creating D1 database"
npx wrangler d1 create bankmandb
# shellcheck disable=SC2034
DATABASE_ID=$(npx wrangler d1 list | grep bankman | awk '{print $2}')
export DATABASE_ID

print_info "> Creating wrangler.toml"
envsubst < wrangler.toml.template > wrangler.toml

print_info "> Waiting for DB to be ready"
sleep 10

print_info "> Applying DB migrations"
# Remove hacky way of disabling prompt after wrangler has a -y, --skip-confirmation flag
CF_PAGES=1 npx wrangler d1 migrations apply bankmandb 2> /dev/null

print_info "> Publishing Cloudflare Worker"
wrangler publish --name bankman

print_info "> Updating secrets"
sed 's/"//g' .dev.vars | sed "s/^/\"/g" | sed "s/=/\":\"/g" | sed "s/$/\",/g" | tr -d '\n' | sed "s/^/{/" | tr -s ',' | sed "s/,$/}/" > tmp
wrangler secret:bulk tmp
rm -f tmp

print_success "> Bankman deployed"
28 changes: 14 additions & 14 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ export interface Env {
NORDIGEN_AGREEMENT_ID: string;
NOTIFY_PATTERN: string;
DISCORD_URL: string;
BANKMAN_KV: KVNamespace;
KV: KVNamespace;
DB: D1Database;
}

// one day in milliseconds
const ONE_DAY_MS = 1000 * 60 * 60 * 24;
const ONE_DAY_MS = 1000 * 60 * 60 * 24;
// days before today to fetch transactions from
const DAYS_TO_FETCH = 2;
// notify expiration once a day under X days
Expand Down Expand Up @@ -115,7 +115,7 @@ async function fetchNordigenAgreementExpiration(
const byId = id && id !== "";

const url = byId ?
NORDIGEN_HOST + `/api/v2/agreements/enduser/${id}/`:
NORDIGEN_HOST + `/api/v2/agreements/enduser/${id}/` :
NORDIGEN_HOST + `/api/v2/agreements/enduser/?limit=1`;

const resp = await fetch(url, {
Expand Down Expand Up @@ -143,7 +143,7 @@ async function fetchNordigenAgreementExpiration(

const expiryDate = (new Date(agreement.accepted)).getTime() + ONE_DAY_MS * agreement.access_valid_for_days;

return (expiryDate - Date.now())/ONE_DAY_MS
return (expiryDate - Date.now()) / ONE_DAY_MS
}

async function storeBankTransactions(
Expand Down Expand Up @@ -237,11 +237,11 @@ async function doExecute(env: Env) {
// check agreement expiration and notify if needed
const expirationDays = await fetchNordigenAgreementExpiration(token, env.NORDIGEN_AGREEMENT_ID);
if (expirationDays <= NOTIFY_EXPIRATION_DAYS) {
const hasNotifiedToday = await checkNotifiedToday(env.BANKMAN_KV);
const hasNotifiedToday = await checkNotifiedToday(env.KV);
if (!hasNotifiedToday) {
const message = `End user agreement expires in ${expirationDays} days.`;
await notifyDiscord(env.DISCORD_URL, message);
await markNotifiedToday(env.BANKMAN_KV);
await markNotifiedToday(env.KV);
}
}

Expand Down Expand Up @@ -305,12 +305,12 @@ export default {
},

// Uncomment lines below to allow invoking with fetch.
// async fetch(
// request: RequestInit,
// env: Env,
// context: ExecutionContext,
// ): Promise<Response> {
// await execute(env);
// return Response.json({status: "ok"})
// }
async fetch(
request: RequestInit,
env: Env,
context: ExecutionContext,
): Promise<Response> {
await execute(env);
return Response.json({ status: "ok" })
}
};
4 changes: 2 additions & 2 deletions wrangler.toml.template
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ crons = [ "0 * * * *" ]
[[ d1_databases ]]
binding = "DB"
database_name = "bankmandb"
database_id = "DATABASE_ID"
database_id = "$DATABASE_ID"

kv_namespaces = [
{ binding = "BANKMAN_KV", id = "KV_ID" }
{ binding = "KV", id = "$KV_ID" }
]

0 comments on commit 36d2278

Please sign in to comment.