Skip to content

Commit

Permalink
Merge pull request #2 from karldreher/develop
Browse files Browse the repository at this point in the history
1.0
  • Loading branch information
karldreher authored Sep 4, 2021
2 parents 18aa831 + 093ea11 commit 7fd610f
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 58 deletions.
7 changes: 0 additions & 7 deletions .prettierrc

This file was deleted.

53 changes: 29 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,50 +16,55 @@ wrangler dev

The application expects to interact with a Cloudflare KV store with several values defined:

| Name | Description | Example Value |
| ------------- | --------------------------------------------------------------------------------------------------- | ----------------------------------------- |
| `AUTH_HEADER` | This is a user:password pair, base64 encoded. The word "Basic" and a space must prepend the entry. | `Basic dXNlcm5hbWU6cGFzc3dvcmQK` |
| `ENDPOINT` | The URL for files in the bucket, either the "S3" or "Freindly" URL. The value should **omit** the slash. | `https://s3.us-west-002.backblazeb2.com` |

| Name | Description | Example Value |
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- |
| `AUTH_HEADER` | This is a user:password pair, base64 encoded. The word "Basic" and a space must prepend the entry. See below for instructions on getting this value. | `Basic dXNlcm5hbWU6cGFzc3dvcmQK` |
| `BUCKET_NAME` | The name of the bucket in Backblaze B2. | `my-cool-bucket` |

The namespace can be named as desired, but wrangler.toml must have `binding = "kv_namespace"` as demonstrated in the example; `kv_namespace` is how the KV is accessed by the script. (according to the values of `id` and `preview_id`)

## Generating the authorization value
## Deployment

### Bootstrapping the KV

You can use Wrangler and the supplied `./tools/init-kv.json` to initiate the KV with "placeholder" values. It is still necessary to create the KV store (store**s** if you utilize a preview environment), then specify them by namespace with `wrangler` to instantiate them:

```bash
wrangler kv:bulk put --namespace 12345678910 ./tools/init-kv.json
```

### Generating the authorization value

1. Within Backblaze, add an application key for your bucket.
2. Select the appropriate bucket (not "All" unless you know what you're doing)
3. Select "Read Only"
4. Provide a duration less than 1000 days in the future, specified in seconds. Since this will need to rotate, it is reccomended (for this application's purpose) a maximum value of `86399999`.

Upon creating the app key, use the **values** for `keyID` and `applicationKey` by Backblaze to generate the base-64 encoded Basic authorization header:
Upon creating the app key, use the **values** for `keyID` and `applicationKey` by Backblaze to generate the base-64 encoded Basic authorization header:

```bash
$ AUTH=$(echo keyID:applicationKey | base64)
$ AUTH=$(echo keyID:applicationKey | base64)
$ VALUE="Basic $AUTH"
$ echo $VALUE
Basic dXNlcm5hbWU6cGFzc3dvcmQK
```
Use the contents of `$VALUE` (including "Basic ") as generated following the steps above, within the KV store value `AUTH_HEADER`

Both the username:password pair and authorization value should be treated as a **secret**. Within Backblaze it is not possible to change or view the keys after creation, they must be regenerated as new keys!

## Deployment

### Bucket Setup

The B2 bucket settings **must** have `{"cache-control":"max-age=86400"}` set on Bucket Info(`86400` is 1 day, this can be set to the desired value) .

Default bucket settings will invalidate Cloudflare's ability to cache. Setting this option will allow the Worker to utilize the Cloudflare CDN.
Use the contents of `$VALUE` (including "Basic ") as generated following the steps above, within the KV store value `AUTH_HEADER`

### Bootstrapping the KV
Both the username:password pair and authorization value should be treated as a **secret**. Within Backblaze it is not possible to change or view the keys after creation, they must be regenerated as new keys!

You can use Wrangler and the supplied `./tools/init-kv.json` to initiate the KV with "placeholder" values. It is still necessary to create the KV store (store**s** if you utilize a preview environment), then specify them by namespace with `wrangler` to instantiate them:
### Deploying the worker

```bash
wrangler kv:bulk put --namespace 12345678910 ./tools/init-kv.json
wrangler publish
```

### Deploying the worker
### Usage

Utilize the worker's endpoint to access your files in your private bucket!
This can be done in the browser, through a shell program, or anything that uses HTTPS.
The worker handles the middleware between your browser, the Cloudflare cache, and the B2 bucket.

```bash
wrangler publish
curl my-secret-bucket.example.workers.dev/pictures-of-cats/fatkitty.jpg
```
More TBD..
68 changes: 49 additions & 19 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,68 @@
addEventListener('fetch', event => {
event.respondWith(handleRequest(event))
})
addEventListener("fetch", (event) => {
event.respondWith(handleRequest(event));
});

async function getValueFromKV(key) {
let value = await kv_namespace.get(key);
return value;
}

async function authorizeAccount() {
authValue = await getValueFromKV("AUTH_HEADER");
authHeader = {
Authorization: authValue,
};
const account = await fetch(
"https://api.backblazeb2.com/b2api/v2/b2_authorize_account",
{
headers: authHeader,
}
).then((res) => res.json());
//https://www.backblaze.com/b2/docs/b2_authorize_account.html
//Note, the auth will be valid for 24 hours. right now this is called every time, on next update should be stored in KV and ideally cron'd
return account;
}

async function serveAsset(event) {
const url = new URL(event.request.url)
const cache = caches.default
let response = await cache.match(event.request)
const url = new URL(event.request.url);

//First determine if the cache has the object already
const cache = caches.default;
let response = await cache.match(event.request);

if (!response) {
//get api url and auth from b2
auth = await authorizeAccount();

//Set target URL
baseURL = await getValueFromKV('ENDPOINT')
const url = new URL(event.request.url);
requestURL = baseURL + url.pathname

response = await fetch(requestURL)
console.log(new Map(response.headers))
//Bucket name comes from KV
bucketName = await getValueFromKV("BUCKET_NAME");
//Request URL to B2 must contain the download URL returned from authorize account, then the path /file/{bucketname}/{pathname}
requestURL = new URL(
auth.downloadUrl + "/file/" + bucketName + url.pathname
);
params = { b2CacheControl: "public,max-age=86400" };
requestURL.search = new URLSearchParams(params).toString();
requestHeaders = {
Authorization: auth.authorizationToken,
};
response = await fetch(requestURL, {
headers: requestHeaders,
});

response = new Response(response.body, response)
response.headers.set("Cache-Control", "public,max-age=86400")
response = new Response(response.body, response);
response.headers.set("Cache-Control", "public,max-age=86400");

event.waitUntil(cache.put(event.request, response.clone()))
event.waitUntil(cache.put(event.request, response.clone()));
}
return response
return response;
}


async function handleRequest(event) {
let response = await serveAsset(event)
let response = await serveAsset(event);
if (response.status > 399) {
response = new Response(response.statusText, { status: response.status })
response = new Response(response.statusText, { status: response.status });
}
return response
return response;
}
12 changes: 12 additions & 0 deletions package-lock.json

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

8 changes: 2 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,8 @@
"description": "Cloudflare worker for caching a private Backblaze B2 bucket.",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"format": "prettier --write '**/*.{js,css,json,md}'"
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Karl Dreher <[email protected]>",
"license": "MIT",
"devDependencies": {
"prettier": "^1.18.2"
}
"license": "MIT"
}
4 changes: 2 additions & 2 deletions tools/init-kv.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"value": "placeholder"
},
{
"key": "ENDPOINT",
"key": "BUCKET_NAME",
"value": "placeholder"
}
]
]

0 comments on commit 7fd610f

Please sign in to comment.