add server to monorepo.

This commit is contained in:
a.bouhuolia
2023-02-03 11:57:50 +02:00
parent 28e309981b
commit 80b97b5fdc
1303 changed files with 137049 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
exports.up = (knex) => knex.schema.createTable('password_resets', (table) => {
table.increments();
table.string('email').index();
table.string('token').index();
table.timestamp('created_at');
});
exports.down = (knex) => knex.schema.dropTableIfExists('password_resets');

View File

@@ -0,0 +1,22 @@
exports.up = function(knex) {
return knex.schema.createTable('tenants', (table) => {
table.bigIncrements();
table.string('organization_id').index();
table.dateTime('under_maintenance_since').nullable();
table.dateTime('initialized_at').nullable();
table.dateTime('seeded_at').nullable();
table.dateTime('built_at').nullable();
table.string('build_job_id');
table.integer('database_batch');
table.string('upgrade_job_id');
table.timestamps();
});
};
exports.down = function(knex) {
return knex.schema.dropTableIfExists('tenants');
};

View File

@@ -0,0 +1,26 @@
exports.up = (knex) => {
return knex.schema.createTable('users', (table) => {
table.increments();
table.string('first_name');
table.string('last_name');
table.string('email').index();
table.string('phone_number').index();
table.string('password');
table.boolean('active').index();
table.string('language');
table
.bigInteger('tenant_id')
.unsigned()
.index()
.references('id')
.inTable('tenants');
table.dateTime('invite_accepted_at').index();
table.dateTime('last_login_at').index();
table.dateTime('deleted_at').index();
table.timestamps();
});
};
exports.down = (knex) => {
return knex.schema.dropTableIfExists('users');
};

View File

@@ -0,0 +1,15 @@
exports.up = function(knex) {
return knex.schema.createTable('user_invites', (table) => {
table.increments();
table.string('email').index();
table.string('token').unique().index();
table.bigInteger('tenant_id').unsigned().index().references('id').inTable('tenants');
table.integer('user_id').unsigned().index().references('id').inTable('users');
table.datetime('created_at');
});
};
exports.down = function(knex) {
return knex.schema.dropTableIfExists('user_invites');
};

View File

@@ -0,0 +1,22 @@
exports.up = function(knex) {
return knex.schema.createTable('subscriptions_plans', table => {
table.increments();
table.string('name');
table.string('description');
table.decimal('price');
table.string('currency', 3);
table.integer('trial_period');
table.string('trial_interval');
table.integer('invoice_period');
table.string('invoice_interval');
table.timestamps();
});
};
exports.down = function(knex) {
return knex.schema.dropTableIfExists('subscriptions_plans')
};

View File

@@ -0,0 +1,30 @@
exports.up = function(knex) {
return knex.schema.createTable('subscription_plans', table => {
table.increments();
table.string('slug');
table.string('name');
table.string('desc');
table.boolean('active');
table.decimal('price').unsigned();
table.string('currency', 3);
table.decimal('trial_period').nullable();
table.string('trial_interval').nullable();
table.decimal('invoice_period').nullable();
table.string('invoice_interval').nullable();
table.integer('index').unsigned();
table.timestamps();
}).then(() => {
return knex.seed.run({
specific: 'seed_subscriptions_plans.js',
});
});
};
exports.down = function(knex) {
return knex.schema.dropTableIfExists('subscription_plans')
};

View File

@@ -0,0 +1,15 @@
exports.up = function(knex) {
return knex.schema.createTable('subscription_plan_features', table => {
table.increments();
table.integer('plan_id').unsigned().index().references('id').inTable('subscription_plans');
table.string('slug');
table.string('name');
table.string('description');
table.timestamps();
});
};
exports.down = function(knex) {
return knex.schema.dropTableIfExists('subscription_plan_features');
};

View File

@@ -0,0 +1,22 @@
exports.up = function(knex) {
return knex.schema.createTable('subscription_plan_subscriptions', table => {
table.increments('id');
table.string('slug');
table.integer('plan_id').unsigned().index().references('id').inTable('subscription_plans');
table.bigInteger('tenant_id').unsigned().index().references('id').inTable('tenants');
table.dateTime('starts_at').nullable();
table.dateTime('ends_at').nullable();
table.dateTime('cancels_at').nullable();
table.dateTime('canceled_at').nullable();
table.timestamps();
});
};
exports.down = function(knex) {
return knex.schema.dropTableIfExists('subscription_plan_subscriptions');
};

View File

@@ -0,0 +1,22 @@
exports.up = function(knex) {
return knex.schema.createTable('subscription_licenses', (table) => {
table.increments();
table.string('license_code').unique().index();
table.integer('plan_id').unsigned().index().references('id').inTable('subscription_plans');
table.integer('license_period').unsigned();
table.string('period_interval');
table.dateTime('sent_at').index();
table.dateTime('disabled_at').index();
table.dateTime('used_at').index();
table.timestamps();
})
};
exports.down = function(knex) {
return knex.schema.dropTableIfExists('subscription_licenses');
};

View File

@@ -0,0 +1,22 @@
exports.up = function (knex) {
return knex.schema.createTable('tenants_metadata', (table) => {
table.bigIncrements();
table.integer('tenant_id').unsigned();
table.string('name');
table.string('industry');
table.string('location');
table.string('base_currency');
table.string('language');
table.string('timezone');
table.string('date_format');
table.string('fiscal_year');
});
};
exports.down = function (knex) {
return knex.schema.dropTableIfExists('tenants_metadata');
};

View File

@@ -0,0 +1,30 @@
import SystemModel from '@/system/models/SystemModel';
import moment from 'moment';
export default class UserInvite extends SystemModel {
/**
* Table name.
*/
static get tableName() {
return 'user_invites';
}
/**
* Timestamps columns.
*/
get timestamps() {
return ['createdAt'];
}
/**
* Model modifiers.
*/
static get modifiers() {
return {
notExpired(query) {
const comp = moment().subtract(24, 'hours').toMySqlDateTime();
query.where('created_at', '>=', comp);
}
}
}
}

View File

@@ -0,0 +1,17 @@
import SystemModel from '@/system/models/SystemModel';
export default class PasswordResets extends SystemModel {
/**
* Table name
*/
static get tableName() {
return 'password_resets';
}
/**
* Timestamps columns.
*/
get timestamps() {
return ['createdAt'];
}
}

View File

@@ -0,0 +1,129 @@
import { Model, mixin } from 'objection';
import moment from 'moment';
import SystemModel from '@/system/models/SystemModel';
export default class License extends SystemModel {
/**
* Table name.
*/
static get tableName() {
return 'subscription_licenses';
}
/**
* Timestamps columns.
*/
get timestamps() {
return ['createdAt', 'updatedAt'];
}
/**
* Model modifiers.
*/
static get modifiers() {
return {
// Filters active licenses.
filterActiveLicense(query) {
query.where('disabled_at', null);
query.where('used_at', null);
},
// Find license by its code or id.
findByCodeOrId(query, id, code) {
if (id) {
query.where('id', id);
}
if (code) {
query.where('license_code', code);
}
},
// Filters licenses list.
filter(builder, licensesFilter) {
if (licensesFilter.active) {
builder.modify('filterActiveLicense');
}
if (licensesFilter.disabled) {
builder.whereNot('disabled_at', null);
}
if (licensesFilter.used) {
builder.whereNot('used_at', null);
}
if (licensesFilter.sent) {
builder.whereNot('sent_at', null);
}
},
};
}
/**
* Relationship mapping.
*/
static get relationMappings() {
const Plan = require('system/models/Subscriptions/Plan');
return {
plan: {
relation: Model.BelongsToOneRelation,
modelClass: Plan.default,
join: {
from: 'subscription_licenses.planId',
to: 'subscriptions_plans.id',
},
},
};
}
/**
* Deletes the given license code from the storage.
* @param {string} licenseCode
* @return {Promise}
*/
static deleteLicense(licenseCode, viaAttribute = 'license_code') {
return this.query().where(viaAttribute, licenseCode).delete();
}
/**
* Marks the given license code as disabled on the storage.
* @param {string} licenseCode
* @return {Promise}
*/
static markLicenseAsDisabled(licenseCode, viaAttribute = 'license_code') {
return this.query().where(viaAttribute, licenseCode).patch({
disabled_at: moment().toMySqlDateTime(),
});
}
/**
* Marks the given license code as sent on the storage.
* @param {string} licenseCode
*/
static markLicenseAsSent(licenseCode, viaAttribute = 'license_code') {
return this.query().where(viaAttribute, licenseCode).patch({
sent_at: moment().toMySqlDateTime(),
});
}
/**
* Marks the given license code as used on the storage.
* @param {string} licenseCode
* @return {Promise}
*/
static markLicenseAsUsed(licenseCode, viaAttribute = 'license_code') {
return this.query().where(viaAttribute, licenseCode).patch({
used_at: moment().toMySqlDateTime(),
});
}
/**
*
* @param {IIPlan} plan
* @return {boolean}
*/
isEqualPlanPeriod(plan) {
return (
this.invoicePeriod === plan.invoiceInterval &&
license.licensePeriod === license.periodInterval
);
}
}

View File

@@ -0,0 +1,82 @@
import { Model, mixin } from 'objection';
import SystemModel from '@/system/models/SystemModel';
import { PlanSubscription } from '..';
export default class Plan extends mixin(SystemModel) {
/**
* Table name.
*/
static get tableName() {
return 'subscription_plans';
}
/**
* Timestamps columns.
*/
get timestamps() {
return ['createdAt', 'updatedAt'];
}
/**
* Defined virtual attributes.
*/
static get virtualAttributes() {
return ['isFree', 'hasTrial'];
}
/**
* Model modifiers.
*/
static get modifiers() {
return {
getFeatureBySlug(builder, featureSlug) {
builder.where('slug', featureSlug);
},
};
}
/**
* Relationship mapping.
*/
static get relationMappings() {
const PlanSubscription = require('system/models/Subscriptions/PlanSubscription');
return {
/**
* The plan may have many subscriptions.
*/
subscriptions: {
relation: Model.HasManyRelation,
modelClass: PlanSubscription.default,
join: {
from: 'subscription_plans.id',
to: 'subscription_plan_subscriptions.planId',
},
}
};
}
/**
* Check if plan is free.
* @return {boolean}
*/
isFree() {
return this.price <= 0;
}
/**
* Check if plan is paid.
* @return {boolean}
*/
isPaid() {
return !this.isFree();
}
/**
* Check if plan has trial.
* @return {boolean}
*/
hasTrial() {
return this.trialPeriod && this.trialInterval;
}
}

View File

@@ -0,0 +1,36 @@
import { Model, mixin } from 'objection';
import SystemModel from '@/system/models/SystemModel';
export default class PlanFeature extends mixin(SystemModel) {
/**
* Table name.
*/
static get tableName() {
return 'subscriptions.plan_features';
}
/**
* Timestamps columns.
*/
static get timestamps() {
return ['createdAt', 'updatedAt'];
}
/**
* Relationship mapping.
*/
static get relationMappings() {
const Plan = require('system/models/Subscriptions/Plan');
return {
plan: {
relation: Model.BelongsToOneRelation,
modelClass: Plan.default,
join: {
from: 'subscriptions.plan_features.planId',
to: 'subscriptions.plans.id',
},
},
};
}
}

View File

@@ -0,0 +1,164 @@
import { Model, mixin } from 'objection';
import SystemModel from '@/system/models/SystemModel';
import moment from 'moment';
import SubscriptionPeriod from '@/services/Subscription/SubscriptionPeriod';
export default class PlanSubscription extends mixin(SystemModel) {
/**
* Table name.
*/
static get tableName() {
return 'subscription_plan_subscriptions';
}
/**
* Timestamps columns.
*/
get timestamps() {
return ['createdAt', 'updatedAt'];
}
/**
* Defined virtual attributes.
*/
static get virtualAttributes() {
return ['active', 'inactive', 'ended', 'onTrial'];
}
/**
* Modifiers queries.
*/
static get modifiers() {
return {
activeSubscriptions(builder) {
const dateFormat = 'YYYY-MM-DD HH:mm:ss';
const now = moment().format(dateFormat);
builder.where('ends_at', '>', now);
builder.where('trial_ends_at', '>', now);
},
inactiveSubscriptions() {
builder.modify('endedTrial');
builder.modify('endedPeriod');
},
subscriptionBySlug(builder, subscriptionSlug) {
builder.where('slug', subscriptionSlug);
},
endedTrial(builder) {
const dateFormat = 'YYYY-MM-DD HH:mm:ss';
const endDate = moment().format(dateFormat);
builder.where('ends_at', '<=', endDate);
},
endedPeriod(builder) {
const dateFormat = 'YYYY-MM-DD HH:mm:ss';
const endDate = moment().format(dateFormat);
builder.where('trial_ends_at', '<=', endDate);
},
};
}
/**
* Relations mappings.
*/
static get relationMappings() {
const Tenant = require('system/models/Tenant');
const Plan = require('system/models/Subscriptions/Plan');
return {
/**
* Plan subscription belongs to tenant.
*/
tenant: {
relation: Model.BelongsToOneRelation,
modelClass: Tenant.default,
join: {
from: 'subscription_plan_subscriptions.tenantId',
to: 'tenants.id',
},
},
/**
* Plan description belongs to plan.
*/
plan: {
relation: Model.BelongsToOneRelation,
modelClass: Plan.default,
join: {
from: 'subscription_plan_subscriptions.planId',
to: 'subscription_plans.id',
},
},
};
}
/**
* Check if subscription is active.
* @return {Boolean}
*/
active() {
return !this.ended() || this.onTrial();
}
/**
* Check if subscription is inactive.
* @return {Boolean}
*/
inactive() {
return !this.active();
}
/**
* Check if subscription period has ended.
* @return {Boolean}
*/
ended() {
return this.endsAt ? moment().isAfter(this.endsAt) : false;
}
/**
* Check if subscription is currently on trial.
* @return {Boolean}
*/
onTrial() {
return this.trailEndsAt ? moment().isAfter(this.trailEndsAt) : false;
}
/**
* Set new period from the given details.
* @param {string} invoiceInterval
* @param {number} invoicePeriod
* @param {string} start
*
* @return {Object}
*/
static setNewPeriod(invoiceInterval, invoicePeriod, start) {
const period = new SubscriptionPeriod(
invoiceInterval,
invoicePeriod,
start,
);
const startsAt = period.getStartDate();
const endsAt = period.getEndDate();
return { startsAt, endsAt };
}
/**
* Renews subscription period.
* @Promise
*/
renew(invoiceInterval, invoicePeriod) {
const { startsAt, endsAt } = PlanSubscription.setNewPeriod(
invoiceInterval,
invoicePeriod,
);
return this.$query().update({ startsAt, endsAt });
}
}

View File

@@ -0,0 +1,19 @@
import { Container } from 'typedi';
import BaseModel from 'models/Model';
export default class SystemModel extends BaseModel{
/**
* Loging all system database queries.
* @param {...any} args
*/
static query(...args) {
const Logger = Container.get('logger');
return super.query(...args).onBuildKnex(knexQueryBuilder => {
knexQueryBuilder.on('query', queryData => {
Logger.info(`[query][system] ${queryData.sql}`, {
bindings: queryData.bindings,
});
});
});
}
}

View File

@@ -0,0 +1,85 @@
import { Model } from 'objection';
import bcrypt from 'bcryptjs';
import SystemModel from '@/system/models/SystemModel';
import SoftDeleteQueryBuilder from '@/collection/SoftDeleteQueryBuilder';
export default class SystemUser extends SystemModel {
/**
* Table name.
*/
static get tableName() {
return 'users';
}
/**
* Soft delete query builder.
*/
static get QueryBuilder() {
return SoftDeleteQueryBuilder;
}
/**
* Timestamps columns.
*/
get timestamps() {
return ['createdAt', 'updatedAt'];
}
/**
* Virtual attributes.
*/
static get virtualAttributes() {
return ['fullName', 'isDeleted', 'isInviteAccepted'];
}
/**
*
*/
get isDeleted() {
return !!this.deletedAt;
}
/**
*
*/
get isInviteAccepted() {
return !!this.inviteAcceptedAt;
}
/**
* Full name attribute.
*/
get fullName() {
return (this.firstName + ' ' + this.lastName).trim();
}
/**
* Relationship mapping.
*/
static get relationMappings() {
const Tenant = require('system/models/Tenant');
return {
/**
* System user may belongs to tenant model.
*/
tenant: {
relation: Model.BelongsToOneRelation,
modelClass: Tenant.default,
join: {
from: 'users.tenantId',
to: 'tenants.id',
},
},
};
}
/**
* Verify the password of the user.
* @param {String} password - The given password.
* @return {Boolean}
*/
verifyPassword(password) {
return bcrypt.compareSync(password, this.password);
}
}

View File

@@ -0,0 +1,226 @@
import moment from 'moment';
import { Model } from 'objection';
import uniqid from 'uniqid';
import SubscriptionPeriod from '@/services/Subscription/SubscriptionPeriod';
import BaseModel from 'models/Model';
import TenantMetadata from './TenantMetadata';
import PlanSubscription from './Subscriptions/PlanSubscription';
export default class Tenant extends BaseModel {
/**
* Table name.
*/
static get tableName() {
return 'tenants';
}
/**
* Timestamps columns.
*/
get timestamps() {
return ['createdAt', 'updatedAt'];
}
/**
* Virtual attributes.
*/
static get virtualAttributes() {
return ['isReady', 'isBuildRunning', 'isUpgradeRunning'];
}
/**
* Tenant is ready.
*/
get isReady() {
return !!(this.initializedAt && this.seededAt);
}
/**
* Detarimes the tenant whether is build currently running.
*/
get isBuildRunning() {
return !!this.buildJobId;
}
/**
* Detarmines the tenant whether is upgrade currently running.
*/
get isUpgradeRunning() {
return !!this.upgradeJobId;
}
/**
* Query modifiers.
*/
static modifiers() {
return {
subscriptions(builder) {
builder.withGraphFetched('subscriptions');
},
};
}
/**
* Relations mappings.
*/
static get relationMappings() {
const PlanSubscription = require('./Subscriptions/PlanSubscription');
const TenantMetadata = require('./TenantMetadata');
return {
subscriptions: {
relation: Model.HasManyRelation,
modelClass: PlanSubscription.default,
join: {
from: 'tenants.id',
to: 'subscription_plan_subscriptions.tenantId',
},
},
metadata: {
relation: Model.HasOneRelation,
modelClass: TenantMetadata.default,
join: {
from: 'tenants.id',
to: 'tenants_metadata.tenantId',
},
},
};
}
/**
* Retrieve the subscribed plans ids.
* @return {number[]}
*/
async subscribedPlansIds() {
const { subscriptions } = this;
return chain(subscriptions).map('planId').unq();
}
/**
*
* @param {*} planId
* @param {*} invoiceInterval
* @param {*} invoicePeriod
* @param {*} subscriptionSlug
* @returns
*/
newSubscription(planId, invoiceInterval, invoicePeriod, subscriptionSlug) {
return Tenant.newSubscription(
this.id,
planId,
invoiceInterval,
invoicePeriod,
subscriptionSlug,
);
}
/**
* Records a new subscription for the associated tenant.
*/
static newSubscription(
tenantId,
planId,
invoiceInterval,
invoicePeriod,
subscriptionSlug
) {
const period = new SubscriptionPeriod(invoiceInterval, invoicePeriod);
return PlanSubscription.query().insert({
tenantId,
slug: subscriptionSlug,
planId,
startsAt: period.getStartDate(),
endsAt: period.getEndDate(),
});
}
/**
* Creates a new tenant with random organization id.
*/
static createWithUniqueOrgId(uniqId) {
const organizationId = uniqid() || uniqId;
return this.query().insert({ organizationId });
}
/**
* Mark as seeded.
* @param {number} tenantId
*/
static markAsSeeded(tenantId) {
const seededAt = moment().toMySqlDateTime();
return this.query().update({ seededAt }).where({ id: tenantId });
}
/**
* Mark the the given organization as initialized.
* @param {string} organizationId
*/
static markAsInitialized(tenantId) {
const initializedAt = moment().toMySqlDateTime();
return this.query().update({ initializedAt }).where({ id: tenantId });
}
/**
* Marks the given tenant as built.
*/
static markAsBuilt(tenantId) {
const builtAt = moment().toMySqlDateTime();
return this.query().update({ builtAt }).where({ id: tenantId });
}
/**
* Marks the given tenant as built.
*/
static markAsBuilding(tenantId, buildJobId) {
return this.query().update({ buildJobId }).where({ id: tenantId });
}
/**
* Marks the given tenant as built.
*/
static markAsBuildCompleted(tenantId) {
return this.query().update({ buildJobId: null }).where({ id: tenantId });
}
/**
* Marks the given tenant as upgrading.
* @param {number} tenantId
* @param {string} upgradeJobId
* @returns
*/
static markAsUpgrading(tenantId, upgradeJobId) {
return this.query().update({ upgradeJobId }).where({ id: tenantId });
}
/**
* Markes the given tenant as upgraded.
* @param {number} tenantId
* @returns
*/
static markAsUpgraded(tenantId) {
return this.query().update({ upgradeJobId: null }).where({ id: tenantId });
}
/**
* Saves the metadata of the given tenant.
*/
static async saveMetadata(tenantId, metadata) {
const foundMetadata = await TenantMetadata.query().findOne({ tenantId });
const updateOrInsert = foundMetadata ? 'update' : 'insert';
return TenantMetadata.query()
[updateOrInsert]({
tenantId,
...metadata,
})
.where({ tenantId });
}
/**
* Saves the metadata of the tenant.
*/
saveMetadata(metadata) {
return Tenant.saveMetadata(this.id, metadata);
}
}

View File

@@ -0,0 +1,10 @@
import BaseModel from 'models/Model';
export default class TenantMetadata extends BaseModel {
/**
* Table name.
*/
static get tableName() {
return 'tenants_metadata';
}
}

View File

@@ -0,0 +1,22 @@
import Plan from './Subscriptions/Plan';
import PlanFeature from './Subscriptions/PlanFeature';
import PlanSubscription from './Subscriptions/PlanSubscription';
import License from './Subscriptions/License';
import Tenant from './Tenant';
import TenantMetadata from './TenantMetadata';
import SystemUser from './SystemUser';
import PasswordReset from './PasswordReset';
import Invite from './Invite';
export {
Plan,
PlanFeature,
PlanSubscription,
License,
Tenant,
TenantMetadata,
SystemUser,
PasswordReset,
Invite,
}

View File

@@ -0,0 +1,26 @@
import SystemRepository from '@/system/repositories/SystemRepository';
import { PlanSubscription } from '@/system/models';
export default class SubscriptionRepository extends SystemRepository {
/**
* Gets the repository's model.
*/
get model() {
return PlanSubscription.bindKnex(this.knex);
}
/**
* Retrieve subscription from a given slug in specific tenant.
* @param {string} slug
* @param {number} tenantId
*/
getBySlugInTenant(slug: string, tenantId: number) {
const cacheKey = this.getCacheKey('getBySlugInTenant', slug, tenantId);
return this.cache.get(cacheKey, () => {
return PlanSubscription.query()
.findOne('slug', slug)
.where('tenant_id', tenantId);
});
}
}

View File

@@ -0,0 +1,5 @@
import CachableRepository from "repositories/CachableRepository";
export default class SystemRepository extends CachableRepository {
}

View File

@@ -0,0 +1,101 @@
import moment from 'moment';
import SystemRepository from '@/system/repositories/SystemRepository';
import { SystemUser } from '@/system/models';
import { ISystemUser } from '@/interfaces';
export default class SystemUserRepository extends SystemRepository {
/**
* Gets the repository's model.
*/
get model() {
return SystemUser.bindKnex(this.knex);
}
/**
* Finds system user by crediential.
* @param {string} crediential - Phone number or email.
* @return {ISystemUser}
* @return {Promise<ISystemUser>}
*/
findByCrediential(crediential: string): Promise<ISystemUser> {
const cacheKey = this.getCacheKey('findByCrediential', crediential);
return this.cache.get(cacheKey, () => {
return this.model.query()
.findOne('email', crediential)
.orWhere('phone_number', crediential);
});
}
/**
* Retrieve user by id and tenant id.
* @param {number} userId - User id.
* @param {number} tenantId - Tenant id.
* @return {Promise<ISystemUser>}
*/
findOneByIdAndTenant(userId: number, tenantId: number): Promise<ISystemUser> {
const cacheKey = this.getCacheKey('findOneByIdAndTenant', userId, tenantId);
return this.cache.get(cacheKey, () => {
return this.model.query()
.findOne({ id: userId, tenant_id: tenantId });
});
}
/**
* Retrieve system user details by the given email.
* @param {string} email - Email
* @return {Promise<ISystemUser>}
*/
findOneByEmail(email: string): Promise<ISystemUser> {
const cacheKey = this.getCacheKey('findOneByEmail', email);
return this.cache.get(cacheKey, () => {
return this.model.query().findOne('email', email);
});
}
/**
* Retrieve user by phone number.
* @param {string} phoneNumber - Phone number
* @return {Promise<ISystemUser>}
*/
findOneByPhoneNumber(phoneNumber: string): Promise<ISystemUser> {
const cacheKey = this.getCacheKey('findOneByPhoneNumber', phoneNumber);
return this.cache.get(cacheKey, () => {
return this.model.query()
.findOne('phoneNumber', phoneNumber);
});
}
/**
* Patches the last login date to the given system user.
* @param {number} userId
* @return {Promise<void>}
*/
patchLastLoginAt(userId: number): Promise<void> {
return super.update(
{ last_login_at: moment().toMySqlDateTime() },
{ id: userId }
);
}
/**
* Activate user by the given id.
* @param {number} userId - User id.
* @return {Promise<void>}
*/
activateById(userId: number): Promise<void> {
return super.update({ active: 1 }, { id: userId });
}
/**
* Inactivate user by the given id.
* @param {number} userId - User id.
* @return {Promise<void>}
*/
inactivateById(userId: number): Promise<void> {
return super.update({ active: 0 }, { id: userId });
}
}

View File

@@ -0,0 +1,43 @@
import moment from "moment";
import uniqid from 'uniqid';
import SystemRepository from "./SystemRepository";
import { Tenant } from "@/system/models";
import { ITenant } from '@/interfaces';
export default class TenantRepository extends SystemRepository {
/**
* Gets the repository's model.
*/
get model() {
return Tenant.bindKnex(this.knex);
}
/**
* Creates a new tenant with random organization id.
* @return {ITenant}
*/
createWithUniqueOrgId(uniqId?: string): Promise<ITenant>{
const organizationId = uniqid() || uniqId;
return super.create({ organizationId });
}
/**
* Mark as seeded.
* @param {number} tenantId
*/
markAsSeeded(tenantId: number) {
return super.update({
seededAt: moment().toMySqlDateTime(),
}, { id: tenantId })
}
/**
* Mark the the given organization as initialized.
* @param {string} organizationId
*/
markAsInitialized(tenantId: number) {
return super.update({
initializedAt: moment().toMySqlDateTime(),
}, { id: tenantId });
}
}

View File

@@ -0,0 +1,9 @@
import SystemUserRepository from '@/system/repositories/SystemUserRepository';
import SubscriptionRepository from '@/system/repositories/SubscriptionRepository';
import TenantRepository from '@/system/repositories/TenantRepository';
export {
SystemUserRepository,
SubscriptionRepository,
TenantRepository,
};

View File

@@ -0,0 +1,66 @@
exports.seed = (knex) => {
// Deletes ALL existing entries
return knex('subscription_plans').del()
.then(() => {
// Inserts seed entries
return knex('subscription_plans').insert([
{
name: 'Essentials',
slug: 'essentials-monthly',
price: 100,
active: true,
currency: 'LYD',
trial_period: 7,
trial_interval: 'days',
},
{
name: 'Essentials',
slug: 'essentials-yearly',
price: 1200,
active: true,
currency: 'LYD',
trial_period: 12,
trial_interval: 'months',
},
{
name: 'Pro',
slug: 'pro-monthly',
price: 200,
active: true,
currency: 'LYD',
trial_period: 1,
trial_interval: 'months',
},
{
name: 'Pro',
slug: 'pro-yearly',
price: 500,
active: true,
currency: 'LYD',
invoice_period: 12,
invoice_interval: 'month',
index: 2,
},
{
name: 'Plus',
slug: 'plus-monthly',
price: 200,
active: true,
currency: 'LYD',
trial_period: 1,
trial_interval: 'months',
},
{
name: 'Plus',
slug: 'plus-yearly',
price: 500,
active: true,
currency: 'LYD',
invoice_period: 12,
invoice_interval: 'month',
index: 2,
},
]);
});
};