tmp.spacet.me devlog part 3
Tags: dohackathon
Published: 2021-01-05 20:55:46

---
title: tmp.spacet.me devlog part 3
tags: dohackathon
series: tmp.spacet.me devlog
published: true
canonical_url: https://notes.dt.in.th/20210105T201754Z9386
---

I added support for external extensions. The settings panel now has an area where extension URLs can be added.

![](https://static.dt.in.th/uploads/2021/01/05/tmp-extension-settings.png)

The first external extension I will write will be for uploading files to a custom endpoint. This helps make it easier for me to upload files to my static files hosting.

I put in the extension URL: `https://raw.githubusercontent.com/dtinth/tmp-uploader/master/`. This effectively installs the extension. The "Uploader" menu now shows up in the files list.

![](https://static.dt.in.th/uploads/2021/01/05/tmp-uploader-integration.png)

When I click on it, it opens a new browser window and uploads the file to my static files hosting. Then it returns the URL to the image, ready to be copied.

![](https://static.dt.in.th/uploads/2021/01/05/tmp-uploader-result.png)

## Under the hood

### Extension installation

When I put in the extension's URL, `tmp.spacet.me` goes ahead and fetches manifest file by appending `/tmp-manifest.json` to the URL. The JSON looks like this:

```json
{
  "name": "dtinth's uploader",
  "description": "Upload files to cloud storage",
  "contributes": {
    "integrations": {
      "uploader": {
        "title": "Uploader",
        "accept": ["*"],
        "url": "https://tmp-uploader.glitch.me/"
      }
    }
  }
}
```

This caused the "Uploader" to appear in the file's menu. And when clicked, `tmp` will launch the URL specified.

### Uploading process

The first time I go to <https://tmp-uploader.glitch.me/>, I am shown the **Configuration** screen, and asked to enter an **Upload URL request endpoint**. This makes the tool generic and can theoretically but with any host that implements the specified interface.

![](https://static.dt.in.th/uploads/2021/01/05/tmp-uploader-config.png)

Now having to configure the URL on every computer may feel cumbersome, but I normally store credentialed URLs like these in my password manager, I know exactly where to recall them should I ever need to.

Now, when a file will be uploaded, the configured endpoint will be called with the filename and MIME type. It returns a JSON that looks like this:

```json
{
  "uploadUrl": "https://s3.ap-southeast-1.amazonaws.com/static.dt.in.th/uploads/2021/01/05/tmp-uploader-result.png?AWSAccessKeyId=...",
  "downloadUrl": "https://static.dt.in.th/uploads/2021/01/05/tmp-uploader-result.png"
}
```

Then the `tmp-uploader` will make a PUT request to `uploadUrl`. Once done the `downloadUrl` is displayed.

### Endpoint implementation

My uploaded files are hosted on Amazon S3. So I created a Lambda function according to the above specification. For `uploadUrl`, it returns a signed URL [that can be used to upload a file to S3 directly from the web browser](https://aws.amazon.com/blogs/compute/uploading-to-amazon-s3-directly-from-a-web-or-mobile-application/).

```js
const AWS = require('aws-sdk')
AWS.config.update({ region: process.env.AWS_REGION })
const s3 = new AWS.S3()

exports.handler = async (event) => {
  if (event.queryStringParameters.code !== process.env.API_KEY) {
    return { statusCode: 401, body: 'Unauthorized' }
  }
  const date = new Date().toJSON().slice(0, 10).replace(/\W+/g, '/')
  const key = `uploads/${date}/${event.queryStringParameters.filename}`
  const s3Params = {
    Bucket: 'static.dt.in.th',
    Key: key,
    Expires: 3600,
    ContentType: event.queryStringParameters.type,
  }
  const uploadUrl = await s3.getSignedUrlPromise('putObject', s3Params)
  const downloadUrl = `https://static.dt.in.th/${key}`
  return JSON.stringify({ uploadUrl, downloadUrl })
}
```

I need to grant the Lambda function essays to the S3 bucket by attaching this policy:

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "VisualEditor0",
      "Effect": "Allow",
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::static.dt.in.th/*"
    }
  ]
}
```

and finally I need to allow file uploads from the browser:

```json
[
  {
    "AllowedHeaders": ["*"],
    "AllowedMethods": ["GET", "PUT"],
    "AllowedOrigins": ["https://tmp-uploader.glitch.me"],
    "ExposeHeaders": []
  }
]
```

## Other things done

Also implemented rudimentary Rename functionality.