mirror of https://github.com/Requarks/wiki.git
NGPixel
8 years ago
12 changed files with 367 additions and 119 deletions
Unified View
Diff Options
-
84agent.js
-
2assets/js/app.js
-
67client/js/components/editor-image.js
-
10controllers/pages.js
-
15controllers/uploads.js
-
2models/entries.js
-
17models/git.js
-
64models/localdata.js
-
176models/uploads.js
-
1package.json
-
32views/error-notexist.pug
-
16ws-server.js
2
assets/js/app.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,176 @@ |
|||||
|
"use strict"; |
||||
|
|
||||
|
var path = require('path'), |
||||
|
Promise = require('bluebird'), |
||||
|
fs = Promise.promisifyAll(require('fs-extra')), |
||||
|
readChunk = require('read-chunk'), |
||||
|
fileType = require('file-type'), |
||||
|
farmhash = require('farmhash'), |
||||
|
moment = require('moment'), |
||||
|
chokidar = require('chokidar'), |
||||
|
_ = require('lodash'); |
||||
|
|
||||
|
/** |
||||
|
* Uploads |
||||
|
* |
||||
|
* @param {Object} appconfig The application configuration |
||||
|
*/ |
||||
|
module.exports = { |
||||
|
|
||||
|
_uploadsPath: './repo/uploads', |
||||
|
_uploadsThumbsPath: './data/thumbs', |
||||
|
|
||||
|
_watcher: null, |
||||
|
|
||||
|
/** |
||||
|
* Initialize Uploads model |
||||
|
* |
||||
|
* @param {Object} appconfig The application config |
||||
|
* @return {Object} Uploads model instance |
||||
|
*/ |
||||
|
init(appconfig) { |
||||
|
|
||||
|
let self = this; |
||||
|
|
||||
|
self._uploadsPath = path.resolve(ROOTPATH, appconfig.datadir.repo, 'uploads'); |
||||
|
self._uploadsThumbsPath = path.resolve(ROOTPATH, appconfig.datadir.db, 'thumbs'); |
||||
|
|
||||
|
return self; |
||||
|
|
||||
|
}, |
||||
|
|
||||
|
watch() { |
||||
|
|
||||
|
let self = this; |
||||
|
|
||||
|
self._watcher = chokidar.watch(self._uploadsPath, { |
||||
|
persistent: true, |
||||
|
ignoreInitial: true, |
||||
|
cwd: self._uploadsPath, |
||||
|
depth: 1, |
||||
|
awaitWriteFinish: true |
||||
|
}); |
||||
|
|
||||
|
self._watcher.on('add', (p) => { |
||||
|
|
||||
|
let pInfo = lcdata.parseUploadsRelPath(p); |
||||
|
return self.processFile(pInfo.folder, pInfo.filename).then((mData) => { |
||||
|
ws.emit('uploadsAddFiles', { |
||||
|
auth: WSInternalKey, |
||||
|
content: mData |
||||
|
}); |
||||
|
}).then(() => { |
||||
|
return git.commitUploads(); |
||||
|
}); |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
}, |
||||
|
|
||||
|
processFile(fldName, f) { |
||||
|
|
||||
|
let self = this; |
||||
|
|
||||
|
let fldPath = path.join(self._uploadsPath, fldName); |
||||
|
let fPath = path.join(fldPath, f); |
||||
|
let fPathObj = path.parse(fPath); |
||||
|
let fUid = farmhash.fingerprint32(fldName + '/' + f); |
||||
|
|
||||
|
return fs.statAsync(fPath).then((s) => { |
||||
|
|
||||
|
if(!s.isFile()) { return false; } |
||||
|
|
||||
|
// Get MIME info
|
||||
|
|
||||
|
let mimeInfo = fileType(readChunk.sync(fPath, 0, 262)); |
||||
|
|
||||
|
// Images
|
||||
|
|
||||
|
if(s.size < 3145728) { // ignore files larger than 3MB
|
||||
|
if(_.includes(['image/png', 'image/jpeg', 'image/gif', 'image/webp'], mimeInfo.mime)) { |
||||
|
return self.getImageMetadata(fPath).then((mData) => { |
||||
|
|
||||
|
let cacheThumbnailPath = path.parse(path.join(self._uploadsThumbsPath, fUid + '.png')); |
||||
|
let cacheThumbnailPathStr = path.format(cacheThumbnailPath); |
||||
|
|
||||
|
mData = _.pick(mData, ['format', 'width', 'height', 'density', 'hasAlpha', 'orientation']); |
||||
|
mData.uid = fUid; |
||||
|
mData.category = 'image'; |
||||
|
mData.mime = mimeInfo.mime; |
||||
|
mData.folder = fldName; |
||||
|
mData.filename = f; |
||||
|
mData.basename = fPathObj.name; |
||||
|
mData.filesize = s.size; |
||||
|
mData.uploadedOn = moment().utc(); |
||||
|
|
||||
|
// Generate thumbnail
|
||||
|
|
||||
|
return fs.statAsync(cacheThumbnailPathStr).then((st) => { |
||||
|
return st.isFile(); |
||||
|
}).catch((err) => { |
||||
|
return false; |
||||
|
}).then((thumbExists) => { |
||||
|
|
||||
|
return (thumbExists) ? mData : fs.ensureDirAsync(cacheThumbnailPath.dir).then(() => { |
||||
|
return self.generateThumbnail(fPath, cacheThumbnailPathStr); |
||||
|
}).return(mData); |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Other Files
|
||||
|
|
||||
|
return { |
||||
|
uid: fUid, |
||||
|
category: 'file', |
||||
|
mime: mimeInfo.mime, |
||||
|
folder: fldName, |
||||
|
filename: f, |
||||
|
basename: fPathObj.name, |
||||
|
filesize: s.size, |
||||
|
uploadedOn: moment().utc() |
||||
|
}; |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Generate thumbnail of image |
||||
|
* |
||||
|
* @param {String} sourcePath The source path |
||||
|
* @return {Promise<Object>} Promise returning the resized image info |
||||
|
*/ |
||||
|
generateThumbnail(sourcePath, destPath) { |
||||
|
|
||||
|
let sharp = require('sharp'); |
||||
|
|
||||
|
return sharp(sourcePath) |
||||
|
.withoutEnlargement() |
||||
|
.resize(150,150) |
||||
|
.background('white') |
||||
|
.embed() |
||||
|
.flatten() |
||||
|
.toFormat('png') |
||||
|
.toFile(destPath); |
||||
|
|
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Gets the image metadata. |
||||
|
* |
||||
|
* @param {String} sourcePath The source path |
||||
|
* @return {Object} The image metadata. |
||||
|
*/ |
||||
|
getImageMetadata(sourcePath) { |
||||
|
|
||||
|
let sharp = require('sharp'); |
||||
|
|
||||
|
return sharp(sourcePath).metadata(); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
}; |
@ -0,0 +1,32 @@ |
|||||
|
doctype html |
||||
|
html |
||||
|
head |
||||
|
meta(http-equiv='X-UA-Compatible', content='IE=edge') |
||||
|
meta(charset='UTF-8') |
||||
|
meta(name='viewport', content='width=device-width, initial-scale=1') |
||||
|
meta(name='theme-color', content='#009688') |
||||
|
meta(name='msapplication-TileColor', content='#009688') |
||||
|
meta(name='msapplication-TileImage', content='/favicons/ms-icon-144x144.png') |
||||
|
title= appconfig.title |
||||
|
|
||||
|
// Favicon |
||||
|
each favsize in [57, 60, 72, 76, 114, 120, 144, 152, 180] |
||||
|
link(rel='apple-touch-icon', sizes=favsize + 'x' + favsize, href='/favicons/apple-icon-' + favsize + 'x' + favsize + '.png') |
||||
|
link(rel='icon', type='image/png', sizes='192x192', href='/favicons/android-icon-192x192.png') |
||||
|
each favsize in [32, 96, 16] |
||||
|
link(rel='icon', type='image/png', sizes=favsize + 'x' + favsize, href='/favicons/favicon-' + favsize + 'x' + favsize + '.png') |
||||
|
link(rel='manifest', href='/manifest.json') |
||||
|
|
||||
|
// CSS |
||||
|
link(type='text/css', rel='stylesheet', href='/css/libs.css') |
||||
|
link(type='text/css', rel='stylesheet', href='/css/app.css') |
||||
|
|
||||
|
body(class='server-error') |
||||
|
section.hero.is-dark.is-fullheight |
||||
|
.hero-body |
||||
|
.container |
||||
|
a(href='/'): img(src='/favicons/android-icon-96x96.png') |
||||
|
h1.title(style={ 'margin-top': '30px'})= message |
||||
|
h2.subtitle(style={ 'margin-bottom': '50px'}) Would you like to create this entry? |
||||
|
a.button.is-dark.is-inverted(href='/create/' + newpath, style={'margin-right': '5px'}) Create |
||||
|
a.button.is-dark.is-inverted(href='/') Go Home |
Write
Preview
Loading…
Cancel
Save