README.md 6.32 KB
Newer Older
James Calfee's avatar
James Calfee committed
1

Johan Nordberg's avatar
Johan Nordberg committed
2
3
imagehoster
===========
James Calfee's avatar
James Calfee committed
4

5
Hive-powered image hosting and proxying service.
James Calfee's avatar
James Calfee committed
6

7

Johan Nordberg's avatar
Johan Nordberg committed
8
9
Developing
----------
10

Johan Nordberg's avatar
Johan Nordberg committed
11
With node.js installed, run:
12

Johan Nordberg's avatar
Johan Nordberg committed
13
14
15
```
make devserver
```
James Calfee's avatar
James Calfee committed
16

Johan Nordberg's avatar
Johan Nordberg committed
17
18
19
20
21
22
23
24
This will pull in all dependencies and spin up a hot-reloading development server.

Run `make lint` to run the autolinter, `make test` to run the unit tests.


Configuration
-------------

Johan Nordberg's avatar
Johan Nordberg committed
25
Defaults are in <./config/default.toml> and can be overridden by env vars as defined in <./config/custom-environment-variables.toml>
Johan Nordberg's avatar
Johan Nordberg committed
26
27
28
29

Load order is: env vars > `config/$NODE_ENV.toml` > `config/default.toml`

See the `config` module docs for more details.
James Calfee's avatar
James Calfee committed
30
31


Johan Nordberg's avatar
Johan Nordberg committed
32
33
API
---
34

Johan Nordberg's avatar
Johan Nordberg committed
35
36
37
38
39
40
41
42
43
44
45
Responses should be determined by the Content-Type header, errors will have a status of `>=400` and a Content-Type of `application/json` with the body in the format:

```json
{
    "error": {
        "name": "error_name",
        "info": {"optional": "metadata"}
    }
}
```

Johan Nordberg's avatar
Johan Nordberg committed
46
#### `POST /<username>/<signature>` - upload an image.
Johan Nordberg's avatar
Johan Nordberg committed
47
48
49
50
51
52
53
54
55
56
57

Multipart image upload, will only consider first file if there are multiple.

Returns a JSON object containing the url to the uploaded image, example:

```json
{
    "url": "https://images.example.com/DQmZi174Xz96UrRVBMNRHb6A2FfU3z1HRPwPPQCgSMgdiUT/test.jpg"
}
```

58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
Requires a signature from a Hive account in good standing, see the "Signing uploads" section below for more information.

#### `POST /hs/<accesstoken>` - upload an image with Hivesigner accessToken.

Multipart image upload, will only consider first file if there are multiple.

Returns a JSON object containing the url to the uploaded image, example:

```json
{
    "url": "https://images.example.com/DQmZi174Xz96UrRVBMNRHb6A2FfU3z1HRPwPPQCgSMgdiUT/test.jpg"
}
```

Requires a access token from a Hivesigner authorized account, for more info: https://hivesigner.com.
Johan Nordberg's avatar
Johan Nordberg committed
73
74


Johan Nordberg's avatar
Johan Nordberg committed
75
#### `GET /<image_hash>/[<filename>]` - fetch an uploaded image.
Johan Nordberg's avatar
Johan Nordberg committed
76
77
78
79

Download a previously uploaded image.

`filename` is optional but can be provided to help users and applications understand the content type (Content-Type header will still always reflect actual image type).
80

81

Johan Nordberg's avatar
Johan Nordberg committed
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#### `GET /p/<b58_image_url>[?options]` - proxy and resize an image.

Downloads and serves the provided image, note that a copy will be taken of the image and that will be served on subsequent requests so even if the upstream is removed or changes you will still get the original from the proxy endpoint.

##### Params

  * `b58_image_url` - [Base58](https://en.wikipedia.org/wiki/Base58) encoded utf8 string containing the url to the image you wish to proxy.

##### Options

The options are set as query-strings and control how the image is transformed before being proxied.

  * `width` - Desired image width.
  * `height` - Desired image height.
  * `mode` - Resizing mode.
    * `cover` *default* - When set the image will be center cropped if the original aspect ratio does not match the aspect ratio of the upstream image.
    * `fit` - Does not crop the image, it will always keep the upstream aspect ratio and resized to fit within the width and height given.
  * `format` - Output image encoding.
    * `match` *default* - Matches the encoding of the upstream image.
    * `jpeg` - Use JPEG encoding.
    * `png` - Use PNG encoding.
    * `webp` - Use WebP encoding.

If only `width` or `height` are given their counterpart will be calculated based on the upstream image aspect ratio.

##### Examples

Upstream image: `https://ipfs.io/ipfs/QmXa4dAFEhGEuZaX7uUSEvBjbEY5mPxkaS2zHZSnHvocpn` (Base58 encoded `46aP2QbqUqBqwzwxM6L1P6uLNceBDDCM9ZJdv282fpHyc9Wgcz1FduB11aVXtczv9TiCSHF1eEmnRSSdQWQEXA5krJNq`)

Proxy the image as-is:
```
https://steemitimages.com/p/46aP2QbqUqBqwzwxM6L1P6uLNceBDDCM9ZJdv282fpHyc9Wgcz1FduB11aVXtczv9TiCSHF1eEmnRSSdQWQEXA5krJNq
```

Center cropped 512x512px avatar image in WebP format:
```
https://steemitimages.com/p/46aP2QbqUqBqwzwxM6L1P6uLNceBDDCM9ZJdv282fpHyc9Wgcz1FduB11aVXtczv9TiCSHF1eEmnRSSdQWQEXA5krJNq?width=512&height=512&format=webp
```

Aspect resized image fitting inside a 200x500px container:
```
https://steemitimages.com/p/46aP2QbqUqBqwzwxM6L1P6uLNceBDDCM9ZJdv282fpHyc9Wgcz1FduB11aVXtczv9TiCSHF1eEmnRSSdQWQEXA5krJNq?width=200&height=500&mode=fit
```

Aspect resized image with variable width and a height of max 100px:
```
https://steemitimages.com/p/46aP2QbqUqBqwzwxM6L1P6uLNceBDDCM9ZJdv282fpHyc9Wgcz1FduB11aVXtczv9TiCSHF1eEmnRSSdQWQEXA5krJNq?&height=100
```

Johan Nordberg's avatar
Johan Nordberg committed
131
#### `GET /<width>x<height>/<image_url>` - proxy and resize an image.
132

Johan Nordberg's avatar
Johan Nordberg committed
133
134
## DEPRECATED

Johan Nordberg's avatar
Johan Nordberg committed
135
136
137
138
Downloads and serves the provided `image_url`, note that a copy will be taken of the image and that will be served on subsequent requests so even if the upstream is removed or changes you will still get the original from the proxy endpoint.

`width` and `height` can be set to `0` to preserve the image dimensions, if they are `>0` the image will be aspect resized (down-sample only) to fit inside the rectangle.

Johan Nordberg's avatar
Johan Nordberg committed
139
#### `GET /u/<username>/avatar/[<size>]` - get user avatar image.
Johan Nordberg's avatar
Johan Nordberg committed
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154

Serves the avatar for `username`, if no avatar is set a default image will be served (set in service config).

Sizes are:

  * `small` - 64x64
  * `medium` - 128x128
  * `large` - 512x512

Note that the avatars follow the same sizing rules as proxied images, so you are not guaranteed to get a square image, just an image fitting inside of the `size` square.


Signing uploads
---------------

155
Uploads require a signature made with by a Hive account's posting authority, further that account has to be above a (service configurable) reputation threshold.
Johan Nordberg's avatar
Johan Nordberg committed
156
157
158
159
160
161

Creating a signature (psuedocode):

```python
signature = secp256k1_sign(sha256('ImageSigningChallenge'+image_data), account_private_posting_key)
```
162

163
Creating a signature (node.js & [dhive](https://github.com/openhive-network/dhive))
164

Johan Nordberg's avatar
Johan Nordberg committed
165
166
```js
#!/usr/bin/env node
167

168
const dhive = require('@hiveio/dhive')
Johan Nordberg's avatar
Johan Nordberg committed
169
170
const crypto = require('crypto')
const fs = require('fs')
James Calfee's avatar
James Calfee committed
171

Johan Nordberg's avatar
Johan Nordberg committed
172
const [wif, file] = process.argv.slice(2)
James Calfee's avatar
James Calfee committed
173

Johan Nordberg's avatar
Johan Nordberg committed
174
175
176
177
if (!wif || !file) {
    process.stderr.write(`Usage: ./sign.js <posting_wif> <file>\n`)
    process.exit(1)
}
James Calfee's avatar
James Calfee committed
178

Johan Nordberg's avatar
Johan Nordberg committed
179
const data = fs.readFileSync(file)
180
const key = dhive.PrivateKey.fromString(wif)
Johan Nordberg's avatar
Johan Nordberg committed
181
182
183
184
const imageHash = crypto.createHash('sha256')
    .update('ImageSigningChallenge')
    .update(data)
    .digest()
James Calfee's avatar
James Calfee committed
185

Johan Nordberg's avatar
Johan Nordberg committed
186
process.stdout.write(key.sign(imageHash).toString() + '\n')
James Calfee's avatar
James Calfee committed
187
188
```

Johan Nordberg's avatar
Johan Nordberg committed
189
190
191
```sh
$ ./sign.js 5J9jN691Gf3MKdwvqWVx54drx9qub6koyA3mjhenyN12CURua8W test.jpg
1f78d007a0b12cd17f2d349446c3f9b7cfa096ae53903a11608d6232781fb994a2086263f21e4da831d2a2b0b372f701b83042a629ba3d87791d05f393d5504db2
James Calfee's avatar
James Calfee committed
192
```