diff --git a/client.js b/client.js
index cf9c9085..d45981fa 100644
--- a/client.js
+++ b/client.js
@@ -1,5 +1,6 @@
import { Mongo } from 'meteor/mongo';
import { Meteor } from 'meteor/meteor';
+import { DDP } from 'meteor/ddp-client';
import { Cookies } from 'meteor/ostrio:cookies';
import { check, Match } from 'meteor/check';
import { UploadInstance } from './upload.js';
@@ -113,29 +114,20 @@ export class FilesCollection extends FilesCollectionCore {
}
const setTokenCookie = () => {
- if ((!cookie.has('x_mtok') && Meteor.connection._lastSessionId) || (cookie.has('x_mtok') && (cookie.get('x_mtok') !== Meteor.connection._lastSessionId))) {
- cookie.set('x_mtok', Meteor.connection._lastSessionId, {
- path: '/'
- });
- }
- };
-
- const unsetTokenCookie = () => {
- if (cookie.has('x_mtok')) {
- cookie.remove('x_mtok', '/');
+ if (Meteor.connection._lastSessionId) {
+ cookie.set('x_mtok', Meteor.connection._lastSessionId, { path: '/' });
+ if (Meteor.isCordova) {
+ cookie.send();
+ }
}
};
if (typeof Accounts !== 'undefined' && Accounts !== null) {
- Meteor.startup(() => {
- setTokenCookie();
- });
- Accounts.onLogin(() => {
- setTokenCookie();
- });
- Accounts.onLogout(() => {
- unsetTokenCookie();
+ DDP.onReconnect((conn) => {
+ conn.onReconnect = setTokenCookie;
});
+ Meteor.startup(setTokenCookie);
+ Accounts.onLogin(setTokenCookie);
}
check(this.onbeforeunloadMessage, Match.OneOf(String, Function));
diff --git a/docs/constructor.md b/docs/constructor.md
index 6547c08f..c194695c 100644
--- a/docs/constructor.md
+++ b/docs/constructor.md
@@ -726,7 +726,24 @@
Use for security reasons when only upload from Client to Server usage is needed, and files shouldn't be downloaded by any user.
-
+
+
+ config.allowedOrigins {Regex|Boolean}
+ |
+
+ Server
+ |
+
+ Regex of Origins that are allowed CORS access or `false` to disable completely.
+ |
+
+ /^http:\/\/localhost:12\d\d\d$/
+ |
+
+ Defaults to `localhost:12000`-`localhost:13000` for allowing Meteor-Cordova builds access.
+ |
+
+
config._preCollection {Mongo.Collection}
|
diff --git a/package.js b/package.js
index e71e0903..f7c9e1fd 100755
--- a/package.js
+++ b/package.js
@@ -16,7 +16,7 @@ Npm.depends({
Package.onUse(function(api) {
api.versionsFrom('1.6.1');
api.use('webapp', 'server');
- api.use(['reactive-var', 'tracker', 'http'], 'client');
+ api.use(['reactive-var', 'tracker', 'http', 'ddp-client'], 'client');
api.use(['mongo', 'check', 'random', 'ecmascript', 'ostrio:cookies@2.3.0'], ['client', 'server']);
api.addAssets('worker.min.js', 'client');
api.mainModule('server.js', 'server');
diff --git a/server.js b/server.js
index de00adee..8b218bad 100644
--- a/server.js
+++ b/server.js
@@ -58,7 +58,8 @@ const NOOP = () => { };
* @param config.downloadCallback {Function} - [Server] Callback triggered each time file is requested, return truthy value to continue download, or falsy to abort
* @param config.interceptDownload {Function} - [Server] Intercept download request, so you can serve file from third-party resource, arguments {http: {request: {...}, response: {...}}, fileRef: {...}}
* @param config.disableUpload {Boolean} - Disable file upload, useful for server only solutions
- * @param config.disableDownload {Boolean} - Disable file download (serving), useful for file management only solutions
+ * @param config.disableDownload {Boolean} - Disable file download (serving), useful for file management only solutions
+ * @param config.allowedOrigins {Regex|Boolean} - [Server] Regex of Origins that are allowed CORS access or `false` to disable completely. Defaults to `localhost:12000`-`localhost:13000` for allowing Meteor-Cordova builds access.
* @param config._preCollection {Mongo.Collection} - [Server] Mongo preCollection Instance
* @param config._preCollectionName {String} - [Server] preCollection name
* @summary Create new instance of FilesCollection
@@ -90,6 +91,7 @@ export class FilesCollection extends FilesCollectionCore {
namingFunction: this.namingFunction,
responseHeaders: this.responseHeaders,
disableDownload: this.disableDownload,
+ allowedOrigins: this.allowedOrigins,
allowClientCode: this.allowClientCode,
downloadCallback: this.downloadCallback,
onInitiateUpload: this.onInitiateUpload,
@@ -205,6 +207,10 @@ export class FilesCollection extends FilesCollectionCore {
this.disableDownload = false;
}
+ if (!this.allowedOrigins) {
+ this.allowedOrigins = /^http:\/\/localhost:12\d\d\d$/;
+ }
+
if (!helpers.isObject(this._currentUploads)) {
this._currentUploads = {};
}
@@ -435,6 +441,23 @@ export class FilesCollection extends FilesCollectionCore {
return;
}
WebApp.connectHandlers.use((httpReq, httpResp, next) => {
+ if (this.allowedOrigins && !!~httpReq._parsedUrl.path.indexOf(`${this.downloadRoute}/`) && !httpResp.headersSent) {
+ if (this.allowedOrigins.test(httpReq.headers.origin)) {
+ httpResp.setHeader('Access-Control-Allow-Credentials', 'true');
+ httpResp.setHeader('Access-Control-Allow-Origin', httpReq.headers.origin);
+ }
+
+ if (httpReq.method === 'OPTIONS') {
+ httpResp.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
+ httpResp.setHeader('Access-Control-Allow-Headers', 'Range, Content-Type, x-mtok, x-start, x-chunkid, x-fileid, x-eof');
+ httpResp.setHeader('Access-Control-Expose-Headers', 'Accept-Ranges, Content-Encoding, Content-Length, Content-Range');
+ httpResp.setHeader('Allow', 'GET, POST, OPTIONS');
+ httpResp.writeHead(200);
+ httpResp.end();
+ return;
+ }
+ }
+
if (!this.disableUpload && !!~httpReq._parsedUrl.path.indexOf(`${this.downloadRoute}/${this.collectionName}/__upload`)) {
if (httpReq.method === 'POST') {
const handleError = (_error) => {
diff --git a/upload.js b/upload.js
index ff472b3e..1c92f8fb 100644
--- a/upload.js
+++ b/upload.js
@@ -377,6 +377,10 @@ export class UploadInstance extends EventEmitter {
'x-fileid': opts.fileId,
'x-chunkid': opts.chunkId,
'content-type': 'text/plain'
+ },
+ beforeSend(xhr) {
+ xhr.withCredentials = true;
+ return true;
}
}, (error) => {
this.transferTime += +new Date() - this.startTime[opts.chunkId];
@@ -423,6 +427,10 @@ export class UploadInstance extends EventEmitter {
'x-mtok': (helpers.isObject(Meteor.connection) ? Meteor.connection._lastSessionId : void 0) || null,
'x-fileId': opts.fileId,
'content-type': 'text/plain'
+ },
+ beforeSend(xhr) {
+ xhr.withCredentials = true;
+ return true;
}
}, (error, _result) => {
let result;
@@ -607,6 +615,10 @@ export class UploadInstance extends EventEmitter {
headers: {
'x-start': '1',
'x-mtok': (helpers.isObject(Meteor.connection) ? Meteor.connection._lastSessionId : void 0) || null
+ },
+ beforeSend(xhr) {
+ xhr.withCredentials = true;
+ return true;
}
}, handleStart);
}