Commit b1b5f3bd authored by James Calfee's avatar James Calfee
Browse files

initial commit

parents
script-compiled.js
config/server-dev.json
node_modules
npm-debug.log
bundle.js
\ No newline at end of file
Configure
Create a credentials file at ~/.aws/credentials on Mac/Linux or C:\Users\USERNAME\.aws\credentials on Windows
[default]
aws_access_key_id = your_access_key
aws_secret_access_key = your_secret_key
#### Create config file
```bash
cd config
cp server-config-example.json server-config-dev.json
```
import { Map, List } from 'immutable'
export default class RateLimit {
/**
duration: 60 * 60 * 1000, // 1 hour
max: npm_package_config_network_ip_requests_per_hour
*/
constructor(config) {
required(config.duration, 'config.duration')
required(config.max, 'config.max')
this.config = config
this.hits = Map()
}
byIp(ctx) {
const key = ctx.req.headers['x-forwarded-for'] || ctx.req.connection.remoteAddress;
const event = Date.now()
const expired = event - this.config.duration
this.hits = this.hits
// Add this event
.update(key, List(), events => events.push(event))
// Remove expired events
.update(key, events => events.filter(e => e > expired))
// Remove 'other' keys that no longer have any events
.filterNot(keys => keys.isEmpty())
if(this.config.verbose)
console.log('RateLimit\t', key, '\t', this.hits.get(key).count(), 'of', this.config.max, 'within', (this.config.duration / 1000) + 's')
const over = this.hits.get(key).count() > this.config.max
if(over) {
ctx.status = 429;
ctx.body = 'Too Many Requests';
}
return over
}
}
function required(data, field_name) {
if (data == null) throw new Error('Missing required field: ' + field_name)
return data
}
import AWS from 'aws-sdk'
import config from '../../config/server-config'
const {amazonBucket} = config
const s3 = new AWS.S3()
s3.createBucket({Bucket: amazonBucket})
export default s3
import RateLimit from './RateLimit'
import config from '../../config/server-config'
import s3 from './amazon-bucket'
const {amazonBucket, imagePrefix} = config
const hour = 60 * 60 * 1000
const limit = new RateLimit({ duration: hour, max: 60 * 60, verbose: false })
const router = require('koa-router')()
router.get('/images/:key', function *() {
try {
if (limit.byIp(this)) return;
const {key} = this.params
yield new Promise(resolve => {
const params = {Bucket: amazonBucket, Key: key};
s3.getObject(params, (err, data) => {
if(err) {
console.log(err)
this.status = 404
this.statusText = `Error fetching ${key}.`
resolve()
return
}
// Example format: 'Wed, 28 Sep 2016 21:09:36 GMT'
this.set('Last-Modified', data.LastModified)
this.body = data.Body.toString();
resolve()
})
})
} catch(error) {console.error(error)}
})
export default router.routes()
import Koa from 'koa';
import uploadImage from './upload-image'
import imageServer from './image-server'
import config from '../../config/server-config'
const app = new Koa()
app.use(imageServer)
app.use(uploadImage)
app.listen(config.port)
console.log(`Application started on port ${config.port}`)
\ No newline at end of file
import AWS from 'aws-sdk'
import config from '../../config/server-config'
import {rateLimitReq} from './utils'
const {amazonBucket, imagePrefix} = config
const s3 = new AWS.S3()
const router = require('koa-router')()
const koaBody = require('koa-body')({
multipart: true,
formLimit: 5000 * 1024,
formidable: {
uploadDir: '/tmp',
}
})
router.post('/uploadImage', koaBody, function *() {
try {
const {fields, files} = this.request.body
const f = files['files[]']
const {username} = fields
// console.log('uploadImage', username)
const key = 'myKey'
yield new Promise(resolve => {
s3.createBucket({Bucket: amazonBucket}, () => {
const params = {Bucket: amazonBucket, Key: key, Body: 'Hello!'};
s3.putObject(params, (err, data) => {
if(err) {
console.log(err)
this.status = 404
this.statusText = `Error uploading ${key}.`
resolve()
return
}
console.log(`Successfully uploaded data to ${amazonBucket}/${key}`);
const url = `${imagePrefix}${key}`
this.body = {files: [{url}]}
resolve()
})
})
})
} catch(error) {console.error(error)}
})
export default router.routes()
export function getRemoteIp(req) {
const remote_address = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
const ip_match = remote_address ? remote_address.match(/(\d+\.\d+\.\d+\.\d+)/) : null;
return ip_match ? ip_match[1] : esc(remote_address);
}
// function checkCSRF(ctx, csrf) {
// try { ctx.assertCSRF(csrf); } catch (e) {
// ctx.status = 403;
// ctx.body = 'invalid csrf token';
// console.log('-- invalid csrf token -->', ctx.request.method, ctx.request.url, ctx.session.uid);
// return false;
// }
// return true;
// }
function esc(value, max_length = 256) {
if (!value) return '';
if (typeof value === 'number') return value;
if (typeof value !== 'string') return '(object)';
let res = value.substring(0, max_length - max_length * 0.2).replace(/[\0\x08\x09\x1a\n\r"'\\\%]/g, function (char) {
switch (char) {
case '\0':
return '\\0';
case '\x08':
return '\\b';
case '\x09':
return '\\t';
case '\x1a':
return '\\z';
case '\n':
return '\\n';
case '\r':
return '\\r';
// case '\'':
// case "'":
// case '"':
// case '\\':
// case '%':
// return '\\' + char; // prepends a backslash to backslash, percent, and double/single quotes
}
return '-';
});
return res.length < max_length ? res : '-';
}
{
"port": 3234,
"amazonBucket": "steem-upload-manager-test",
"imagePrefix": "http://localhost:3234/images/"
}
\ No newline at end of file
{
}
\ No newline at end of file
const config = {}
export default config
Object.assign(config, require(process.env.NODE_ENV === 'production' ? './server-config-prod.json' : './server-config-dev.json'))
{
"name": "steem-upload-manager",
"version": "1.0.0",
"description": "",
"main": "upload-image.js",
"scripts": {
"start": "NODE_ENV=development babel-node --presets es2015 ./app/server/server.js ",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"aws-sdk": "^2.6.5",
"koa": "^1.2.4",
"koa-body": "^1.5.0",
"koa-router": "^5.4.0"
},
"devDependencies": {
"babel-cli": "^6.16.0",
"webpack": "^1.13.2"
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment