diff --git a/server/controllers/project.controller.js b/server/controllers/project.controller.js index 88b990a3..15e9b3ee 100644 --- a/server/controllers/project.controller.js +++ b/server/controllers/project.controller.js @@ -4,6 +4,7 @@ import moment from 'moment'; import isUrl from 'is-url'; import slugify from 'slugify'; import jsdom, { serializeDocument } from 'jsdom'; +import generateFileSystemSafeName from '../utils/generateFileSystemSafeName'; import Project from '../models/project'; import User from '../models/user'; import { deleteObjectsFromS3, getObjectKey } from './aws.controller'; @@ -295,7 +296,7 @@ function buildZip(project, req, res) { const currentTime = moment().format('YYYY_MM_DD_HH_mm_ss'); project.slug = slugify(project.name, '_'); - res.attachment(`${project.slug}_${currentTime}.zip`); + res.attachment(`${generateFileSystemSafeName(project.slug)}_${currentTime}.zip`); zip.pipe(res); function addFileToZip(file, path) { diff --git a/server/models/project.js b/server/models/project.js index c1a255d8..e3c22bdb 100644 --- a/server/models/project.js +++ b/server/models/project.js @@ -4,14 +4,17 @@ import slugify from 'slugify'; const { Schema } = mongoose; -const fileSchema = new Schema({ - name: { type: String, default: 'sketch.js' }, - content: { type: String, default: '' }, - url: { type: String }, - children: { type: [String], default: [] }, - fileType: { type: String, default: 'file' }, - isSelectedFile: { type: Boolean } -}, { timestamps: true, _id: true, usePushEach: true }); +const fileSchema = new Schema( + { + name: { type: String, default: 'sketch.js' }, + content: { type: String, default: '' }, + url: { type: String }, + children: { type: [String], default: [] }, + fileType: { type: String, default: 'file' }, + isSelectedFile: { type: Boolean } + }, + { timestamps: true, _id: true, usePushEach: true } +); fileSchema.virtual('id').get(function getFileId() { return this._id.toHexString(); @@ -21,14 +24,17 @@ fileSchema.set('toJSON', { virtuals: true }); -const projectSchema = new Schema({ - name: { type: String, default: "Hello p5.js, it's the server" }, - user: { type: Schema.Types.ObjectId, ref: 'User' }, - serveSecure: { type: Boolean, default: false }, - files: { type: [fileSchema] }, - _id: { type: String, default: shortid.generate }, - slug: { type: String } -}, { timestamps: true, usePushEach: true }); +const projectSchema = new Schema( + { + name: { type: String, default: "Hello p5.js, it's the server" }, + user: { type: Schema.Types.ObjectId, ref: 'User' }, + serveSecure: { type: Boolean, default: false }, + files: { type: [fileSchema] }, + _id: { type: String, default: shortid.generate }, + slug: { type: String } + }, + { timestamps: true, usePushEach: true } +); projectSchema.virtual('id').get(function getProjectId() { return this._id; diff --git a/server/utils/generateFileSystemSafeName.js b/server/utils/generateFileSystemSafeName.js new file mode 100644 index 00000000..db835979 --- /dev/null +++ b/server/utils/generateFileSystemSafeName.js @@ -0,0 +1,16 @@ +/** + * generate file system safe string for a given string + * that can be used as a valid file name + * in all operating systems + * @param {String} string + * @param {String} replacer (optional) character to replace invalid characters + */ +function generateFileSystemSafeName(string, replacer) { + // from here https://serverfault.com/a/242134 + const INVALID_CHARS_REGEX = /[*/?:\\<>|"\u0000-\u001F]/g; // eslint-disable-line + const slug = string.replace(INVALID_CHARS_REGEX, replacer || ''); + + return slug; +} + +export default generateFileSystemSafeName;