mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-03 23:00:14 -05:00
Added status property to members (#12570)
refs #12160 This flag will allow us easier filtering of members via the API * Added status column to members table This flag will be used to determine if a member is free or paid, rather than relying on joins with the customers and subscriptions tables. * Added migration to populate members.status As we add the column with a default value of "free" we only need to care about the paid members here. We also preemptively handle migrations for SQLite where there are > 998 paid members.
This commit is contained in:
parent
b724b2be92
commit
a79ed1170d
8 changed files with 68 additions and 2 deletions
|
@ -104,7 +104,8 @@ function serializeMember(member, options) {
|
|||
email_count: json.email_count,
|
||||
email_opened_count: json.email_opened_count,
|
||||
email_open_rate: json.email_open_rate,
|
||||
email_recipients: json.email_recipients
|
||||
email_recipients: json.email_recipients,
|
||||
status: json.status
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -152,6 +153,7 @@ function createSerializer(debugString, serialize) {
|
|||
* @prop {number} email_opened_count
|
||||
* @prop {number} email_open_rate
|
||||
* @prop {null|SerializedEmailRecipient[]} email_recipients
|
||||
* @prop {'free'|'paid'} status
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
const {createAddColumnMigration} = require('../../utils');
|
||||
|
||||
module.exports = createAddColumnMigration('members', 'status', {
|
||||
type: 'string',
|
||||
maxlength: 50,
|
||||
nullable: false,
|
||||
defaultTo: 'free',
|
||||
validations: {
|
||||
isIn: [['free', 'paid']]
|
||||
}
|
||||
});
|
|
@ -0,0 +1,45 @@
|
|||
const {chunk} = require('lodash');
|
||||
const {createTransactionalMigration} = require('../../utils');
|
||||
const logging = require('../../../../../shared/logging');
|
||||
|
||||
module.exports = createTransactionalMigration(
|
||||
async function up(knex) {
|
||||
logging.info('Updating members.status based on members_stripe_customers_subscriptions.status');
|
||||
const paidMemberIds = (await knex('members')
|
||||
.select('members.id')
|
||||
.innerJoin(
|
||||
'members_stripe_customers',
|
||||
'members.id',
|
||||
'members_stripe_customers.member_id'
|
||||
).innerJoin(
|
||||
'members_stripe_customers_subscriptions',
|
||||
function () {
|
||||
this.on(
|
||||
'members_stripe_customers.customer_id',
|
||||
'members_stripe_customers_subscriptions.customer_id'
|
||||
).onIn(
|
||||
'members_stripe_customers_subscriptions.status',
|
||||
['active', 'trialing', 'past_due', 'unpaid']
|
||||
);
|
||||
}
|
||||
)).map(({id}) => id);
|
||||
|
||||
// Umm? Well... The current version of SQLite3 bundled with Ghost supports
|
||||
// a maximum of 999 variables, we use one variable for the SET value
|
||||
// and so we're left with 998 for our WHERE IN clause values
|
||||
const chunkSize = 998;
|
||||
const paidMemberIdChunks = chunk(paidMemberIds, chunkSize);
|
||||
|
||||
for (const paidMemberIdsChunk of paidMemberIdChunks) {
|
||||
await knex('members')
|
||||
.update('status', 'paid')
|
||||
.whereIn('id', paidMemberIdsChunk);
|
||||
}
|
||||
},
|
||||
async function down(knex) {
|
||||
logging.info('Updating all members status to "free"');
|
||||
return knex('members').update({
|
||||
status: 'free'
|
||||
});
|
||||
}
|
||||
);
|
|
@ -373,6 +373,11 @@ module.exports = {
|
|||
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
||||
uuid: {type: 'string', maxlength: 36, nullable: true, unique: true, validations: {isUUID: true}},
|
||||
email: {type: 'string', maxlength: 191, nullable: false, unique: true, validations: {isEmail: true}},
|
||||
status: {
|
||||
type: 'string', maxlength: 50, nullable: false, defaultTo: 'free', validations: {
|
||||
isIn: [['free', 'paid']]
|
||||
}
|
||||
},
|
||||
name: {type: 'string', maxlength: 191, nullable: true},
|
||||
note: {type: 'string', maxlength: 2000, nullable: true},
|
||||
geolocation: {type: 'string', maxlength: 2000, nullable: true},
|
||||
|
|
|
@ -10,6 +10,7 @@ const Member = ghostBookshelf.Model.extend({
|
|||
|
||||
defaults() {
|
||||
return {
|
||||
status: 'free',
|
||||
subscribed: true,
|
||||
uuid: uuid.v4(),
|
||||
email_count: 0,
|
||||
|
|
|
@ -95,6 +95,7 @@ const expectedProperties = {
|
|||
.concat('avatar_image')
|
||||
.concat('comped')
|
||||
.concat('labels')
|
||||
.without('status')
|
||||
,
|
||||
member_signin_url: ['member_id', 'url'],
|
||||
role: _(schema.roles)
|
||||
|
|
|
@ -61,6 +61,7 @@ const expectedProperties = {
|
|||
.concat('avatar_image')
|
||||
.concat('comped')
|
||||
.concat('labels')
|
||||
.without('status')
|
||||
,
|
||||
member_signin_url: ['member_id', 'url'],
|
||||
role: _(schema.roles)
|
||||
|
|
|
@ -32,7 +32,7 @@ const defaultSettings = require('../../../../core/server/data/schema/default-set
|
|||
*/
|
||||
describe('DB version integrity', function () {
|
||||
// Only these variables should need updating
|
||||
const currentSchemaHash = 'c61ecbc8c11f62d1d350c66ee1ac3151';
|
||||
const currentSchemaHash = '47e9b182da4ea9c056878354cc291191';
|
||||
const currentFixturesHash = '370d0da0ab7c45050b2ff30bce8896ba';
|
||||
const currentSettingsHash = '162f9294cc427eb32bc0577006c385ce';
|
||||
const currentRoutesHash = '3d180d52c663d173a6be791ef411ed01';
|
||||
|
|
Loading…
Add table
Reference in a new issue