Skip to content
Snippets Groups Projects
Commit 4c4babb0 authored by Louis's avatar Louis :fire:
Browse files

Support file uploads to google cloud

parent c4766c54
No related branches found
No related tags found
No related merge requests found
......@@ -3,4 +3,5 @@ node_modules
*.log
.env
*.iml
**/dist/
\ No newline at end of file
**/dist/
google-storage.json
\ No newline at end of file
......@@ -7,6 +7,14 @@ module.exports = {
defaultValue: Types.UUIDV4,
allowNull: false,
},
user_id: {
type: Types.UUID,
allowNull: true,
references: {
model: 'users',
key: 'id',
},
},
provider: {
type: Types.TEXT,
allowNull: false,
......
......@@ -12,11 +12,13 @@ CREATE TABLE IF NOT EXISTS files_search_index (
await migration.sequelize.query(`
CREATE OR REPLACE FUNCTION update_files_search_index() RETURNS TRIGGER AS $FUNC$
DECLARE
indexed_text TEXT := to_tsvector(concat(NEW.comment, array_to_string(NEW.tags, ' ', ''), array_to_string(NEW.labels, ' ', '')));
indexed_text TSVECTOR := to_tsvector(concat(NEW.comment, array_to_string(NEW.tags, ' ', ''), array_to_string(NEW.labels, ' ', '')));
BEGIN
INSERT INTO files_search_index(file_id, search_index)
VALUES (NEW.id, to_tsvector(concat(NEW.comment, indexed_text)))
ON CONFLICT (file_id) DO UPDATE SET search_index = indexed_text;
RETURN NEW;
END;
$FUNC$ LANGUAGE plpgsql;`, { transaction: t })
......
const timestamps = require('./properties/timestamps')
function generatePublicUri(provider, file) {
return ['https://storage.googleapis.com', file.meta.bucket, file.file_root, file.file_name].join('/')
}
module.exports = (sequelize, DataTypes) => {
const Model = sequelize.define('File', Object.assign(
{
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
},
provider: {
type: DataTypes.TEXT,
},
file_root: {
type: DataTypes.TEXT,
},
file_name: {
type: DataTypes.TEXT,
},
comment: {
type: DataTypes.TEXT,
},
tags: {
type: DataTypes.ARRAY(DataTypes.TEXT),
},
labels: {
type: DataTypes.ARRAY(DataTypes.TEXT),
},
public_uri: {
type: DataTypes.VIRTUAL,
get() {
const provider = this.provider
return generatePublicUri(provider, this)
}
},
stream: {
type: DataTypes.TEXT,
},
requires_approval: {
type: DataTypes.BOOLEAN,
},
approved: {
type: DataTypes.BOOLEAN,
},
featured: {
type: DataTypes.BOOLEAN,
},
meta: {
type: DataTypes.JSONB,
},
},
timestamps(DataTypes),
), {
paranoid: true,
tableName: 'files',
})
Model.getPolyIdentifier = () => 'file'
Model.getRelationIdentifier = () => 'files'
Model.prototype.toJSON = function userToJSON() {
return {
id: this.id,
uri: this.public_uri,
name: this.file_name,
comment: this.comment,
tags: this.tags,
labels: this.labels,
requires_approval: this.requires_approval,
approved: this.approved,
meta: this.meta,
created_at: this.created_at,
updated_at: this.updated_at,
}
}
Model.prototype.handleIncludes = async function(includes, loaders = null) {
}
Model.associate = function defineModelAssociations(models) {
Model.belongsTo(models.User, { foreignKey: 'user_id' })
}
Model.relations = [
]
return Model
}
......@@ -123,6 +123,7 @@ module.exports = (sequelize, DataTypes) => {
Model.hasMany(models.OAuthClient, { foreignKey: 'owner_id' })
Model.hasMany(models.AccessToken, { foreignKey: 'user_id' })
Model.hasMany(models.RefreshToken, { foreignKey: 'user_id' })
Model.hasMany(models.File, { foreignKey: 'user_id' })
}
Model.relations = [
......
const storage = require('@google-cloud/storage')
const { sequelize, File } = require('database/models')
const { Storage } = require('@google-cloud/storage')
const { sequelize, File, User } = require('database/models')
const HttpError = require('core/errors/HttpError')
const mimeType = require('mime-types')
const uuid = require('uuid/v4')
const { fs, env } = require('bootstrap')
const storage = new Storage({ keyFilename: fs.path('google-storage.json') })
exports.saveFile = async ctx => {
if (ctx.file == null) {
throw new HttpError({ description: 'No featured image provided' })
}
const user = await ctx.services.authService.getUser()
if (!user) {
throw new HttpError({ status: 403, title: 'Must be signed in to feature an image', description: 'You must be signed in to submit images to the content feed' })
}
const { file } = ctx
const { tags, title } = ctx.request.body
const { tags = [], title } = ctx.request.body
const fileinfo = {
meta: {
mimetype: file.mimetype,
size: file.size,
bucket: env('GCS_BUCKET'),
},
file_name: `${ uuid() }.${ mimeType.extension(file.mimetype) }`,
file_root: (await ctx.services.authService.getUser()).id
file_root: `${ user.id }`,
user_id: user.id,
stream: 'plastics-public',
provider: 'google',
comment: title,
tags,
approved: true,
featured: true,
}
console.log(fileinfo)
const persistedFile = await File.create(fileinfo)
const storagePath = `${ fileinfo.file_root }/${ fileinfo.file_name }`
const bucket = storage.bucket(env('GCS_BUCKET'))
ctx.body = fileinfo
const uploadOpts = {
destination: storagePath,
public: true,
metadata: {
contentType: file.mimetype,
metadata: {
tags,
mimetype: file.mimetype,
uploader: user.id,
}
}
}
const uploadedFile = await bucket.upload(file.path, uploadOpts)
// await bucket.file(storagePath).makePublic()
console.log(uploadedFile)
ctx.body = {
file: persistedFile,
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment