Skip to content

Commit

Permalink
Merge pull request #266 from internxt/feat/limits-per-tier
Browse files Browse the repository at this point in the history
[PB-1433]: feat/add-feature-limit-guard
  • Loading branch information
sg-gs authored Feb 22, 2024
2 parents c6e35a1 + ac29d8f commit 721328b
Show file tree
Hide file tree
Showing 39 changed files with 1,737 additions and 3 deletions.
4 changes: 3 additions & 1 deletion .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,6 @@ STRIPE_SK=sk_x
STRIPE_SK_TEST=sk_test_y

GATEWAY_USER=user
GATEWAY_PASS=gatewaypass
GATEWAY_PASS=gatewaypass

PAYMENTS_API_URL=http://host.docker.internal:8003
37 changes: 37 additions & 0 deletions migrations/20240128230532-create-tiers-table.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use strict';

const tableName = 'tiers';

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable(tableName, {
id: {
type: Sequelize.UUID,
allowNull: false,
primaryKey: true,
},
label: {
type: Sequelize.STRING,
allowNull: false,
},
context: {
type: Sequelize.STRING,
},
created_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.NOW,
},
updated_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.NOW,
},
});
},

async down(queryInterface) {
await queryInterface.dropTable(tableName);
},
};
42 changes: 42 additions & 0 deletions migrations/20240128230553-create-limits-table.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
'use strict';

const tableName = 'limits';

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable(tableName, {
id: {
type: Sequelize.UUID,
allowNull: false,
primaryKey: true,
},
label: {
type: Sequelize.STRING,
allowNull: false,
},
type: {
type: Sequelize.ENUM('counter', 'boolean'),
allowNull: false,
},
value: {
type: Sequelize.STRING,
allowNull: false,
},
created_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.NOW,
},
updated_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.NOW,
},
});
},

async down(queryInterface) {
await queryInterface.dropTable(tableName);
},
};
40 changes: 40 additions & 0 deletions migrations/20240128230608-create-tiers-limits-table.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
'use strict';

const tableName = 'tiers_limits';

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable(tableName, {
id: {
type: Sequelize.UUID,
allowNull: false,
primaryKey: true,
},
tier_id: {
type: Sequelize.UUID,
allowNull: false,
references: { model: 'tiers', key: 'id' },
},
limit_id: {
type: Sequelize.UUID,
allowNull: false,
references: { model: 'limits', key: 'id' },
},
created_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.NOW,
},
updated_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.NOW,
},
});
},

async down(queryInterface) {
await queryInterface.dropTable(tableName);
},
};
17 changes: 17 additions & 0 deletions migrations/20240128230626-add-tier-id-to-users.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use strict';

const tableName = 'users';
const newColumn = 'tier_id';

module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.addColumn(tableName, newColumn, {
type: Sequelize.UUID,
references: { model: 'tiers', key: 'id' },
});
},

async down(queryInterface) {
await queryInterface.removeColumn(tableName, newColumn);
},
};
46 changes: 46 additions & 0 deletions migrations/20240220163114-create-paid-plans-tiers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use strict';

const tableName = 'paid_plans';

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable(tableName, {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
allowNull: false,
autoIncrement: true,
},
plan_id: {
type: Sequelize.STRING,
allowNull: false,
},
tier_id: {
type: Sequelize.UUID,
allowNull: false,
references: {
model: 'tiers',
key: 'id',
},
},
description: {
type: Sequelize.STRING,
},
created_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.NOW,
},
updated_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.NOW,
},
});
},

async down(queryInterface) {
await queryInterface.dropTable(tableName);
},
};
138 changes: 138 additions & 0 deletions migrations/20240220203734-seed-tiers-and-limits.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
'use strict';

const { v4: uuidv4 } = require('uuid');

module.exports = {
async up(queryInterface) {
const tiersData = [
{ id: uuidv4(), label: '10gb_individual', context: 'Plan 10GB' },
{ id: uuidv4(), label: '200gb_individual', context: 'Plan 200GB' },
{ id: uuidv4(), label: '2tb_individual', context: 'Plan 2TB' },
{ id: uuidv4(), label: '5tb_individual', context: 'Plan 5TB' },
{ id: uuidv4(), label: '10tb_individual', context: 'Plan 10TB' },
];

await queryInterface.bulkInsert(
'tiers',
tiersData.map((tier) => ({
id: tier.id,
label: tier.label,
context: tier.context,
created_at: new Date(),
updated_at: new Date(),
})),
);

const limitsData = [
{
tierLabel: '10gb_individual',
limits: [
{ label: 'max-shared-items', type: 'counter', value: '10' },
{ label: 'max-shared-invites', type: 'counter', value: '5' },
{ label: 'file-versioning', type: 'boolean', value: 'false' },
{ label: 'max-file-upload-size', type: 'counter', value: '1' },
{ label: 'max-trash-storage-days', type: 'counter', value: '7' },
{ label: 'max-back-up-devices', type: 'counter', value: '1' },
],
},
{
tierLabel: '200gb_individual',
limits: [
{ label: 'max-shared-items', type: 'counter', value: '50' },
{ label: 'max-shared-invites', type: 'counter', value: '50' },
{ label: 'file-versioning', type: 'boolean', value: 'false' },
{ label: 'max-file-upload-size', type: 'counter', value: '5' },
{ label: 'max-trash-storage-days', type: 'counter', value: '90' },
{ label: 'max-back-up-devices', type: 'counter', value: '5' },
],
},
{
tierLabel: '2tb_individual',
limits: [
{ label: 'max-shared-items', type: 'counter', value: '50' },
{ label: 'max-shared-invites', type: 'counter', value: '50' },
{ label: 'file-versioning', type: 'boolean', value: 'true' },
{ label: 'max-file-upload-size', type: 'counter', value: '20' },
{ label: 'max-trash-storage-days', type: 'counter', value: '90' },
{ label: 'max-back-up-devices', type: 'counter', value: '10' },
],
},
{
tierLabel: '5tb_individual',
limits: [
{ label: 'max-shared-items', type: 'counter', value: '1000' },
{ label: 'max-shared-invites', type: 'counter', value: '100' },
{ label: 'file-versioning', type: 'boolean', value: 'true' },
{ label: 'max-file-upload-size', type: 'counter', value: '20' },
{ label: 'max-trash-storage-days', type: 'counter', value: '180' },
{ label: 'max-back-up-devices', type: 'counter', value: '20' },
],
},
{
tierLabel: '10tb_individual',
limits: [
{ label: 'max-shared-items', type: 'counter', value: '1000' },
{ label: 'max-shared-invites', type: 'counter', value: '100' },
{ label: 'file-versioning', type: 'boolean', value: 'true' },
{ label: 'max-file-upload-size', type: 'counter', value: '20' },
{ label: 'max-trash-storage-days', type: 'counter', value: '365' },
{ label: 'max-back-up-devices', type: 'counter', value: '20' },
],
},
];

let tierAndLimitsRelations = [];

const flattenedLimits = limitsData.flatMap((plan) => {
return plan.limits.map((limit) => {
const uuid = uuidv4();

tierAndLimitsRelations.push({
id: uuidv4(),
limit_id: uuid,
tier_id: tiersData.find((tier) => tier.label === plan.tierLabel).id,
created_at: new Date(),
updated_at: new Date(),
});

return {
id: uuid,
label: limit.label,
type: limit.type,
value: limit.value,
created_at: new Date(),
updated_at: new Date(),
};
});
});

await queryInterface.bulkInsert('limits', flattenedLimits);

await queryInterface.bulkInsert('tiers_limits', tierAndLimitsRelations);
},

async down(queryInterface) {
const tierLabels = [
'10gb_individual',
'200gb_individual',
'2tb_individual',
'5tb_individual',
'10tb_individual',
];

await queryInterface.sequelize.query(
`DELETE FROM tiers_limits WHERE tier_id IN (SELECT id FROM tiers WHERE label IN (:tierLabels))`,
{ replacements: { tierLabels } },
);

await queryInterface.sequelize.query(
`DELETE FROM tiers WHERE label IN (:tierLabels)`,
{ replacements: { tierLabels } },
);

// Delete any orphaned limits that are no longer associated with any tier
await queryInterface.sequelize.query(`
DELETE FROM limits WHERE id NOT IN (SELECT limit_id FROM tiers_limits)
`);
},
};
25 changes: 25 additions & 0 deletions migrations/20240220215830-seed-free-plan-to-paid-plans-table.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use strict';

module.exports = {
async up(queryInterface, Sequelize) {
const tiers = await queryInterface.sequelize.query(
"SELECT id FROM tiers WHERE label = '10gb_individual' LIMIT 1",
{ type: Sequelize.QueryTypes.SELECT },
);

if (tiers.length > 0) {
const tierId = tiers[0].id;

await queryInterface.sequelize.query(
`INSERT INTO paid_plans (plan_id, tier_id, description, created_at, updated_at)
VALUES ('free_000000', '${tierId}', 'Free Tier 10gb' ,NOW(), NOW())`,
);
}
},

async down(queryInterface) {
await queryInterface.sequelize.query(
"DELETE FROM paid_plans WHERE plan_id = 'free_000000'",
);
},
};
3 changes: 3 additions & 0 deletions src/config/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ export default () => ({
url: process.env.RECAPTCHA_V3_ENDPOINT,
threshold: process.env.RECAPTCHA_V3_SCORE_THRESHOLD,
},
payments: {
url: process.env.PAYMENTS_API_URL,
},
},
clients: {
drive: {
Expand Down
Loading

0 comments on commit 721328b

Please sign in to comment.