feat: image upload + list root assets (wip)
This commit is contained in:
@@ -17,8 +17,12 @@ defaults:
|
||||
storage: ./db.sqlite
|
||||
ssl:
|
||||
enabled: false
|
||||
pool: {}
|
||||
bindIP: 0.0.0.0
|
||||
logLevel: info
|
||||
uploads:
|
||||
maxFileSize: 5242880
|
||||
maxFiles: 10
|
||||
# DB defaults
|
||||
graphEndpoint: 'https://graph.requarks.io'
|
||||
lang:
|
||||
|
79
server/controllers/upload.js
Normal file
79
server/controllers/upload.js
Normal file
@@ -0,0 +1,79 @@
|
||||
const express = require('express')
|
||||
const router = express.Router()
|
||||
const _ = require('lodash')
|
||||
const multer = require('multer')
|
||||
const path = require('path')
|
||||
const sanitize = require('sanitize-filename')
|
||||
|
||||
/* global WIKI */
|
||||
|
||||
/**
|
||||
* Upload files
|
||||
*/
|
||||
router.post('/u', multer({
|
||||
dest: path.join(WIKI.ROOTPATH, 'data/uploads'),
|
||||
limits: {
|
||||
fileSize: WIKI.config.uploads.maxFileSize,
|
||||
files: WIKI.config.uploads.maxFiles
|
||||
}
|
||||
}).array('mediaUpload'), async (req, res, next) => {
|
||||
if (!_.some(req.user.permissions, pm => _.includes(['write:assets', 'manage:system'], pm))) {
|
||||
return res.status(403).json({
|
||||
succeeded: false,
|
||||
message: 'You are not authorized to upload files.'
|
||||
})
|
||||
} else if (req.files.length < 1) {
|
||||
return res.status(400).json({
|
||||
succeeded: false,
|
||||
message: 'Missing upload payload.'
|
||||
})
|
||||
} else if (req.files.length > 1) {
|
||||
return res.status(400).json({
|
||||
succeeded: false,
|
||||
message: 'You cannot upload multiple files within the same request.'
|
||||
})
|
||||
}
|
||||
const fileMeta = _.get(req, 'files[0]', false)
|
||||
if (!fileMeta) {
|
||||
return res.status(500).json({
|
||||
succeeded: false,
|
||||
message: 'Missing upload file metadata.'
|
||||
})
|
||||
}
|
||||
|
||||
let folderPath = ''
|
||||
try {
|
||||
const folderRaw = _.get(req, 'body.mediaUpload', false)
|
||||
if (folderRaw) {
|
||||
folderPath = _.get(JSON.parse(folderRaw), 'path', false)
|
||||
}
|
||||
} catch (err) {
|
||||
return res.status(400).json({
|
||||
succeeded: false,
|
||||
message: 'Missing upload folder metadata.'
|
||||
})
|
||||
}
|
||||
|
||||
if (!WIKI.auth.checkAccess(req.user, ['write:assets'], { path: `${folderPath}/${fileMeta.originalname}`})) {
|
||||
return res.status(403).json({
|
||||
succeeded: false,
|
||||
message: 'You are not authorized to upload files to this folder.'
|
||||
})
|
||||
}
|
||||
|
||||
await WIKI.models.assets.upload({
|
||||
...fileMeta,
|
||||
originalname: sanitize(fileMeta.originalname).toLowerCase(),
|
||||
folder: folderPath,
|
||||
userId: req.user.id
|
||||
})
|
||||
res.send('ok')
|
||||
})
|
||||
|
||||
router.get('/u', async (req, res, next) => {
|
||||
res.json({
|
||||
ok: true
|
||||
})
|
||||
})
|
||||
|
||||
module.exports = router
|
@@ -67,6 +67,7 @@ module.exports = {
|
||||
asyncStackTraces: WIKI.IS_DEBUG,
|
||||
connection: dbConfig,
|
||||
pool: {
|
||||
...WIKI.config.pool,
|
||||
async afterCreate(conn, done) {
|
||||
// -> Set Connection App Name
|
||||
switch (WIKI.config.db.type) {
|
||||
|
@@ -1,15 +1,10 @@
|
||||
exports.up = knex => {
|
||||
const dbCompat = {
|
||||
charset: (WIKI.config.db.type === `mysql` || WIKI.config.db.type === `mariadb`),
|
||||
noForeign: WIKI.config.db.type === 'sqlite'
|
||||
}
|
||||
return knex.schema
|
||||
// =====================================
|
||||
// MODEL TABLES
|
||||
// =====================================
|
||||
// ASSETS ------------------------------
|
||||
.createTable('assets', table => {
|
||||
if (dbCompat.charset) { table.charset('utf8mb4') }
|
||||
table.increments('id').primary()
|
||||
table.string('filename').notNullable()
|
||||
table.string('basename').notNullable()
|
||||
@@ -26,7 +21,6 @@ exports.up = knex => {
|
||||
})
|
||||
// ASSET FOLDERS -----------------------
|
||||
.createTable('assetFolders', table => {
|
||||
if (dbCompat.charset) { table.charset('utf8mb4') }
|
||||
table.increments('id').primary()
|
||||
table.string('name').notNullable()
|
||||
table.string('slug').notNullable()
|
||||
@@ -34,7 +28,6 @@ exports.up = knex => {
|
||||
})
|
||||
// AUTHENTICATION ----------------------
|
||||
.createTable('authentication', table => {
|
||||
if (dbCompat.charset) { table.charset('utf8mb4') }
|
||||
table.string('key').notNullable().primary()
|
||||
table.boolean('isEnabled').notNullable().defaultTo(false)
|
||||
table.json('config').notNullable()
|
||||
@@ -44,7 +37,6 @@ exports.up = knex => {
|
||||
})
|
||||
// COMMENTS ----------------------------
|
||||
.createTable('comments', table => {
|
||||
if (dbCompat.charset) { table.charset('utf8mb4') }
|
||||
table.increments('id').primary()
|
||||
table.text('content').notNullable()
|
||||
table.string('createdAt').notNullable()
|
||||
@@ -55,14 +47,12 @@ exports.up = knex => {
|
||||
})
|
||||
// EDITORS -----------------------------
|
||||
.createTable('editors', table => {
|
||||
if (dbCompat.charset) { table.charset('utf8mb4') }
|
||||
table.string('key').notNullable().primary()
|
||||
table.boolean('isEnabled').notNullable().defaultTo(false)
|
||||
table.json('config').notNullable()
|
||||
})
|
||||
// GROUPS ------------------------------
|
||||
.createTable('groups', table => {
|
||||
if (dbCompat.charset) { table.charset('utf8mb4') }
|
||||
table.increments('id').primary()
|
||||
table.string('name').notNullable()
|
||||
table.json('permissions').notNullable()
|
||||
@@ -73,7 +63,6 @@ exports.up = knex => {
|
||||
})
|
||||
// LOCALES -----------------------------
|
||||
.createTable('locales', table => {
|
||||
if (dbCompat.charset) { table.charset('utf8mb4') }
|
||||
table.string('code', 2).notNullable().primary()
|
||||
table.json('strings')
|
||||
table.boolean('isRTL').notNullable().defaultTo(false)
|
||||
@@ -84,7 +73,6 @@ exports.up = knex => {
|
||||
})
|
||||
// LOGGING ----------------------------
|
||||
.createTable('loggers', table => {
|
||||
if (dbCompat.charset) { table.charset('utf8mb4') }
|
||||
table.string('key').notNullable().primary()
|
||||
table.boolean('isEnabled').notNullable().defaultTo(false)
|
||||
table.string('level').notNullable().defaultTo('warn')
|
||||
@@ -92,13 +80,11 @@ exports.up = knex => {
|
||||
})
|
||||
// NAVIGATION ----------------------------
|
||||
.createTable('navigation', table => {
|
||||
if (dbCompat.charset) { table.charset('utf8mb4') }
|
||||
table.string('key').notNullable().primary()
|
||||
table.json('config')
|
||||
})
|
||||
// PAGE HISTORY ------------------------
|
||||
.createTable('pageHistory', table => {
|
||||
if (dbCompat.charset) { table.charset('utf8mb4') }
|
||||
table.increments('id').primary()
|
||||
table.string('path').notNullable()
|
||||
table.string('hash').notNullable()
|
||||
@@ -119,7 +105,6 @@ exports.up = knex => {
|
||||
})
|
||||
// PAGES -------------------------------
|
||||
.createTable('pages', table => {
|
||||
if (dbCompat.charset) { table.charset('utf8mb4') }
|
||||
table.increments('id').primary()
|
||||
table.string('path').notNullable()
|
||||
table.string('hash').notNullable()
|
||||
@@ -144,7 +129,6 @@ exports.up = knex => {
|
||||
})
|
||||
// PAGE TREE ---------------------------
|
||||
.createTable('pageTree', table => {
|
||||
if (dbCompat.charset) { table.charset('utf8mb4') }
|
||||
table.increments('id').primary()
|
||||
table.string('path').notNullable()
|
||||
table.integer('depth').unsigned().notNullable()
|
||||
@@ -159,28 +143,24 @@ exports.up = knex => {
|
||||
})
|
||||
// RENDERERS ---------------------------
|
||||
.createTable('renderers', table => {
|
||||
if (dbCompat.charset) { table.charset('utf8mb4') }
|
||||
table.string('key').notNullable().primary()
|
||||
table.boolean('isEnabled').notNullable().defaultTo(false)
|
||||
table.json('config')
|
||||
})
|
||||
// SEARCH ------------------------------
|
||||
.createTable('searchEngines', table => {
|
||||
if (dbCompat.charset) { table.charset('utf8mb4') }
|
||||
table.string('key').notNullable().primary()
|
||||
table.boolean('isEnabled').notNullable().defaultTo(false)
|
||||
table.json('config')
|
||||
})
|
||||
// SETTINGS ----------------------------
|
||||
.createTable('settings', table => {
|
||||
if (dbCompat.charset) { table.charset('utf8mb4') }
|
||||
table.string('key').notNullable().primary()
|
||||
table.json('value')
|
||||
table.string('updatedAt').notNullable()
|
||||
})
|
||||
// STORAGE -----------------------------
|
||||
.createTable('storage', table => {
|
||||
if (dbCompat.charset) { table.charset('utf8mb4') }
|
||||
table.string('key').notNullable().primary()
|
||||
table.boolean('isEnabled').notNullable().defaultTo(false)
|
||||
table.string('mode', ['sync', 'push', 'pull']).notNullable().defaultTo('push')
|
||||
@@ -188,7 +168,6 @@ exports.up = knex => {
|
||||
})
|
||||
// TAGS --------------------------------
|
||||
.createTable('tags', table => {
|
||||
if (dbCompat.charset) { table.charset('utf8mb4') }
|
||||
table.increments('id').primary()
|
||||
table.string('tag').notNullable().unique()
|
||||
table.string('title')
|
||||
@@ -197,7 +176,6 @@ exports.up = knex => {
|
||||
})
|
||||
// USER KEYS ---------------------------
|
||||
.createTable('userKeys', table => {
|
||||
if (dbCompat.charset) { table.charset('utf8mb4') }
|
||||
table.increments('id').primary()
|
||||
table.string('kind').notNullable()
|
||||
table.string('token').notNullable()
|
||||
@@ -208,7 +186,6 @@ exports.up = knex => {
|
||||
})
|
||||
// USERS -------------------------------
|
||||
.createTable('users', table => {
|
||||
if (dbCompat.charset) { table.charset('utf8mb4') }
|
||||
table.increments('id').primary()
|
||||
table.string('email').notNullable()
|
||||
table.string('name').notNullable()
|
||||
@@ -235,21 +212,18 @@ exports.up = knex => {
|
||||
// =====================================
|
||||
// PAGE HISTORY TAGS ---------------------------
|
||||
.createTable('pageHistoryTags', table => {
|
||||
if (dbCompat.charset) { table.charset('utf8mb4') }
|
||||
table.increments('id').primary()
|
||||
table.integer('pageId').unsigned().references('id').inTable('pageHistory').onDelete('CASCADE')
|
||||
table.integer('tagId').unsigned().references('id').inTable('tags').onDelete('CASCADE')
|
||||
})
|
||||
// PAGE TAGS ---------------------------
|
||||
.createTable('pageTags', table => {
|
||||
if (dbCompat.charset) { table.charset('utf8mb4') }
|
||||
table.increments('id').primary()
|
||||
table.integer('pageId').unsigned().references('id').inTable('pages').onDelete('CASCADE')
|
||||
table.integer('tagId').unsigned().references('id').inTable('tags').onDelete('CASCADE')
|
||||
})
|
||||
// USER GROUPS -------------------------
|
||||
.createTable('userGroups', table => {
|
||||
if (dbCompat.charset) { table.charset('utf8mb4') }
|
||||
table.increments('id').primary()
|
||||
table.integer('userId').unsigned().references('id').inTable('users').onDelete('CASCADE')
|
||||
table.integer('groupId').unsigned().references('id').inTable('groups').onDelete('CASCADE')
|
||||
@@ -257,39 +231,7 @@ exports.up = knex => {
|
||||
// =====================================
|
||||
// REFERENCES
|
||||
// =====================================
|
||||
// .table('assets', table => {
|
||||
// dbCompat.noForeign ? table.integer('folderId').unsigned() : table.integer('folderId').unsigned().references('id').inTable('assetFolders')
|
||||
// dbCompat.noForeign ? table.integer('authorId').unsigned() : table.integer('authorId').unsigned().references('id').inTable('users')
|
||||
// })
|
||||
// .table('comments', table => {
|
||||
// dbCompat.noForeign ? table.integer('pageId').unsigned() : table.integer('pageId').unsigned().references('id').inTable('pages')
|
||||
// dbCompat.noForeign ? table.integer('authorId').unsigned() : table.integer('authorId').unsigned().references('id').inTable('users')
|
||||
// })
|
||||
// .table('pageHistory', table => {
|
||||
// dbCompat.noForeign ? table.integer('pageId').unsigned() : table.integer('pageId').unsigned().references('id').inTable('pages')
|
||||
// dbCompat.noForeign ? table.string('editorKey') : table.string('editorKey').references('key').inTable('editors')
|
||||
// dbCompat.noForeign ? table.string('localeCode', 2) : table.string('localeCode', 2).references('code').inTable('locales')
|
||||
// dbCompat.noForeign ? table.integer('authorId').unsigned() : table.integer('authorId').unsigned().references('id').inTable('users')
|
||||
// })
|
||||
// .table('pages', table => {
|
||||
// dbCompat.noForeign ? table.string('editorKey') : table.string('editorKey').references('key').inTable('editors')
|
||||
// dbCompat.noForeign ? table.string('localeCode', 2) : table.string('localeCode', 2).references('code').inTable('locales')
|
||||
// dbCompat.noForeign ? table.integer('authorId').unsigned() : table.integer('authorId').unsigned().references('id').inTable('users')
|
||||
// dbCompat.noForeign ? table.integer('creatorId').unsigned() : table.integer('creatorId').unsigned().references('id').inTable('users')
|
||||
// })
|
||||
// .table('pageTree', table => {
|
||||
// dbCompat.noForeign ? table.integer('parent').unsigned() : table.integer('parent').unsigned().references('id').inTable('pageTree')
|
||||
// dbCompat.noForeign ? table.integer('pageId').unsigned() : table.integer('pageId').unsigned().references('id').inTable('pages')
|
||||
// dbCompat.noForeign ? table.string('localeCode', 2) : table.string('localeCode', 2).references('code').inTable('locales')
|
||||
// })
|
||||
// .table('userKeys', table => {
|
||||
// dbCompat.noForeign ? table.integer('userId').unsigned() : table.integer('userId').unsigned().references('id').inTable('users')
|
||||
// })
|
||||
.table('users', table => {
|
||||
// dbCompat.noForeign ? table.string('providerKey') : table.string('providerKey').references('key').inTable('authentication').notNullable().defaultTo('local')
|
||||
// dbCompat.noForeign ? table.string('localeCode', 2) : table.string('localeCode', 2).references('code').inTable('locales').notNullable().defaultTo('en')
|
||||
// dbCompat.noForeign ? table.string('defaultEditor') : table.string('defaultEditor').references('key').inTable('editors').notNullable().defaultTo('markdown')
|
||||
|
||||
table.unique(['providerKey', 'email'])
|
||||
})
|
||||
}
|
||||
|
15
server/db/migrations-sqlite/2.0.0-beta.127.js
Normal file
15
server/db/migrations-sqlite/2.0.0-beta.127.js
Normal file
@@ -0,0 +1,15 @@
|
||||
exports.up = knex => {
|
||||
return knex.schema
|
||||
.table('assets', table => {
|
||||
table.dropColumn('basename')
|
||||
table.string('hash').notNullable()
|
||||
})
|
||||
}
|
||||
|
||||
exports.down = knex => {
|
||||
return knex.schema
|
||||
.table('assets', table => {
|
||||
table.dropColumn('hash')
|
||||
table.string('basename').notNullable()
|
||||
})
|
||||
}
|
@@ -1,10 +1,6 @@
|
||||
exports.up = knex => {
|
||||
const dbCompat = {
|
||||
charset: (WIKI.config.db.type === `mysql` || WIKI.config.db.type === `mariadb`)
|
||||
}
|
||||
return knex.schema
|
||||
.createTable('assetData', table => {
|
||||
if (dbCompat.charset) { table.charset('utf8mb4') }
|
||||
table.integer('id').primary()
|
||||
table.binary('data').notNullable()
|
||||
})
|
||||
|
15
server/db/migrations/2.0.0-beta.127.js
Normal file
15
server/db/migrations/2.0.0-beta.127.js
Normal file
@@ -0,0 +1,15 @@
|
||||
exports.up = knex => {
|
||||
return knex.schema
|
||||
.table('assets', table => {
|
||||
table.dropColumn('basename')
|
||||
table.string('hash').notNullable()
|
||||
})
|
||||
}
|
||||
|
||||
exports.down = knex => {
|
||||
return knex.schema
|
||||
.table('assets', table => {
|
||||
table.dropColumn('hash')
|
||||
table.string('basename').notNullable()
|
||||
})
|
||||
}
|
@@ -7,7 +7,7 @@ const PubSub = require('graphql-subscriptions').PubSub
|
||||
const { LEVEL, MESSAGE } = require('triple-beam')
|
||||
const Transport = require('winston-transport')
|
||||
const { createRateLimitTypeDef } = require('graphql-rate-limit-directive')
|
||||
const { GraphQLUpload } = require('graphql-upload')
|
||||
// const { GraphQLUpload } = require('graphql-upload')
|
||||
|
||||
/* global WIKI */
|
||||
|
||||
@@ -28,7 +28,7 @@ schemas.forEach(schema => {
|
||||
// Resolvers
|
||||
|
||||
let resolvers = {
|
||||
Upload: GraphQLUpload
|
||||
// Upload: GraphQLUpload
|
||||
}
|
||||
const resolversObj = _.values(autoload(path.join(WIKI.SERVERPATH, 'graph/resolvers')))
|
||||
resolversObj.forEach(resolver => {
|
||||
|
60
server/graph/resolvers/asset.js
Normal file
60
server/graph/resolvers/asset.js
Normal file
@@ -0,0 +1,60 @@
|
||||
|
||||
/* global WIKI */
|
||||
|
||||
const gql = require('graphql')
|
||||
|
||||
module.exports = {
|
||||
Query: {
|
||||
async assets() { return {} }
|
||||
},
|
||||
Mutation: {
|
||||
async assets() { return {} }
|
||||
},
|
||||
AssetQuery: {
|
||||
async list(obj, args, context) {
|
||||
const result = await WIKI.models.assets.query().where({
|
||||
folderId: null,
|
||||
kind: args.kind.toLowerCase()
|
||||
})
|
||||
return result.map(a => ({
|
||||
...a,
|
||||
kind: a.kind.toUpperCase()
|
||||
}))
|
||||
}
|
||||
},
|
||||
AssetMutation: {
|
||||
// deleteFile(obj, args) {
|
||||
// return WIKI.models.File.destroy({
|
||||
// where: {
|
||||
// id: args.id
|
||||
// },
|
||||
// limit: 1
|
||||
// })
|
||||
// },
|
||||
// renameFile(obj, args) {
|
||||
// return WIKI.models.File.update({
|
||||
// filename: args.filename
|
||||
// }, {
|
||||
// where: { id: args.id }
|
||||
// })
|
||||
// },
|
||||
// moveFile(obj, args) {
|
||||
// return WIKI.models.File.findById(args.fileId).then(fl => {
|
||||
// if (!fl) {
|
||||
// throw new gql.GraphQLError('Invalid File ID')
|
||||
// }
|
||||
// return WIKI.models.Folder.findById(args.folderId).then(fld => {
|
||||
// if (!fld) {
|
||||
// throw new gql.GraphQLError('Invalid Folder ID')
|
||||
// }
|
||||
// return fl.setFolder(fld)
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
}
|
||||
// File: {
|
||||
// folder(fl) {
|
||||
// return fl.getFolder()
|
||||
// }
|
||||
// }
|
||||
}
|
@@ -1,51 +0,0 @@
|
||||
|
||||
/* global WIKI */
|
||||
|
||||
const gql = require('graphql')
|
||||
|
||||
module.exports = {
|
||||
// Query: {
|
||||
// files(obj, args, context, info) {
|
||||
// return WIKI.models.File.findAll({ where: args })
|
||||
// }
|
||||
// },
|
||||
// Mutation: {
|
||||
// uploadFile(obj, args) {
|
||||
// // todo
|
||||
// return WIKI.models.File.create(args)
|
||||
// },
|
||||
// deleteFile(obj, args) {
|
||||
// return WIKI.models.File.destroy({
|
||||
// where: {
|
||||
// id: args.id
|
||||
// },
|
||||
// limit: 1
|
||||
// })
|
||||
// },
|
||||
// renameFile(obj, args) {
|
||||
// return WIKI.models.File.update({
|
||||
// filename: args.filename
|
||||
// }, {
|
||||
// where: { id: args.id }
|
||||
// })
|
||||
// },
|
||||
// moveFile(obj, args) {
|
||||
// return WIKI.models.File.findById(args.fileId).then(fl => {
|
||||
// if (!fl) {
|
||||
// throw new gql.GraphQLError('Invalid File ID')
|
||||
// }
|
||||
// return WIKI.models.Folder.findById(args.folderId).then(fld => {
|
||||
// if (!fld) {
|
||||
// throw new gql.GraphQLError('Invalid Folder ID')
|
||||
// }
|
||||
// return fl.setFolder(fld)
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
// },
|
||||
// File: {
|
||||
// folder(fl) {
|
||||
// return fl.getFolder()
|
||||
// }
|
||||
// }
|
||||
}
|
@@ -17,8 +17,8 @@ extend type Mutation {
|
||||
type AssetQuery {
|
||||
list(
|
||||
root: String
|
||||
kind: [AssetKind]
|
||||
): [AssetItem]
|
||||
kind: AssetKind
|
||||
): [AssetItem] @auth(requires: ["manage:system", "read:assets"])
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
@@ -37,6 +37,20 @@ type AssetMutation {
|
||||
|
||||
type AssetItem {
|
||||
id: Int!
|
||||
filename: String!
|
||||
ext: String!
|
||||
kind: AssetKind!
|
||||
mime: String!
|
||||
fileSize: Int!
|
||||
metadata: String
|
||||
createdAt: Date!
|
||||
updatedAt: Date!
|
||||
folder: AssetFolder
|
||||
author: User
|
||||
}
|
||||
|
||||
type AssetFolder {
|
||||
id: Int!
|
||||
}
|
||||
|
||||
enum AssetKind {
|
||||
|
12
server/helpers/asset.js
Normal file
12
server/helpers/asset.js
Normal file
@@ -0,0 +1,12 @@
|
||||
const crypto = require('crypto')
|
||||
|
||||
/* global WIKI */
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* Generate unique hash from page
|
||||
*/
|
||||
generateHash(assetPath) {
|
||||
return crypto.createHash('sha1').update(assetPath).digest('hex')
|
||||
}
|
||||
}
|
@@ -3,7 +3,6 @@ const _ = require('lodash')
|
||||
const crypto = require('crypto')
|
||||
|
||||
const localeSegmentRegex = /^[A-Z]{2}(-[A-Z]{2})?$/i
|
||||
const systemSegmentRegex = /^[A-Z]\//i
|
||||
|
||||
/* global WIKI */
|
||||
|
||||
@@ -67,8 +66,17 @@ module.exports = {
|
||||
*/
|
||||
isReservedPath(rawPath) {
|
||||
const firstSection = _.head(rawPath.split('/'))
|
||||
return _.some(WIKI.data.reservedPaths, p => {
|
||||
return p === firstSection || systemSegmentRegex.test(rawPath)
|
||||
})
|
||||
if (firstSection.length === 1) {
|
||||
return true
|
||||
} else if (localeSegmentRegex.test(firstSection)) {
|
||||
return true
|
||||
} else if (
|
||||
_.some(WIKI.data.reservedPaths, p => {
|
||||
return p === firstSection
|
||||
})) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -140,7 +140,6 @@ module.exports = async () => {
|
||||
path: '/graphql-subscriptions'
|
||||
}
|
||||
})
|
||||
app.use('/graphql', mw.upload)
|
||||
apolloServer.applyMiddleware({ app })
|
||||
|
||||
// ----------------------------------------
|
||||
@@ -148,6 +147,7 @@ module.exports = async () => {
|
||||
// ----------------------------------------
|
||||
|
||||
app.use('/', ctrl.auth)
|
||||
app.use('/', ctrl.upload)
|
||||
app.use('/', ctrl.common)
|
||||
|
||||
// ----------------------------------------
|
||||
|
35
server/models/assetFolders.js
Normal file
35
server/models/assetFolders.js
Normal file
@@ -0,0 +1,35 @@
|
||||
/* global WIKI */
|
||||
|
||||
const Model = require('objection').Model
|
||||
|
||||
/**
|
||||
* Users model
|
||||
*/
|
||||
module.exports = class AssetFolder extends Model {
|
||||
static get tableName() { return 'assetFolders' }
|
||||
|
||||
static get jsonSchema () {
|
||||
return {
|
||||
type: 'object',
|
||||
|
||||
properties: {
|
||||
id: {type: 'integer'},
|
||||
name: {type: 'string'},
|
||||
slug: {type: 'string'}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static get relationMappings() {
|
||||
return {
|
||||
parent: {
|
||||
relation: Model.BelongsToOneRelation,
|
||||
modelClass: AssetFolder,
|
||||
join: {
|
||||
from: 'assetFolders.folderId',
|
||||
to: 'assetFolders.id'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
97
server/models/assets.js
Normal file
97
server/models/assets.js
Normal file
@@ -0,0 +1,97 @@
|
||||
/* global WIKI */
|
||||
|
||||
const Model = require('objection').Model
|
||||
const moment = require('moment')
|
||||
const path = require('path')
|
||||
const fs = require('fs-extra')
|
||||
const _ = require('lodash')
|
||||
const assetHelper = require('../helpers/asset')
|
||||
|
||||
/**
|
||||
* Users model
|
||||
*/
|
||||
module.exports = class Asset extends Model {
|
||||
static get tableName() { return 'assets' }
|
||||
|
||||
static get jsonSchema () {
|
||||
return {
|
||||
type: 'object',
|
||||
|
||||
properties: {
|
||||
id: {type: 'integer'},
|
||||
filename: {type: 'string'},
|
||||
hash: {type: 'string'},
|
||||
ext: {type: 'string'},
|
||||
kind: {type: 'string'},
|
||||
mime: {type: 'string'},
|
||||
fileSize: {type: 'integer'},
|
||||
metadata: {type: 'object'},
|
||||
createdAt: {type: 'string'},
|
||||
updatedAt: {type: 'string'}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static get relationMappings() {
|
||||
return {
|
||||
author: {
|
||||
relation: Model.BelongsToOneRelation,
|
||||
modelClass: require('./users'),
|
||||
join: {
|
||||
from: 'assets.authorId',
|
||||
to: 'users.id'
|
||||
}
|
||||
},
|
||||
folder: {
|
||||
relation: Model.BelongsToOneRelation,
|
||||
modelClass: require('./assetFolders'),
|
||||
join: {
|
||||
from: 'assets.folderId',
|
||||
to: 'assetFolders.id'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async $beforeUpdate(opt, context) {
|
||||
await super.$beforeUpdate(opt, context)
|
||||
|
||||
this.updatedAt = moment.utc().toISOString()
|
||||
}
|
||||
async $beforeInsert(context) {
|
||||
await super.$beforeInsert(context)
|
||||
|
||||
this.createdAt = moment.utc().toISOString()
|
||||
this.updatedAt = moment.utc().toISOString()
|
||||
}
|
||||
|
||||
static async upload(opts) {
|
||||
const fileInfo = path.parse(opts.originalname)
|
||||
const fileHash = assetHelper.generateHash(`${opts.folder}/${opts.originalname}`)
|
||||
|
||||
// Create asset entry
|
||||
const asset = await WIKI.models.assets.query().insert({
|
||||
filename: opts.originalname,
|
||||
hash: fileHash,
|
||||
ext: fileInfo.ext,
|
||||
kind: _.startsWith(opts.mimetype, 'image/') ? 'image' : 'binary',
|
||||
mime: opts.mimetype,
|
||||
fileSize: opts.size,
|
||||
authorId: opts.userId
|
||||
})
|
||||
|
||||
// Save asset data
|
||||
try {
|
||||
const fileBuffer = await fs.readFile(opts.path)
|
||||
await WIKI.models.knex('assetData').insert({
|
||||
id: asset.id,
|
||||
data: fileBuffer
|
||||
})
|
||||
} catch (err) {
|
||||
WIKI.logger.warn(err)
|
||||
}
|
||||
|
||||
// Move temp upload to cache
|
||||
await fs.move(opts.path, path.join(process.cwd(), `data/cache/${fileHash}.dat`))
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user