0
Fork 0
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:
Fabien 'egg' O'Carroll 2021-01-27 14:06:21 +00:00 committed by Daniel Lockyer
parent b724b2be92
commit a79ed1170d
No known key found for this signature in database
GPG key ID: FFBC6FA2A6F6ABC1
8 changed files with 68 additions and 2 deletions

View file

@ -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
*/
/**

View file

@ -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']]
}
});

View file

@ -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'
});
}
);

View file

@ -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},

View file

@ -10,6 +10,7 @@ const Member = ghostBookshelf.Model.extend({
defaults() {
return {
status: 'free',
subscribed: true,
uuid: uuid.v4(),
email_count: 0,

View file

@ -95,6 +95,7 @@ const expectedProperties = {
.concat('avatar_image')
.concat('comped')
.concat('labels')
.without('status')
,
member_signin_url: ['member_id', 'url'],
role: _(schema.roles)

View file

@ -61,6 +61,7 @@ const expectedProperties = {
.concat('avatar_image')
.concat('comped')
.concat('labels')
.without('status')
,
member_signin_url: ['member_id', 'url'],
role: _(schema.roles)

View file

@ -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';