0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-03-11 02:12:21 -05:00

Merge branch 'master' into v3

This commit is contained in:
Kevin Ansfield 2019-09-10 09:50:15 +01:00
commit 1752132051
25 changed files with 671 additions and 1570 deletions

@ -1 +1 @@
Subproject commit eb4ff95383bca56407d3eff41d70b22e645bc370
Subproject commit a8ea51b3ad0c099aa02ef790b80c6a26056acbef

View file

@ -2,14 +2,19 @@
// Usage: `{{asset "css/screen.css"}}`
//
// Returns the path to the specified asset.
const proxy = require('./proxy');
const {SafeString, metaData, errors, i18n} = require('./proxy');
const get = require('lodash/get');
const {SafeString} = proxy;
const {getAssetUrl} = proxy.metaData;
const {getAssetUrl} = metaData;
module.exports = function asset(path, options) {
const hasMinFile = get(options, 'hash.hasMinFile');
if (!path) {
throw new errors.IncorrectUsageError({
message: i18n.t('warnings.helpers.asset.pathIsRequired')
});
}
return new SafeString(
getAssetUrl(path, hasMinFile)
);

View file

@ -57,7 +57,7 @@ function getAjaxHelper(clientId, clientSecret) {
function getMembersHelper() {
return `
<script src="${getAssetUrl('public/members-theme-bindings.js')}"></script>
<script src="https://js.stripe.com/v3/"></script>
<script defer src="${getAssetUrl('public/members.js')}"></script>
`;
}

View file

@ -123,6 +123,14 @@ class LocalFileStore extends StorageBase {
}));
}
if (err.statusCode === 400) {
return next(new common.errors.BadRequestError({err: err}));
}
if (err.statusCode === 403) {
return next(new common.errors.NoPermissionError({err: err}));
}
return next(new common.errors.GhostError({err: err}));
}

View file

@ -1,6 +1,7 @@
// NOTE: We must not cache references to membersService.api
// as it is a getter and may change during runtime.
const membersService = require('../../services/members');
const common = require('../../lib/common');
const members = {
docName: 'members',
@ -28,8 +29,14 @@ const members = {
],
validation: {},
permissions: true,
query(frame) {
return membersService.api.members.get(frame.data, frame.options);
async query(frame) {
const member = await membersService.api.members.get(frame.data, frame.options);
if (!member) {
throw new common.errors.NotFoundError({
message: common.i18n.t('errors.api.members.memberNotFound')
});
}
return member;
}
},

View file

@ -1,6 +1,7 @@
// NOTE: We must not cache references to membersService.api
// as it is a getter and may change during runtime.
const membersService = require('../../services/members');
const common = require('../../lib/common');
const members = {
docName: 'members',
@ -28,8 +29,14 @@ const members = {
],
validation: {},
permissions: true,
query(frame) {
return membersService.api.members.get(frame.data, frame.options);
async query(frame) {
const member = await membersService.api.members.get(frame.data, frame.options);
if (!member) {
throw new common.errors.NotFoundError({
message: common.i18n.t('errors.api.members.memberNotFound')
});
}
return member;
}
},

View file

@ -0,0 +1,55 @@
const commands = require('../../../schema').commands;
module.exports = {
up: commands.createColumnMigration({
table: 'members',
column: 'password',
dbIsInCorrectState(hasColumn) {
return hasColumn === false;
},
operation: commands.dropColumn,
operationVerb: 'Dropping'
}, {
table: 'members',
column: 'name',
dbIsInCorrectState(hasColumn) {
return hasColumn === false;
},
operation: commands.dropColumn,
operationVerb: 'Dropping'
}),
down: commands.createColumnMigration({
table: 'members',
column: 'password',
dbIsInCorrectState(hasColumn) {
return hasColumn === true;
},
operation: commands.addColumn,
operationVerb: 'Adding',
columnDefinition: {
type: 'string',
maxlength: 60,
nullable: true
}
}, {
table: 'members',
column: 'name',
dbIsInCorrectState(hasColumn) {
return hasColumn === true;
},
operation: commands.addColumn,
operationVerb: 'Adding',
columnDefinition: {
type: 'string',
maxlength: 191,
nullable: false,
defaultTo: ''
}
}),
config: {
transaction: true
}
};

View file

@ -133,6 +133,40 @@ function checkTables(transaction) {
}
}
const createLog = type => msg => common.logging[type](msg);
function createColumnMigration(...migrations) {
async function runColumnMigration(conn, migration) {
const {
table,
column,
dbIsInCorrectState,
operation,
operationVerb,
columnDefinition
} = migration;
const hasColumn = await conn.schema.hasColumn(table, column);
const isInCorrectState = dbIsInCorrectState(hasColumn);
const log = createLog(isInCorrectState ? 'info' : 'warn');
log(`${operationVerb} ${table}.${column}`);
if (!isInCorrectState) {
await operation(table, column, conn, columnDefinition);
}
}
return async function columnMigration(options) {
const conn = options.transacting || options.connection;
for (const migration of migrations) {
await runColumnMigration(conn, migration);
}
};
}
module.exports = {
checkTables: checkTables,
createTable: createTable,
@ -143,5 +177,6 @@ module.exports = {
dropUnique: dropUnique,
addColumn: addColumn,
dropColumn: dropColumn,
getColumns: getColumns
getColumns: getColumns,
createColumnMigration
};

View file

@ -383,8 +383,6 @@ module.exports = {
members: {
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
email: {type: 'string', maxlength: 191, nullable: false, unique: true, validations: {isEmail: true}},
name: {type: 'string', maxlength: 191, nullable: false},
password: {type: 'string', maxlength: 60, nullable: true},
created_at: {type: 'dateTime', nullable: false},
created_by: {type: 'string', maxlength: 24, nullable: false},
updated_at: {type: 'dateTime', nullable: true},

View file

@ -1,896 +0,0 @@
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.MembersThemeBindings = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
"use strict";
var root = require('./_root');
var Symbol = root.Symbol;
module.exports = Symbol;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/_Symbol.js
},{"./_root":24}],2:[function(require,module,exports){
"use strict";
function arrayEach(array, iteratee) {
var index = -1,
length = array == null ? 0 : array.length;
while (++index < length) {
if (iteratee(array[index], index, array) === false) {
break;
}
}
return array;
}
module.exports = arrayEach;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/_arrayEach.js
},{}],3:[function(require,module,exports){
"use strict";
var baseTimes = require('./_baseTimes'),
isArguments = require('./isArguments'),
isArray = require('./isArray'),
isBuffer = require('./isBuffer'),
isIndex = require('./_isIndex'),
isTypedArray = require('./isTypedArray');
var objectProto = Object.prototype;
var hasOwnProperty = objectProto.hasOwnProperty;
function arrayLikeKeys(value, inherited) {
var isArr = isArray(value),
isArg = !isArr && isArguments(value),
isBuff = !isArr && !isArg && isBuffer(value),
isType = !isArr && !isArg && !isBuff && isTypedArray(value),
skipIndexes = isArr || isArg || isBuff || isType,
result = skipIndexes ? baseTimes(value.length, String) : [],
length = result.length;
for (var key in value) {
if ((inherited || hasOwnProperty.call(value, key)) && !(skipIndexes && (key == 'length' || (isBuff && (key == 'offset' || key == 'parent')) || (isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) || isIndex(key, length)))) {
result.push(key);
}
}
return result;
}
module.exports = arrayLikeKeys;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/_arrayLikeKeys.js
},{"./_baseTimes":11,"./_isIndex":18,"./isArguments":28,"./isArray":29,"./isBuffer":31,"./isTypedArray":36}],4:[function(require,module,exports){
"use strict";
var baseForOwn = require('./_baseForOwn'),
createBaseEach = require('./_createBaseEach');
var baseEach = createBaseEach(baseForOwn);
module.exports = baseEach;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/_baseEach.js
},{"./_baseForOwn":6,"./_createBaseEach":14}],5:[function(require,module,exports){
"use strict";
var createBaseFor = require('./_createBaseFor');
var baseFor = createBaseFor();
module.exports = baseFor;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/_baseFor.js
},{"./_createBaseFor":15}],6:[function(require,module,exports){
"use strict";
var baseFor = require('./_baseFor'),
keys = require('./keys');
function baseForOwn(object, iteratee) {
return object && baseFor(object, iteratee, keys);
}
module.exports = baseForOwn;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/_baseForOwn.js
},{"./_baseFor":5,"./keys":37}],7:[function(require,module,exports){
"use strict";
var Symbol = require('./_Symbol'),
getRawTag = require('./_getRawTag'),
objectToString = require('./_objectToString');
var nullTag = '[object Null]',
undefinedTag = '[object Undefined]';
var symToStringTag = Symbol ? Symbol.toStringTag : undefined;
function baseGetTag(value) {
if (value == null) {
return value === undefined ? undefinedTag : nullTag;
}
return (symToStringTag && symToStringTag in Object(value)) ? getRawTag(value) : objectToString(value);
}
module.exports = baseGetTag;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/_baseGetTag.js
},{"./_Symbol":1,"./_getRawTag":17,"./_objectToString":22}],8:[function(require,module,exports){
"use strict";
var baseGetTag = require('./_baseGetTag'),
isObjectLike = require('./isObjectLike');
var argsTag = '[object Arguments]';
function baseIsArguments(value) {
return isObjectLike(value) && baseGetTag(value) == argsTag;
}
module.exports = baseIsArguments;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/_baseIsArguments.js
},{"./_baseGetTag":7,"./isObjectLike":35}],9:[function(require,module,exports){
"use strict";
var baseGetTag = require('./_baseGetTag'),
isLength = require('./isLength'),
isObjectLike = require('./isObjectLike');
var argsTag = '[object Arguments]',
arrayTag = '[object Array]',
boolTag = '[object Boolean]',
dateTag = '[object Date]',
errorTag = '[object Error]',
funcTag = '[object Function]',
mapTag = '[object Map]',
numberTag = '[object Number]',
objectTag = '[object Object]',
regexpTag = '[object RegExp]',
setTag = '[object Set]',
stringTag = '[object String]',
weakMapTag = '[object WeakMap]';
var arrayBufferTag = '[object ArrayBuffer]',
dataViewTag = '[object DataView]',
float32Tag = '[object Float32Array]',
float64Tag = '[object Float64Array]',
int8Tag = '[object Int8Array]',
int16Tag = '[object Int16Array]',
int32Tag = '[object Int32Array]',
uint8Tag = '[object Uint8Array]',
uint8ClampedTag = '[object Uint8ClampedArray]',
uint16Tag = '[object Uint16Array]',
uint32Tag = '[object Uint32Array]';
var typedArrayTags = {};
typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = typedArrayTags[uint32Tag] = true;
typedArrayTags[argsTag] = typedArrayTags[arrayTag] = typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = typedArrayTags[dataViewTag] = typedArrayTags[dateTag] = typedArrayTags[errorTag] = typedArrayTags[funcTag] = typedArrayTags[mapTag] = typedArrayTags[numberTag] = typedArrayTags[objectTag] = typedArrayTags[regexpTag] = typedArrayTags[setTag] = typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false;
function baseIsTypedArray(value) {
return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[baseGetTag(value)];
}
module.exports = baseIsTypedArray;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/_baseIsTypedArray.js
},{"./_baseGetTag":7,"./isLength":33,"./isObjectLike":35}],10:[function(require,module,exports){
"use strict";
var isPrototype = require('./_isPrototype'),
nativeKeys = require('./_nativeKeys');
var objectProto = Object.prototype;
var hasOwnProperty = objectProto.hasOwnProperty;
function baseKeys(object) {
if (!isPrototype(object)) {
return nativeKeys(object);
}
var result = [];
for (var key in Object(object)) {
if (hasOwnProperty.call(object, key) && key != 'constructor') {
result.push(key);
}
}
return result;
}
module.exports = baseKeys;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/_baseKeys.js
},{"./_isPrototype":19,"./_nativeKeys":20}],11:[function(require,module,exports){
"use strict";
function baseTimes(n, iteratee) {
var index = -1,
result = Array(n);
while (++index < n) {
result[index] = iteratee(index);
}
return result;
}
module.exports = baseTimes;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/_baseTimes.js
},{}],12:[function(require,module,exports){
"use strict";
function baseUnary(func) {
return function(value) {
return func(value);
};
}
module.exports = baseUnary;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/_baseUnary.js
},{}],13:[function(require,module,exports){
"use strict";
var identity = require('./identity');
function castFunction(value) {
return typeof value == 'function' ? value : identity;
}
module.exports = castFunction;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/_castFunction.js
},{"./identity":27}],14:[function(require,module,exports){
"use strict";
var isArrayLike = require('./isArrayLike');
function createBaseEach(eachFunc, fromRight) {
return function(collection, iteratee) {
if (collection == null) {
return collection;
}
if (!isArrayLike(collection)) {
return eachFunc(collection, iteratee);
}
var length = collection.length,
index = fromRight ? length : -1,
iterable = Object(collection);
while ((fromRight ? index-- : ++index < length)) {
if (iteratee(iterable[index], index, iterable) === false) {
break;
}
}
return collection;
};
}
module.exports = createBaseEach;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/_createBaseEach.js
},{"./isArrayLike":30}],15:[function(require,module,exports){
"use strict";
function createBaseFor(fromRight) {
return function(object, iteratee, keysFunc) {
var index = -1,
iterable = Object(object),
props = keysFunc(object),
length = props.length;
while (length--) {
var key = props[fromRight ? length : ++index];
if (iteratee(iterable[key], key, iterable) === false) {
break;
}
}
return object;
};
}
module.exports = createBaseFor;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/_createBaseFor.js
},{}],16:[function(require,module,exports){
(function (global){
"use strict";
var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
module.exports = freeGlobal;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/_freeGlobal.js
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],17:[function(require,module,exports){
"use strict";
var Symbol = require('./_Symbol');
var objectProto = Object.prototype;
var hasOwnProperty = objectProto.hasOwnProperty;
var nativeObjectToString = objectProto.toString;
var symToStringTag = Symbol ? Symbol.toStringTag : undefined;
function getRawTag(value) {
var isOwn = hasOwnProperty.call(value, symToStringTag),
tag = value[symToStringTag];
try {
value[symToStringTag] = undefined;
var unmasked = true;
} catch (e) {}
var result = nativeObjectToString.call(value);
if (unmasked) {
if (isOwn) {
value[symToStringTag] = tag;
} else {
delete value[symToStringTag];
}
}
return result;
}
module.exports = getRawTag;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/_getRawTag.js
},{"./_Symbol":1}],18:[function(require,module,exports){
"use strict";
var MAX_SAFE_INTEGER = 9007199254740991;
var reIsUint = /^(?:0|[1-9]\d*)$/;
function isIndex(value, length) {
var type = typeof value;
length = length == null ? MAX_SAFE_INTEGER : length;
return !!length && (type == 'number' || (type != 'symbol' && reIsUint.test(value))) && (value > -1 && value % 1 == 0 && value < length);
}
module.exports = isIndex;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/_isIndex.js
},{}],19:[function(require,module,exports){
"use strict";
var objectProto = Object.prototype;
function isPrototype(value) {
var Ctor = value && value.constructor,
proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto;
return value === proto;
}
module.exports = isPrototype;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/_isPrototype.js
},{}],20:[function(require,module,exports){
"use strict";
var overArg = require('./_overArg');
var nativeKeys = overArg(Object.keys, Object);
module.exports = nativeKeys;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/_nativeKeys.js
},{"./_overArg":23}],21:[function(require,module,exports){
"use strict";
var freeGlobal = require('./_freeGlobal');
var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;
var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;
var moduleExports = freeModule && freeModule.exports === freeExports;
var freeProcess = moduleExports && freeGlobal.process;
var nodeUtil = (function() {
try {
var types = freeModule && freeModule.require && freeModule.require('util').types;
if (types) {
return types;
}
return freeProcess && freeProcess.binding && freeProcess.binding('util');
} catch (e) {}
}());
module.exports = nodeUtil;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/_nodeUtil.js
},{"./_freeGlobal":16}],22:[function(require,module,exports){
"use strict";
var objectProto = Object.prototype;
var nativeObjectToString = objectProto.toString;
function objectToString(value) {
return nativeObjectToString.call(value);
}
module.exports = objectToString;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/_objectToString.js
},{}],23:[function(require,module,exports){
"use strict";
function overArg(func, transform) {
return function(arg) {
return func(transform(arg));
};
}
module.exports = overArg;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/_overArg.js
},{}],24:[function(require,module,exports){
"use strict";
var freeGlobal = require('./_freeGlobal');
var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
var root = freeGlobal || freeSelf || Function('return this')();
module.exports = root;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/_root.js
},{"./_freeGlobal":16}],25:[function(require,module,exports){
"use strict";
module.exports = require('./forEach');
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/each.js
},{"./forEach":26}],26:[function(require,module,exports){
"use strict";
var arrayEach = require('./_arrayEach'),
baseEach = require('./_baseEach'),
castFunction = require('./_castFunction'),
isArray = require('./isArray');
function forEach(collection, iteratee) {
var func = isArray(collection) ? arrayEach : baseEach;
return func(collection, castFunction(iteratee));
}
module.exports = forEach;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/forEach.js
},{"./_arrayEach":2,"./_baseEach":4,"./_castFunction":13,"./isArray":29}],27:[function(require,module,exports){
"use strict";
function identity(value) {
return value;
}
module.exports = identity;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/identity.js
},{}],28:[function(require,module,exports){
"use strict";
var baseIsArguments = require('./_baseIsArguments'),
isObjectLike = require('./isObjectLike');
var objectProto = Object.prototype;
var hasOwnProperty = objectProto.hasOwnProperty;
var propertyIsEnumerable = objectProto.propertyIsEnumerable;
var isArguments = baseIsArguments(function() {
return arguments;
}()) ? baseIsArguments : function(value) {
return isObjectLike(value) && hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee');
};
module.exports = isArguments;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/isArguments.js
},{"./_baseIsArguments":8,"./isObjectLike":35}],29:[function(require,module,exports){
"use strict";
var isArray = Array.isArray;
module.exports = isArray;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/isArray.js
},{}],30:[function(require,module,exports){
"use strict";
var isFunction = require('./isFunction'),
isLength = require('./isLength');
function isArrayLike(value) {
return value != null && isLength(value.length) && !isFunction(value);
}
module.exports = isArrayLike;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/isArrayLike.js
},{"./isFunction":32,"./isLength":33}],31:[function(require,module,exports){
"use strict";
var root = require('./_root'),
stubFalse = require('./stubFalse');
var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;
var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;
var moduleExports = freeModule && freeModule.exports === freeExports;
var Buffer = moduleExports ? root.Buffer : undefined;
var nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined;
var isBuffer = nativeIsBuffer || stubFalse;
module.exports = isBuffer;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/isBuffer.js
},{"./_root":24,"./stubFalse":38}],32:[function(require,module,exports){
"use strict";
var baseGetTag = require('./_baseGetTag'),
isObject = require('./isObject');
var asyncTag = '[object AsyncFunction]',
funcTag = '[object Function]',
genTag = '[object GeneratorFunction]',
proxyTag = '[object Proxy]';
function isFunction(value) {
if (!isObject(value)) {
return false;
}
var tag = baseGetTag(value);
return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag;
}
module.exports = isFunction;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/isFunction.js
},{"./_baseGetTag":7,"./isObject":34}],33:[function(require,module,exports){
"use strict";
var MAX_SAFE_INTEGER = 9007199254740991;
function isLength(value) {
return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
}
module.exports = isLength;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/isLength.js
},{}],34:[function(require,module,exports){
"use strict";
function isObject(value) {
var type = typeof value;
return value != null && (type == 'object' || type == 'function');
}
module.exports = isObject;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/isObject.js
},{}],35:[function(require,module,exports){
"use strict";
function isObjectLike(value) {
return value != null && typeof value == 'object';
}
module.exports = isObjectLike;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/isObjectLike.js
},{}],36:[function(require,module,exports){
"use strict";
var baseIsTypedArray = require('./_baseIsTypedArray'),
baseUnary = require('./_baseUnary'),
nodeUtil = require('./_nodeUtil');
var nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray;
var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray;
module.exports = isTypedArray;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/isTypedArray.js
},{"./_baseIsTypedArray":9,"./_baseUnary":12,"./_nodeUtil":21}],37:[function(require,module,exports){
"use strict";
var arrayLikeKeys = require('./_arrayLikeKeys'),
baseKeys = require('./_baseKeys'),
isArrayLike = require('./isArrayLike');
function keys(object) {
return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object);
}
module.exports = keys;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/keys.js
},{"./_arrayLikeKeys":3,"./_baseKeys":10,"./isArrayLike":30}],38:[function(require,module,exports){
"use strict";
function stubFalse() {
return false;
}
module.exports = stubFalse;
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/lodash/stubFalse.js
},{}],39:[function(require,module,exports){
"use strict";
module.exports = function(n) {
var t = {},
e = [];
n = n || this, n.on = function(e, r, l) {
return (t[e] = t[e] || []).push([r, l]), n;
}, n.off = function(r, l) {
r || (t = {});
for (var o = t[r] || e,
u = o.length = l ? o.length : 0; u--; )
l == o[u][0] && o.splice(u, 1);
return n;
}, n.emit = function(r) {
for (var l,
o = t[r] || e,
u = o.length > 0 ? o.slice(0, o.length) : o,
i = 0; l = u[i++]; )
l[0].apply(l[1], e.slice.call(arguments, 1));
return n;
};
};
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/minivents/dist/minivents.commonjs.min.js
},{}],40:[function(require,module,exports){
"use strict";
var gatewayApi = require('@tryghost/members-gateway-api');
module.exports = function layer2(options) {
var authUrl = (options.membersUrl + "/auth");
var gatewayUrl = (options.membersUrl + "/gateway");
var container = options.container;
var members = gatewayApi({
gatewayUrl: gatewayUrl,
container: container
});
function postMessage(frame, data) {
var origin = new URL(frame.getAttribute('src')).origin;
frame.contentWindow.postMessage(data, origin);
}
var loadAuth = loadFrame(authUrl, container).then(function(frame) {
frame.style.position = 'fixed';
frame.style.width = '100%';
frame.style.height = '100%';
frame.style.background = 'transparent';
frame.style.top = '0';
frame.style['z-index'] = '9999';
return frame;
});
function openAuth(hash) {
var query = arguments[1] !== (void 0) ? arguments[1] : '';
return loadAuth.then(function(frame) {
return new Promise(function(resolve) {
postMessage(frame, {
hash: hash,
query: query
});
frame.style.display = 'block';
window.addEventListener('message', function messageListener(event) {
if (event.source !== frame.contentWindow) {
return;
}
if (!event.data || event.data.msg !== 'pls-close-auth-popup') {
return;
}
window.removeEventListener('message', messageListener);
frame.style.display = 'none';
resolve(!!event.data.success);
});
});
});
}
function resetPassword($__0) {
var token = $__0.token;
var query = ("token=" + token);
return openAuth('reset-password', query);
}
function signin() {
return openAuth('signin');
}
function upgrade() {
return openAuth('upgrade');
}
function signup() {
var coupon = (arguments[0] !== (void 0) ? arguments[0] : {}).coupon;
var query = ("coupon=" + coupon);
return openAuth('signup', query);
}
function getToken($__0) {
var $__1 = $__0,
audience = $__1.audience,
fresh = $__1.fresh;
return members.getToken({
audience: audience,
fresh: fresh
});
}
function getSSRToken() {
var fresh = (arguments[0] !== (void 0) ? arguments[0] : {}).fresh;
return members.getConfig().then(function($__1) {
var issuer = $__1.issuer;
return members.getToken({
audience: issuer,
fresh: fresh
});
});
}
function signout() {
return members.signout();
}
return Object.assign(members.bus, {
getToken: getToken,
getSSRToken: getSSRToken,
signout: signout,
signin: signin,
signup: signup,
upgrade: upgrade,
resetPassword: resetPassword
});
};
function loadFrame(src) {
var container = arguments[1] !== (void 0) ? arguments[1] : document.body;
return new Promise(function(resolve) {
var frame = document.createElement('iframe');
frame.style.display = 'none';
frame.src = src;
frame.onload = function() {
resolve(frame);
};
container.appendChild(frame);
});
}
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/@tryghost/members-browser-auth/index.js
},{"@tryghost/members-gateway-api":41}],41:[function(require,module,exports){
"use strict";
var gatewayProtocol = require('@tryghost/members-gateway-protocol');
var events = require('minivents');
module.exports = function layer1(options) {
var members = {
getToken: getToken,
getConfig: getConfig,
signout: signout,
signin: signin,
signup: signup,
requestPasswordReset: requestPasswordReset,
resetPassword: resetPassword,
bus: new events()
};
var loadGateway = loadFrame(options.gatewayUrl, options.container).then(function(frame) {
var gateway = gatewayProtocol(frame);
var init = gatewayFn('init');
gateway.listen(function(data) {
members.bus.emit(data.event, data.payload);
});
return init(gateway).then(function() {
return gateway;
});
});
function getToken($__0) {
var $__1 = $__0,
audience = $__1.audience,
fresh = $__1.fresh;
return loadGateway.then(gatewayFn('getToken', {
audience: audience,
fresh: fresh
}));
}
function getConfig() {
return loadGateway.then(gatewayFn('getConfig'));
}
function signout() {
return loadGateway.then(gatewayFn('signout'));
}
function signin($__0) {
var $__1 = $__0,
email = $__1.email,
password = $__1.password;
return loadGateway.then(gatewayFn('signin', {
email: email,
password: password
}));
}
function signup($__0) {
var $__1 = $__0,
name = $__1.name,
email = $__1.email,
password = $__1.password;
return loadGateway.then(gatewayFn('signin', {
name: name,
email: email,
password: password
}));
}
function requestPasswordReset($__0) {
var email = $__0.email;
return loadGateway.then(gatewayFn('request-password-reset', {email: email}));
}
function resetPassword($__0) {
var $__1 = $__0,
token = $__1.token,
password = $__1.password;
return loadGateway.then(gatewayFn('reset-password', {
token: token,
password: password
}));
}
return members;
};
function gatewayFn(method) {
var opts = arguments[1] !== (void 0) ? arguments[1] : {};
return function(gateway) {
return new Promise(function(resolve, reject) {
gateway.call(method, opts, function(err, res) {
if (err) {
reject(err);
}
resolve(res);
});
});
};
}
function loadFrame(src) {
var container = arguments[1] !== (void 0) ? arguments[1] : document.body;
return new Promise(function(resolve) {
var frame = document.createElement('iframe');
frame.style.display = 'none';
frame.src = src;
frame.onload = function() {
resolve(frame);
};
container.appendChild(frame);
});
}
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/@tryghost/members-gateway-api/index.js
},{"@tryghost/members-gateway-protocol":42,"minivents":39}],42:[function(require,module,exports){
"use strict";
module.exports = function layer0(frame) {
var getuid = (function(i) {
return function() {
return i += 1;
};
})(1);
var origin = new URL(frame.getAttribute('src')).origin;
var handlers = {};
var listener = null;
window.addEventListener('message', function(event) {
if (event.origin !== origin) {
return;
}
if (!event.data || !event.data.uid) {
if (event.data.event) {
return listener && listener(event.data);
}
return;
}
var handler = handlers[event.data.uid];
if (!handler) {
return;
}
delete handlers[event.data.uid];
handler(event.data.error, event.data.data);
});
function call(method, options, cb) {
var uid = getuid();
var data = {
uid: uid,
method: method,
options: options
};
handlers[uid] = cb;
frame.contentWindow.postMessage(data, origin);
}
function listen(fn) {
if (listener) {
return false;
}
listener = fn;
return true;
}
return {
call: call,
listen: listen
};
};
//# sourceURL=/home/donny/usr/src/ghost.org/Members/node_modules/@tryghost/members-gateway-protocol/index.js
},{}],43:[function(require,module,exports){
"use strict";
var each = require('lodash/each');
var browserAuth = require('@tryghost/members-browser-auth');
module.exports.init = init;
function init($__0) {
var $__1 = $__0,
membersUrl = $__1.membersUrl,
ssrUrl = $__1.ssrUrl;
var auth = browserAuth({membersUrl: membersUrl});
var $__2 = window.location.hash.match(/^#([^?]+)\??(.*)$/) || [],
hashMatch = $__2[0],
hash = $__2[1],
query = $__2[2];
if (hashMatch && hash === 'reset-password') {
var $__3 = query.match(/token=([a-zA-Z0-9-_]+.[a-zA-Z0-9-_]+.[a-zA-Z0-9-_]+)/) || [],
tokenMatch = $__3[0],
token = $__3[1];
if (tokenMatch) {
return auth.resetPassword({token: token}).then((function(success) {
window.location.hash = '';
return success;
})).then(reload);
}
}
auth.on('signedin', function() {
auth.getSSRToken({fresh: true}).then(function(token) {
createSession(token, ssrUrl);
});
});
auth.on('signedout', function() {
destroySession();
});
function signout() {
auth.signout().then((function() {
return destroySession(ssrUrl);
})).then(reload);
}
function signin() {
auth.signin().then((function() {
return auth.getSSRToken({fresh: true}).then(function(token) {
return createSession(token, ssrUrl);
});
})).then(reload);
}
function signup($__4) {
var $__6;
var $__5 = $__4,
coupon = ($__6 = $__5.coupon) === void 0 ? '' : $__6;
auth.signup({coupon: coupon}).then((function() {
return auth.getSSRToken({fresh: true}).then(function(token) {
return createSession(token, ssrUrl);
});
})).then(reload);
}
function upgrade() {
auth.upgrade().then((function() {
return auth.getSSRToken({fresh: true}).then(function(token) {
return createSession(token, ssrUrl);
});
})).then(reload);
}
var signinEls = document.querySelectorAll('[data-members-signin]');
var signupEls = document.querySelectorAll('[data-members-signup]');
var upgradeEls = document.querySelectorAll('[data-members-upgrade]');
var signoutEls = document.querySelectorAll('[data-members-signout]');
each(signinEls, (function(el) {
el.addEventListener('click', (function(event) {
event.preventDefault();
signin();
}));
}));
each(signupEls, (function(el) {
el.addEventListener('click', (function(event) {
event.preventDefault();
var coupon = el.dataset.membersCoupon;
signup({coupon: coupon});
}));
}));
each(upgradeEls, (function(el) {
el.addEventListener('click', (function(event) {
event.preventDefault();
upgrade();
}));
}));
each(signoutEls, (function(el) {
el.addEventListener('click', (function(event) {
event.preventDefault();
signout();
}));
}));
}
function reload(success) {
if (success) {
window.location.reload();
}
}
function createSession(token, ssrUrl) {
return fetch(ssrUrl, {
method: 'post',
credentials: 'include',
body: token
}).then(function(res) {
return !!res.ok;
});
}
function destroySession(ssrUrl) {
return fetch(ssrUrl, {method: 'delete'}).then(function(res) {
return !!res.ok;
});
}
//# sourceURL=/home/donny/usr/src/ghost.org/Members/packages/members-theme-bindings/index.js
},{"@tryghost/members-browser-auth":40,"lodash/each":25}]},{},[43])(43)
});

View file

@ -1 +1,83 @@
MembersThemeBindings.init({ssrUrl: "{{blog-url}}/members/ssr", membersUrl: "{{admin-url}}/api/v2/members/static"});
Array.prototype.forEach.call(document.querySelectorAll('form[data-members-form]'), function (form){
form.addEventListener('submit', function (event) {
event.preventDefault();
form.classList.remove('success', 'invalid', 'error');
var input = event.target.querySelector('input[data-members-email]');
var email = input.value;
if (!email.includes('@')) {
form.classList.add('invalid')
return;
}
fetch('{{admin-url}}/api/canary/members/send-magic-link/', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: email
})
}).then(function (res) {
if (res.ok) {
form.classList.add('success')
} else {
form.classList.add('error')
}
});
});
});
Array.prototype.forEach.call(document.querySelectorAll('[data-members-subscription]'), function (button) {
button.addEventListener('click', function (event) {
event.preventDefault();
var plan = event.target.dataset.membersSubscriptionPlan;
fetch('{{blog-url}}/members/ssr', {
credentials: 'same-origin'
}).then(function (res) {
if (!res.ok) {
throw new Error('Could not get identity token');
}
return res.text();
}).then(function (identity) {
return fetch('{{admin-url}}/api/canary/members/create-stripe-checkout-session/', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
plan: plan,
identity: identity
})
}).then(function (res) {
if (!res.ok) {
throw new Error('Could not create stripe checkout session');
}
return res.json();
});
}).then(function (result) {
var stripe = Stripe(result.publicKey);
return stripe.redirectToCheckout({
sessionId: result.sessionId
});
});
});
});
var magicLinkRegEx = /token=([a-zA-Z0-9_\-]+\.[a-zA-Z0-9_\-]+\.[a-zA-Z0-9_\-]+)/;
var match = location.search.match(magicLinkRegEx);
var isMagicLink = !!match
var token = match && match[1];
if (isMagicLink) {
fetch('{{blog-url}}/members/ssr', {
method: 'POST',
body: token
}).then(function (res) {
if (res.ok) {
window.location.search = window.location.search.replace(magicLinkRegEx, '');
}
});
}

View file

@ -1 +1 @@
MembersThemeBindings.init({siteUrl: "{{blog-url}}"});
Array.prototype.forEach.call(document.querySelectorAll("form[data-members-form]"),function(form){form.addEventListener("submit",function(event){event.preventDefault(),form.classList.remove("success","invalid","error");var email=event.target.querySelector("input[data-members-email]").value;email.includes("@")?fetch("{{admin-url}}/api/canary/members/send-magic-link/",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:email})}).then(function(res){res.ok?form.classList.add("success"):form.classList.add("error")}):form.classList.add("invalid")})}),Array.prototype.forEach.call(document.querySelectorAll("[data-members-subscription]"),function(button){button.addEventListener("click",function(event){event.preventDefault();var plan=event.target.dataset.membersSubscriptionPlan;fetch("{{blog-url}}/members/ssr",{credentials:"same-origin"}).then(function(res){if(!res.ok)throw new Error("Could not get identity token");return res.text()}).then(function(identity){return fetch("{{admin-url}}/api/canary/members/create-stripe-checkout-session/",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({plan:plan,identity:identity})}).then(function(res){if(!res.ok)throw new Error("Could not create stripe checkout session");return res.json()})}).then(function(result){return Stripe(result.publicKey).redirectToCheckout({sessionId:result.sessionId})})})});var magicLinkRegEx=/token=([a-zA-Z0-9_\-]+\.[a-zA-Z0-9_\-]+\.[a-zA-Z0-9_\-]+)/,match=location.search.match(magicLinkRegEx),isMagicLink=!!match,token=match&&match[1];isMagicLink&&fetch("{{blog-url}}/members/ssr",{method:"POST",body:token}).then(function(res){res.ok&&(window.location.search=window.location.search.replace(magicLinkRegEx,""))});

View file

@ -1,35 +1,24 @@
const url = require('url');
const {URL} = require('url');
const settingsCache = require('../settings/cache');
const urlUtils = require('../../lib/url-utils');
const MembersApi = require('@tryghost/members-api');
const common = require('../../lib/common');
const models = require('../../models');
const mail = require('../mail');
const blogIcon = require('../../lib/image/blog-icon');
const doBlock = fn => fn();
const models = require('../../models');
function createMember({name, email, password}) {
function createMember({email}) {
return models.Member.add({
name,
email,
password
}).then((member) => {
return member.toJSON();
});
}
function updateMember(member, newData) {
return models.Member.findOne(member, {
require: true
}).then(({id}) => {
return models.Member.edit(newData, {id});
email
}).then((member) => {
return member.toJSON();
});
}
function getMember(data, options = {}) {
return models.Member.findOne(data, Object.assign({require: true}, options)).then((model) => {
if (!data.email && !data.id) {
return Promise.resolve(null);
}
return models.Member.findOne(data, options).then((model) => {
if (!model) {
return null;
}
@ -57,153 +46,73 @@ function listMembers(options) {
});
}
function validateMember({email, password}) {
return models.Member.findOne({email}, {
require: true
}).then((member) => {
return member.comparePassword(password).then((res) => {
if (!res) {
throw new Error('Password is incorrect');
}
return member;
});
}).then((member) => {
return member.toJSON();
});
}
function getSubscriptionSettings() {
let membersSettings = settingsCache.get('members_subscription_settings');
if (!membersSettings) {
membersSettings = {
isPaid: false,
paymentProcessors: [{
adapter: 'stripe',
config: {
secret_token: '',
public_token: '',
product: {
name: 'Ghost Subscription'
},
plans: [
{
name: 'Monthly',
currency: 'usd',
interval: 'month',
amount: ''
},
{
name: 'Yearly',
currency: 'usd',
interval: 'year',
amount: ''
}
]
}
}]
};
}
if (!membersSettings.isPaid) {
membersSettings.paymentProcessors = [];
}
return membersSettings;
}
const siteUrl = urlUtils.getSiteUrl();
const siteOrigin = doBlock(() => {
const {protocol, host} = url.parse(siteUrl);
return `${protocol}//${host}`;
});
const adminOrigin = doBlock(() => {
const {protocol, host} = url.parse(urlUtils.urlFor('admin', true));
return `${protocol}//${host}`;
});
const getApiUrl = ({version, type}) => {
const {href} = new url.URL(
const {href} = new URL(
urlUtils.getApiPath({version, type}),
urlUtils.urlFor('admin', true)
);
return href;
};
const contentApiUrl = getApiUrl({version: 'v2', type: 'content'});
const siteUrl = urlUtils.getSiteUrl();
const membersApiUrl = getApiUrl({version: 'v2', type: 'members'});
const accessControl = {
[siteOrigin]: {
[contentApiUrl]: {
tokenLength: '20m'
},
[membersApiUrl]: {
tokenLength: '180d'
}
},
'*': {
tokenLength: '20m'
const ghostMailer = new mail.GhostMailer();
function getStripePaymentConfig() {
const subscriptionSettings = settingsCache.get('members_subscription_settings');
if (!subscriptionSettings || subscriptionSettings.isPaid === false) {
return null;
}
};
const sendEmail = (function createSendEmail(mailer) {
return function sendEmail(member, {token}) {
if (!(mailer instanceof mail.GhostMailer)) {
mailer = new mail.GhostMailer();
}
const message = {
to: member.email,
subject: 'Reset password',
html: `
Hi ${member.name},
const stripePaymentProcessor = subscriptionSettings.paymentProcessors.find(
paymentProcessor => paymentProcessor.adapter === 'stripe'
);
To reset your password, click the following link and follow the instructions:
if (!stripePaymentProcessor || !stripePaymentProcessor.config) {
return null;
}
${siteUrl}#reset-password?token=${token}
If you didn't request a password change, just ignore this email.
`
};
/* eslint-disable */
// @TODO remove this
console.log(message.html);
/* eslint-enable */
return mailer.send(message).catch((err) => {
return Promise.reject(err);
});
};
})();
const getSiteConfig = () => {
return {
title: settingsCache.get('title') ? settingsCache.get('title').replace(/"/g, '\\"') : 'Publication',
icon: blogIcon.getIconUrl()
publicKey: stripePaymentProcessor.config.public_token,
secretKey: stripePaymentProcessor.config.secret_token,
checkoutSuccessUrl: siteUrl,
checkoutCancelUrl: siteUrl,
product: stripePaymentProcessor.config.product,
plans: stripePaymentProcessor.config.plans
};
};
}
module.exports = createApiInstance;
function createApiInstance() {
const membersApiInstance = MembersApi({
authConfig: {
tokenConfig: {
issuer: membersApiUrl,
ssoOrigin: adminOrigin,
publicKey: settingsCache.get('members_public_key'),
privateKey: settingsCache.get('members_private_key'),
sessionSecret: settingsCache.get('members_session_secret'),
accessControl
privateKey: settingsCache.get('members_private_key')
},
auth: {
getSigninURL(token) {
const signinURL = new URL(siteUrl);
signinURL.searchParams.set('token', token);
return signinURL.href;
}
},
mail: {
transporter: {
sendMail(message) {
return ghostMailer.send(Object.assign({subject: 'Signin'}, message));
}
}
},
paymentConfig: {
processors: getSubscriptionSettings().paymentProcessors
stripe: getStripePaymentConfig()
},
siteConfig: getSiteConfig(),
createMember,
getMember,
deleteMember,
listMembers,
validateMember,
updateMember,
sendEmail
listMembers
});
membersApiInstance.setLogger(common.logging);

View file

@ -1,5 +1,3 @@
const {static} = require('express');
const path = require('path');
const MembersSSR = require('@tryghost/members-ssr');
const createMembersApiInstance = require('./api');
@ -11,7 +9,7 @@ let membersApi;
// Bind to events to automatically keep subscription info up-to-date from settings
common.events.on('settings.edited', function updateSettingFromModel(settingModel) {
if (!['members_subscription_settings', 'title', 'icon'].includes(settingModel.get('key'))) {
if (!['members_subscription_settings'].includes(settingModel.get('key'))) {
return;
}
@ -44,17 +42,12 @@ const membersService = {
ssr: MembersSSR({
cookieSecure: urlUtils.isSSL(urlUtils.getSiteUrl()),
cookieKeys: [settingsCache.get('theme_session_secret')],
cookieName: 'ghost-members-ssr',
cookieCacheName: 'ghost-members-ssr-cache',
// This is passed as a function so that updates to the instance
// are picked up in the ssr module
membersApi: () => membersApi
}),
authPages: static(
path.join(
require.resolve('@tryghost/members-auth-pages'),
'../dist'
)
)
})
};
module.exports = membersService;

View file

@ -578,6 +578,9 @@
"flagMustBeEnabled": "The {flagName} flag must be enabled in labs if you wish to use the \\{\\{{helperName}\\}\\} helper.",
"seeLink": "See {url}",
"mustBeCalledAsBlock": "The \\{\\{{helperName}\\}\\} helper must be called as a block. E.g. \\{\\{#{helperName}\\}\\} \\{\\{/{helperName}\\}\\}",
"asset": {
"pathIsRequired": "The \\{\\{asset\\}\\} helper must be passed a path"
},
"foreach": {
"iteratorNeeded": "Need to pass an iterator to #foreach"
},

View file

@ -1,6 +1,9 @@
const {URL} = require('url');
const debug = require('ghost-ignition').debug('web:canary:members:app');
const express = require('express');
const cors = require('cors');
const membersService = require('../../../../services/members');
const urlUtils = require('../../../../lib/url-utils');
const labs = require('../../../shared/middlewares/labs');
const shared = require('../../../shared');
@ -11,8 +14,9 @@ module.exports = function setupMembersApiApp() {
// Entire app is behind labs flag
apiApp.use(labs.members);
// Set up the auth pages
apiApp.use('/static/auth', membersService.authPages);
// Support CORS for requests from the frontend
const siteUrl = new URL(urlUtils.getSiteUrl());
apiApp.use(cors(siteUrl.origin));
// Set up the api endpoints and the gateway
// NOTE: this is wrapped in a function to ensure we always go via the getter

View file

@ -1,6 +1,9 @@
const {URL} = require('url');
const debug = require('ghost-ignition').debug('web:v2:members:app');
const express = require('express');
const cors = require('cors');
const membersService = require('../../../../services/members');
const urlUtils = require('../../../../lib/url-utils');
const labs = require('../../../shared/middlewares/labs');
const shared = require('../../../shared');
@ -11,8 +14,9 @@ module.exports = function setupMembersApiApp() {
// Entire app is behind labs flag
apiApp.use(labs.members);
// Set up the auth pages
apiApp.use('/static/auth', membersService.authPages);
// Support CORS for requests from the frontend
const siteUrl = new URL(urlUtils.getSiteUrl());
apiApp.use(cors(siteUrl.origin));
// Set up the api endpoints and the gateway
// NOTE: this is wrapped in a function to ensure we always go via the getter

View file

@ -186,7 +186,7 @@ const privateBlog = () => {
extend({
attachResetToRequest: false,
failCallback(req, res, next, nextValidRequestDate) {
common.logging.error(new common.errors.GhostError({
common.logging.error(new common.errors.TooManyRequestsError({
message: common.i18n.t('errors.middleware.spamprevention.tooManySigninAttempts.error',
{
rateSigninAttempts: spamPrivateBlog.freeRetries + 1 || 5,
@ -195,7 +195,7 @@ const privateBlog = () => {
context: common.i18n.t('errors.middleware.spamprevention.tooManySigninAttempts.context')
}));
return next(new common.errors.GhostError({
return next(new common.errors.TooManyRequestsError({
message: `Too many private sign-in attempts try again in ${moment(nextValidRequestDate).fromNow(true)}`
}));
},

View file

@ -39,6 +39,14 @@ _private.prepareError = (err, req, res, next) => {
err = new common.errors.NotFoundError({
err: err
});
} else if (err instanceof TypeError && err.stack.match(/node_modules\/handlebars\//)) {
// Temporary handling of theme errors from handlebars
// @TODO remove this when #10496 is solved properly
err = new common.errors.IncorrectUsageError({
err: err,
message: '{{#if}} or {{#unless}} helper is malformed',
statusCode: err.statusCode
});
} else {
err = new common.errors.GhostError({
err: err,

View file

@ -1,6 +1,8 @@
const debug = require('ghost-ignition').debug('web:site:app');
const path = require('path');
const express = require('express');
const cors = require('cors');
const {URL} = require('url');
// App requires
const config = require('../../config');
@ -20,6 +22,39 @@ const STATIC_IMAGE_URL_PREFIX = `/${urlUtils.STATIC_IMAGE_URL_PREFIX}`;
let router;
const corsOptionsDelegate = function corsOptionsDelegate(req, callback) {
const origin = req.header('Origin');
const corsOptions = {
origin: false, // disallow cross-origin requests by default
credentials: true // required to allow admin-client to login to private sites
};
if (origin) {
const originUrl = new URL(origin);
// allow all localhost and 127.0.0.1 requests no matter the port
if (originUrl.hostname === 'localhost' || originUrl.hostname === '127.0.0.1') {
corsOptions.origin = true;
}
// allow the configured host through on any protocol
const siteUrl = new URL(config.get('url'));
if (originUrl.host === siteUrl.host) {
corsOptions.origin = true;
}
// allow the configured admin:url host through on any protocol
if (config.get('admin:url')) {
const adminUrl = new URL(config.get('admin:url'));
if (originUrl.host === adminUrl.host) {
corsOptions.origin = true;
}
}
}
callback(null, corsOptions);
};
function SiteRouter(req, res, next) {
router(req, res, next);
}
@ -33,6 +68,9 @@ module.exports = function setupSiteApp(options = {}) {
// set the view engine
siteApp.set('view engine', 'hbs');
// enable CORS headers (allows admin client to hit front-end when configured on separate URLs)
siteApp.use(cors(corsOptionsDelegate));
// you can extend Ghost with a custom redirects file
// see https://github.com/TryGhost/Ghost/issues/7707
shared.middlewares.customRedirects.use(siteApp);
@ -93,6 +131,16 @@ module.exports = function setupSiteApp(options = {}) {
// @TODO only loads this stuff if members is enabled
// Set req.member & res.locals.member if a cookie is set
siteApp.get('/members/ssr', shared.middlewares.labs.members, function (req, res) {
membersService.ssr.getIdentityTokenForMemberFromSession(req, res).then((token) => {
res.writeHead(200);
res.end(token);
}).catch((err) => {
common.logging.warn(err.message);
res.writeHead(err.statusCode);
res.end(err.message);
});
});
siteApp.post('/members/ssr', shared.middlewares.labs.members, function (req, res) {
membersService.ssr.exchangeTokenForSession(req, res).then(() => {
res.writeHead(200);

View file

@ -68,7 +68,7 @@ describe('Admin Routing', function () {
describe('Legacy Redirects', function () {
it('should redirect /logout/ to /ghost/#/signout/', function (done) {
request.get('/logout/')
.expect('Location', '/ghost/#/signout/')
.expect('Location', 'http://127.0.0.1:2369/ghost/#/signout/')
.expect('Cache-Control', testUtils.cacheRules.year)
.expect(301)
.end(doEndNoAuth(done));
@ -76,7 +76,7 @@ describe('Admin Routing', function () {
it('should redirect /signout/ to /ghost/#/signout/', function (done) {
request.get('/signout/')
.expect('Location', '/ghost/#/signout/')
.expect('Location', 'http://127.0.0.1:2369/ghost/#/signout/')
.expect('Cache-Control', testUtils.cacheRules.year)
.expect(301)
.end(doEndNoAuth(done));
@ -84,7 +84,7 @@ describe('Admin Routing', function () {
it('should redirect /signup/ to /ghost/#/signup/', function (done) {
request.get('/signup/')
.expect('Location', '/ghost/#/signup/')
.expect('Location', 'http://127.0.0.1:2369/ghost/#/signup/')
.expect('Cache-Control', testUtils.cacheRules.year)
.expect(301)
.end(doEndNoAuth(done));
@ -93,7 +93,7 @@ describe('Admin Routing', function () {
// Admin aliases
it('should redirect /signin/ to /ghost/', function (done) {
request.get('/signin/')
.expect('Location', '/ghost/')
.expect('Location', 'http://127.0.0.1:2369/ghost/')
.expect('Cache-Control', testUtils.cacheRules.year)
.expect(301)
.end(doEndNoAuth(done));
@ -101,7 +101,7 @@ describe('Admin Routing', function () {
it('should redirect /admin/ to /ghost/', function (done) {
request.get('/admin/')
.expect('Location', '/ghost/')
.expect('Location', 'http://127.0.0.1:2369/ghost/')
.expect('Cache-Control', testUtils.cacheRules.year)
.expect(301)
.end(doEndNoAuth(done));

View file

@ -269,7 +269,7 @@ describe('Dynamic Routing', function () {
it('should redirect to tag settings', function (done) {
request.get('/tag/getting-started/edit/')
.expect('Location', '/ghost/#/settings/tags/getting-started/')
.expect('Location', 'http://127.0.0.1:2369/ghost/#/settings/tags/getting-started/')
.expect('Cache-Control', testUtils.cacheRules.public)
.expect(302)
.end(doEnd(done));
@ -496,7 +496,7 @@ describe('Dynamic Routing', function () {
it('should redirect to editor', function (done) {
request.get('/author/ghost-owner/edit/')
.expect('Location', '/ghost/#/team/ghost-owner/')
.expect('Location', 'http://127.0.0.1:2369/ghost/#/team/ghost-owner/')
.expect('Cache-Control', testUtils.cacheRules.public)
.expect(302)
.end(doEnd(done));

View file

@ -19,7 +19,7 @@ var should = require('should'),
*/
describe('DB version integrity', function () {
// Only these variables should need updating
const currentSchemaHash = '2b2cf2575dcf3fc86136b0dd4fba3263';
const currentSchemaHash = 'ca66b8548e520731f23be1493f9d560e';
const currentFixturesHash = 'c7b485fe2f16517295bd35c761129729';
// If this test is failing, then it is likely a change has been made that requires a DB version bump,

View file

@ -1,6 +1,6 @@
{
"name": "ghost",
"version": "2.30.2",
"version": "2.31.0",
"description": "The professional publishing platform",
"author": "Ghost Foundation",
"homepage": "https://ghost.org",
@ -32,8 +32,7 @@
"lint:test": "eslint -c core/test/.eslintrc.json --ignore-path core/test/.eslintignore 'core/test/**/*.js'",
"lint": "yarn lint:server && yarn lint:frontend && yarn lint:test",
"posttest": "yarn lint",
"fixmodulenotdefined": "yarn cache clean && cd core/client && rm -rf node_modules tmp dist && yarn && cd ../../",
"postinstall": "cpy node_modules/@tryghost/members-theme-bindings/build/members-theme-bindings.js core/server/public/"
"fixmodulenotdefined": "yarn cache clean && cd core/client && rm -rf node_modules tmp dist && yarn && cd ../../"
},
"engines": {
"node": "^8.10.0 || ^10.13.0",
@ -41,14 +40,12 @@
},
"dependencies": {
"@nexes/nql": "0.3.0",
"@tryghost/helpers": "1.1.8",
"@tryghost/members-api": "0.3.0",
"@tryghost/members-auth-pages": "1.1.0",
"@tryghost/members-ssr": "0.2.1",
"@tryghost/members-theme-bindings": "0.2.3",
"@tryghost/helpers": "1.1.9",
"@tryghost/members-api": "0.5.0",
"@tryghost/members-ssr": "0.4.0",
"@tryghost/social-urls": "0.1.2",
"@tryghost/string": "^0.1.3",
"@tryghost/url-utils": "0.3.0",
"@tryghost/url-utils": "0.3.1",
"ajv": "6.10.2",
"amperize": "0.6.0",
"analytics-node": "3.3.0",
@ -57,7 +54,7 @@
"bluebird": "3.5.5",
"body-parser": "1.19.0",
"bookshelf": "0.14.2",
"bookshelf-relations": "1.1.2",
"bookshelf-relations": "1.3.0",
"brute-knex": "4.0.0",
"bson-objectid": "1.3.0",
"chalk": "2.4.2",
@ -99,15 +96,15 @@
"markdown-it-lazy-headers": "0.1.3",
"markdown-it-mark": "2.0.0",
"metascraper": "5.6.6",
"metascraper-url": "5.6.6",
"metascraper-title": "5.6.6",
"metascraper-description": "5.6.6",
"metascraper-author": "5.6.6",
"metascraper-publisher": "5.6.6",
"metascraper-description": "5.6.6",
"metascraper-image": "5.6.6",
"metascraper-logo": "5.6.6",
"metascraper-logo-favicon": "5.6.6",
"mobiledoc-dom-renderer": "0.6.6",
"metascraper-publisher": "5.6.6",
"metascraper-title": "5.6.6",
"metascraper-url": "5.6.6",
"mobiledoc-dom-renderer": "0.7.0",
"moment": "2.24.0",
"moment-timezone": "0.5.23",
"multer": "1.4.2",
@ -133,9 +130,9 @@
"xml": "1.0.1"
},
"optionalDependencies": {
"@tryghost/html-to-mobiledoc": "0.5.1",
"@tryghost/html-to-mobiledoc": "0.6.0",
"sharp": "0.23.0",
"sqlite3": "4.0.9"
"sqlite3": "4.1.0"
},
"devDependencies": {
"eslint": "6.1.0",
@ -158,7 +155,7 @@
"matchdep": "2.0.0",
"mocha": "6.2.0",
"mock-knex": "0.4.6",
"nock": "10.0.6",
"nock": "11.3.3",
"proxyquire": "2.1.3",
"rewire": "4.0.1",
"should": "13.2.3",

792
yarn.lock

File diff suppressed because it is too large Load diff