Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export {
AJV_PATTERN_KEYS_NOT_OBJECT,
US_ERRORS,
CURRENT_TIMESTAMP,
OrderBy,
DEFAULT_QUERY_TIMEOUT,
EXTENDED_QUERY_TIMEOUT,
ALL_COLUMNS,
Expand Down
14 changes: 14 additions & 0 deletions api/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,17 @@ export {
JoinedEntryRevisionTenant,
JoinedEntryRevisionTenantColumns,
} from '../src/db/presentations/joined-entry-revision-tenant';

export {
LicenseLimit,
LicenseLimitColumn,
LicenseLimitColumnRaw,
} from '../src/db/models/new/license-limit';
export {LicenseLimitWithStartedInFuture} from '../src/db/models/new/license-limit/presentations';
export {
LicenseAssignment,
LicenseAssignmentColumnRaw,
LicenseAssignmentColumn,
} from '../src/db/models/new/license-assignment';
export {LicenseType} from '../src/db/models/new/license-assignment/types';
export {LicenseAssignmentWithIsActive} from '../src/db/models/new/license-assignment/presentations';
1 change: 1 addition & 0 deletions api/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export {
isTenantIdWithOrgId,
getOrgIdFromTenantId,
makeTenantIdFromOrgId,
mapValuesToSnakeCase,
} from '../src/utils';

export {normalizedEnv} from '../src/utils/normalized-env';
Expand Down
4 changes: 4 additions & 0 deletions src/const/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,10 @@ export const DEFAULT_PAGE_SIZE = 1000;
export const DEFAULT_PAGE = 0;

export const CURRENT_TIMESTAMP = 'CURRENT_TIMESTAMP';
export enum OrderBy {
Asc = 'ASC',
Desc = 'DESC',
}

export const APP_NAME = 'united-storage';

Expand Down
52 changes: 52 additions & 0 deletions src/db/migrations/20251019110620_add_table_license_limits.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import type {Knex} from 'knex';

export async function up(knex: Knex): Promise<void> {
return knex.raw(`
CREATE TABLE license_limits (
license_limit_id BIGINT NOT NULL PRIMARY KEY DEFAULT get_id(),
tenant_id TEXT NOT NULL DEFAULT 'common' REFERENCES tenants (tenant_id) ON UPDATE CASCADE ON DELETE CASCADE,
limit_value INT NOT NULL,
started_at TIMESTAMPTZ NOT NULL,
created_by TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_by TEXT NOT NULL,
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX license_limits_tenant_id_started_at_idx ON license_limits(tenant_id, started_at);

CREATE TYPE LICENSE_TYPE AS ENUM ('creator', 'visitor');

CREATE TABLE license_assignments (
license_assignment_id BIGINT NOT NULL PRIMARY KEY DEFAULT get_id(),
tenant_id TEXT NOT NULL DEFAULT 'common' REFERENCES tenants (tenant_id) ON UPDATE CASCADE ON DELETE CASCADE,
user_id TEXT NOT NULL,
license_type LICENSE_TYPE NOT NULL,
expires_at TIMESTAMPTZ,
created_by TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_by TEXT NOT NULL,
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE UNIQUE INDEX license_assignments_tenant_id_user_id_idx ON license_assignments(tenant_id, user_id);
CREATE INDEX license_assignments_expires_at_idx ON license_assignments(expires_at);
CREATE INDEX license_assignments_created_at_idx ON license_assignments(created_at);
CREATE INDEX license_assignments_updated_at_idx ON license_assignments(updated_at);

`);
}

export async function down(knex: Knex): Promise<void> {
return knex.raw(`
DROP INDEX license_assignments_updated_at_idx;
DROP INDEX license_assignments_created_at_idx;
DROP INDEX license_assignments_expires_at_idx;
DROP INDEX license_assignments_tenant_id_user_id_idx;
DROP TABLE license_assignments;
DROP TYPE LICENSE_TYPE;

DROP INDEX license_limits_tenant_id_started_at_idx;
DROP TABLE license_limits;
`);
}
38 changes: 38 additions & 0 deletions src/db/models/new/license-assignment/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {Model} from '../../..';
import {mapValuesToSnakeCase} from '../../../../utils';

import {LicenseType} from './types';

export const LicenseAssignmentColumn = {
LicenseAssignmentId: 'licenseAssignmentId',
TenantId: 'tenantId',
UserId: 'userId',
LicenseType: 'licenseType',
ExpiresAt: 'expiresAt',
CreatedBy: 'createdBy',
CreatedAt: 'createdAt',
UpdatedBy: 'updatedBy',
UpdatedAt: 'updatedAt',
} as const;

export const LicenseAssignmentColumnRaw = mapValuesToSnakeCase(LicenseAssignmentColumn);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool :)


export class LicenseAssignment extends Model {
static get tableName() {
return 'license_assignments';
}

static get idColumn() {
return LicenseAssignmentColumn.LicenseAssignmentId;
}

[LicenseAssignmentColumn.LicenseAssignmentId]!: string;
[LicenseAssignmentColumn.TenantId]!: string;
[LicenseAssignmentColumn.UserId]!: string;
[LicenseAssignmentColumn.LicenseType]!: `${LicenseType}`;
[LicenseAssignmentColumn.ExpiresAt]!: Nullable<string>;
[LicenseAssignmentColumn.CreatedBy]!: string;
[LicenseAssignmentColumn.CreatedAt]!: string;
[LicenseAssignmentColumn.UpdatedBy]!: string;
[LicenseAssignmentColumn.UpdatedAt]!: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './license-assignment-with-is-active';
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {QueryBuilder, TransactionOrKnex, raw} from 'objection';

import {CURRENT_TIMESTAMP} from '../../../../../const';
import {LicenseAssignment, LicenseAssignmentColumnRaw} from '../index';

export class LicenseAssignmentWithIsActive extends LicenseAssignment {
protected static get selectedColumns() {
return [
'*',
raw(`?? > ${CURRENT_TIMESTAMP} OR ?? IS NULL`, [
LicenseAssignmentColumnRaw.ExpiresAt,
LicenseAssignmentColumnRaw.ExpiresAt,
]).as('is_active'),
];
}

static getSelectQuery(trx: TransactionOrKnex) {
const query = LicenseAssignment.query(trx).select(this.selectedColumns);

return query as QueryBuilder<
LicenseAssignmentWithIsActive,
LicenseAssignmentWithIsActive[]
>;
}

isActive!: boolean;
}
4 changes: 4 additions & 0 deletions src/db/models/new/license-assignment/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum LicenseType {
Creator = 'creator',
Visitor = 'visitor',
}
34 changes: 34 additions & 0 deletions src/db/models/new/license-limit/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {Model} from '../../..';
import {mapValuesToSnakeCase} from '../../../../utils';

export const LicenseLimitColumn = {
LicenseLimitId: 'licenseLimitId',
TenantId: 'tenantId',
LimitValue: 'limitValue',
StartedAt: 'startedAt',
CreatedBy: 'createdBy',
CreatedAt: 'createdAt',
UpdatedBy: 'updatedBy',
UpdatedAt: 'updatedAt',
} as const;

export const LicenseLimitColumnRaw = mapValuesToSnakeCase(LicenseLimitColumn);

export class LicenseLimit extends Model {
static get tableName() {
return 'license_limits';
}

static get idColumn() {
return LicenseLimitColumn.LicenseLimitId;
}

[LicenseLimitColumn.LicenseLimitId]!: string;
[LicenseLimitColumn.TenantId]!: string;
[LicenseLimitColumn.LimitValue]!: number;
[LicenseLimitColumn.StartedAt]!: string;
[LicenseLimitColumn.CreatedBy]!: string;
[LicenseLimitColumn.CreatedAt]!: string;
[LicenseLimitColumn.UpdatedBy]!: string;
[LicenseLimitColumn.UpdatedAt]!: string;
}
1 change: 1 addition & 0 deletions src/db/models/new/license-limit/presentations/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './license-limit-with-started-in-future';
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {QueryBuilder, TransactionOrKnex, raw} from 'objection';

import {CURRENT_TIMESTAMP} from '../../../../../const';
import {LicenseLimit, LicenseLimitColumnRaw} from '../index';

export class LicenseLimitWithStartedInFuture extends LicenseLimit {
protected static get selectedColumns() {
return [
'*',
raw(`?? > ${CURRENT_TIMESTAMP}`, [LicenseLimitColumnRaw.StartedAt]).as(
'started_in_future',
),
];
}

static getSelectQuery(trx: TransactionOrKnex) {
const query = LicenseLimit.query(trx).select(this.selectedColumns);

return query as QueryBuilder<
LicenseLimitWithStartedInFuture,
LicenseLimitWithStartedInFuture[]
>;
}

startedInFuture!: boolean;
}
18 changes: 18 additions & 0 deletions src/utils/cases.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
type SnakeCase<S extends string> = S extends `${infer T}${infer U}`
? `${T extends Capitalize<T> ? '_' : ''}${Lowercase<T>}${SnakeCase<U>}`
: S;

type MapToSnakeCase<T extends Record<string, string>> = {
[K in keyof T]: SnakeCase<T[K]>;
};

const toSnakeCase = (str: string): string =>
str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);

export const mapValuesToSnakeCase = <T extends Record<string, string>>(
obj: T,
): MapToSnakeCase<T> => {
return Object.fromEntries(
Object.entries(obj).map(([key, value]) => [key, toSnakeCase(value)]),
) as MapToSnakeCase<T>;
};
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from './user';
export * from './validation';
export * from './tenant';
export * from './promise';
export * from './cases';
Loading