Initial commit.
This commit is contained in:
commit
b48a4e92e1
169 changed files with 7538 additions and 0 deletions
52
lib/format.js
Normal file
52
lib/format.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
import moment from 'moment';
|
||||
import { sniff } from './type';
|
||||
|
||||
const fileHost = process.env.FILE_HOST || 'http://localhost:4040';
|
||||
|
||||
export function formatDate(timestamp) {
|
||||
return moment.unix(timestamp).format('D MMM YY [at] h:mm A');
|
||||
}
|
||||
|
||||
export function formatSize(size) {
|
||||
if (size >= 1073741824) {
|
||||
size = Math.round((size / 1073741824) * 10) / 10 + 'GB';
|
||||
} else {
|
||||
if (size >= 1048576) {
|
||||
size = Math.round((size / 1048576) * 10) / 10 + 'MB';
|
||||
} else {
|
||||
if (size >= 1024) {
|
||||
size = Math.round((size / 1024) * 10) / 10 + 'KB';
|
||||
} else {
|
||||
size = Math.round(size) + 'B';
|
||||
}
|
||||
}
|
||||
}
|
||||
return size;
|
||||
};
|
||||
|
||||
export function formatFile(file) {
|
||||
const formattedFile = {
|
||||
added: moment.unix(file.time_added).format(),
|
||||
readableAdded: formatDate(file.time_added),
|
||||
downloads: file.downloads !== undefined ? file.downloads : 0,
|
||||
href: fileHost + '/' + file._id, // eslint-disable-line no-underscore-dangle
|
||||
id: file._id, // eslint-disable-line no-underscore-dangle
|
||||
name: file.file_name,
|
||||
size: file.file_size,
|
||||
readableSize: formatSize(file.file_size),
|
||||
type: sniff(file.file_name),
|
||||
trashed: (file.status === 'trashed'),
|
||||
status: file.status
|
||||
};
|
||||
|
||||
if (file.width) {
|
||||
formattedFile.height = file.height;
|
||||
formattedFile.width = file.width;
|
||||
const ext = (file.file_name.split('.').pop().toLowerCase() === 'psd' ? '.png' : '');
|
||||
formattedFile.direct = {
|
||||
'150x': fileHost + '/file/150/' + file._id + '/' + file.file_name + ext, // eslint-disable-line no-underscore-dangle
|
||||
'970x': fileHost + '/file/970/' + file._id + '/' + file.file_name + ext // eslint-disable-line no-underscore-dangle
|
||||
};
|
||||
}
|
||||
return formattedFile;
|
||||
}
|
36
lib/hostr-file-stream.js
Normal file
36
lib/hostr-file-stream.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import createError from 'http-errors';
|
||||
import { get as getFile } from './s3';
|
||||
|
||||
import debugname from 'debug';
|
||||
const debug = debugname('hostr:file-stream');
|
||||
|
||||
export default function* hostrFileStream(localPath, remotePath) {
|
||||
const localRead = fs.createReadStream(localPath);
|
||||
return new Promise((resolve, reject) => {
|
||||
localRead.once('error', () => {
|
||||
debug('local error');
|
||||
const remoteRead = getFile(remotePath);
|
||||
|
||||
remoteRead.once('readable', () => {
|
||||
debug('remote readable');
|
||||
const localWrite = fs.createWriteStream(localPath);
|
||||
localWrite.once('finish', () => {
|
||||
debug('local write end');
|
||||
resolve(fs.createReadStream(localPath));
|
||||
});
|
||||
remoteRead.pipe(localWrite);
|
||||
});
|
||||
|
||||
remoteRead.once('error', () => {
|
||||
debug('remote error');
|
||||
reject(createError(404));
|
||||
});
|
||||
});
|
||||
localRead.once('readable', () => {
|
||||
debug('local readable');
|
||||
resolve(localRead);
|
||||
});
|
||||
});
|
||||
}
|
26
lib/hostr-id.js
Normal file
26
lib/hostr-id.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
|
||||
function randomID() {
|
||||
let rand = '';
|
||||
for (let i = 0; i < 12; i++) {
|
||||
rand += chars.charAt(Math.floor((Math.random() * chars.length)));
|
||||
}
|
||||
return rand;
|
||||
}
|
||||
|
||||
function* checkId(Files, fileId, attempts) {
|
||||
if (attempts > 10) {
|
||||
return false;
|
||||
}
|
||||
const file = yield Files.findOne({'_id': fileId});
|
||||
if(file === null) {
|
||||
return fileId;
|
||||
} else {
|
||||
return checkId(randomID(), attempts++);
|
||||
}
|
||||
}
|
||||
|
||||
export default function* (Files) {
|
||||
let attempts = 0;
|
||||
return yield checkId(Files, randomID(), attempts);
|
||||
}
|
75
lib/koa-error.js
Normal file
75
lib/koa-error.js
Normal file
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var swig = require('swig');
|
||||
var http = require('http');
|
||||
|
||||
/**
|
||||
* Expose `error`.
|
||||
*/
|
||||
|
||||
module.exports = error;
|
||||
|
||||
/**
|
||||
* Error middleware.
|
||||
*
|
||||
* - `template` defaults to ./error.html
|
||||
*
|
||||
* @param {Object} opts
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function error(opts) {
|
||||
opts = opts || {};
|
||||
|
||||
// template
|
||||
var path = opts.template || __dirname + '/error.html';
|
||||
var render = swig.compileFile(path);
|
||||
|
||||
// env
|
||||
var env = process.env.NODE_ENV || 'development';
|
||||
|
||||
return function *error(next){
|
||||
try {
|
||||
yield next;
|
||||
if (404 == this.response.status && !this.response.body) this.throw(404);
|
||||
} catch (err) {
|
||||
this.status = err.status || 500;
|
||||
|
||||
// application
|
||||
this.app.emit('error', err, this);
|
||||
|
||||
// accepted types
|
||||
switch (this.accepts('html', 'text', 'json')) {
|
||||
case 'text':
|
||||
this.type = 'text/plain';
|
||||
if ('development' == env) this.body = err.message
|
||||
else if (err.expose) this.body = err.message
|
||||
else throw err;
|
||||
break;
|
||||
|
||||
case 'json':
|
||||
this.type = 'application/json';
|
||||
if ('development' == env) this.body = { error: err.message }
|
||||
else if (err.expose) this.body = { error: err.message }
|
||||
else this.body = { error: http.STATUS_CODES[this.status] }
|
||||
break;
|
||||
|
||||
case 'html':
|
||||
this.type = 'text/html';
|
||||
this.body = render({
|
||||
env: env,
|
||||
ctx: this,
|
||||
request: this.request,
|
||||
response: this.response,
|
||||
error: err.message,
|
||||
stack: err.stack,
|
||||
status: this.status,
|
||||
code: err.code
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
38
lib/malware.js
Normal file
38
lib/malware.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
import virustotal from 'virustotal.js';
|
||||
|
||||
virustotal.setKey(process.env.VIRUSTOTAL);
|
||||
|
||||
const extensions = ['EXE', 'PIF', 'APPLICATION', 'GADGET', 'MSI', 'MSP', 'COM', 'SCR', 'HTA', 'CPL', 'MSC',
|
||||
'JAR', 'BAT', 'CMD', 'VB', 'VBS', 'VBE', 'JS', 'JSE', 'WS', 'WSF', 'WSC', 'WSH', 'PS1', 'PS1XML', 'PS2',
|
||||
'PS2XML', 'PSC1', 'PSC2', 'MSH', 'MSH1', 'MSH2', 'MSHXML', 'MSH1XML', 'MSH2XML', 'SCF', 'LNK', 'INF', 'REG',
|
||||
'PDF', 'DOC', 'XLS', 'PPT', 'DOCM', 'DOTM', 'XLSM', 'XLTM', 'XLAM', 'PPTM', 'POTM', 'PPAM', 'PPSM', 'SLDM',
|
||||
'RAR', 'TAR', 'ZIP', 'GZ'
|
||||
];
|
||||
|
||||
function getExtension(filename) {
|
||||
const i = filename.lastIndexOf('.');
|
||||
return (i < 0) ? '' : filename.substr(i + 1);
|
||||
};
|
||||
|
||||
export default function (file) {
|
||||
const deferred = {};
|
||||
deferred.promise = new Promise(function(resolve, reject) {
|
||||
deferred.resolve = resolve;
|
||||
deferred.reject = reject;
|
||||
});
|
||||
if (extensions.indexOf(getExtension(file.file_name.toUpperCase())) >= 0) {
|
||||
virustotal.getFileReport(file.md5, function (err, res) {
|
||||
if (err) {
|
||||
return deferred.reject(err);
|
||||
}
|
||||
if (res.scans) {
|
||||
deferred.resolve({positive: res.positives >= 5, result: res});
|
||||
} else {
|
||||
deferred.resolve();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
deferred.resolve();
|
||||
}
|
||||
return deferred.promise;
|
||||
};
|
10
lib/resize.js
Normal file
10
lib/resize.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
import debugname from 'debug';
|
||||
const debug = debugname('hostr-api:resize');
|
||||
import gm from 'gm';
|
||||
|
||||
export default function(input, size) {
|
||||
debug('Resizing');
|
||||
const image = gm(input);
|
||||
|
||||
return image.resize(size.width, size.height, '>').stream();
|
||||
}
|
19
lib/s3.js
Normal file
19
lib/s3.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
import aws from 'aws-sdk';
|
||||
import s3UploadStream from 's3-upload-stream';
|
||||
import debugname from 'debug';
|
||||
const debug = debugname('hostr:s3');
|
||||
|
||||
const bucket = process.env.AWS_BUCKET || 'hostrdotcodev';
|
||||
|
||||
const s3 = new aws.S3();
|
||||
const s3Stream = s3UploadStream(s3);
|
||||
|
||||
export function get(key) {
|
||||
debug('fetching file: %s', 'hostr_files/' + key);
|
||||
return s3.getObject({Bucket: bucket, Key: 'hostr_files/' + key}).createReadStream();
|
||||
}
|
||||
|
||||
export function upload(key, body) {
|
||||
debug('Uploading file: %s', 'hostr_files/' + key);
|
||||
return s3Stream.upload({Bucket: bucket, Key: 'hostr_files/' + key});
|
||||
}
|
24
lib/storage.js
Normal file
24
lib/storage.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
function range(start,stop) {
|
||||
var result=[];
|
||||
for (var idx=start.charCodeAt(0),end=stop.charCodeAt(0); idx <=end; ++idx){
|
||||
result.push(String.fromCharCode(idx));
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const storePath = process.env.FILE_PATH || path.join(process.env.HOME, '.hostr', 'uploads');
|
||||
|
||||
const directories = range('A', 'Z').concat(range('a', 'z'), range('0', '9'));
|
||||
|
||||
export function init() {
|
||||
directories.forEach((directory) => {
|
||||
if (!fs.existsSync(path.join(storePath, directory))) {
|
||||
fs.mkdirSync(path.join(storePath, directory));
|
||||
fs.mkdirSync(path.join(storePath, directory, '150'));
|
||||
fs.mkdirSync(path.join(storePath, directory, '970'));
|
||||
}
|
||||
});
|
||||
}
|
34
lib/type.js
Normal file
34
lib/type.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
const extensions = {
|
||||
'jpg': 'image',
|
||||
'jpeg': 'image',
|
||||
'png': 'image',
|
||||
'gif': 'image',
|
||||
'bmp': 'image',
|
||||
'tiff': 'image',
|
||||
'psd': 'image',
|
||||
'mp3': 'audio',
|
||||
'm4a': 'audio',
|
||||
'ogg': 'audio',
|
||||
'flac': 'audio',
|
||||
'aac': 'audio',
|
||||
'mpg': 'video',
|
||||
'mkv': 'video',
|
||||
'avi': 'video',
|
||||
'divx': 'video',
|
||||
'mpeg': 'video',
|
||||
'flv': 'video',
|
||||
'mp4': 'video',
|
||||
'mov': 'video',
|
||||
'zip': 'archive',
|
||||
'gz': 'archive',
|
||||
'tgz': 'archive',
|
||||
'bz2': 'archive',
|
||||
'rar': 'archive'
|
||||
};
|
||||
|
||||
export function sniff(filename) {
|
||||
if (extensions[filename.split('.').pop().toLowerCase()]) {
|
||||
return extensions[filename.split('.').pop().toLowerCase()];
|
||||
}
|
||||
return 'other';
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue