feat: implement automation feature

This commit is contained in:
Rotimi Best
2026-03-18 23:39:12 +01:00
parent 576d0366de
commit ef4424e267
121 changed files with 4544 additions and 1055 deletions
@@ -1,6 +1,6 @@
import * as schema from '@db/schema';
import { and, desc, eq, isNull } from 'drizzle-orm';
import { and, count, desc, eq, isNull } from 'drizzle-orm';
import { db } from '@db/drizzle';
import type { TNewOrganizationApiKey, TOrganizationApiKey, TOrganizationApiKeyType } from '@db/types';
@@ -42,6 +42,29 @@ export const listOrganizationApiKeys = async (
}
};
export const countActiveOrganizationApiKeys = async (
organizationId: string,
type: TOrganizationApiKeyType
): Promise<number> => {
try {
const [row] = await db
.select({ total: count() })
.from(schema.organizationApiKey)
.where(
and(
eq(schema.organizationApiKey.organizationId, organizationId),
eq(schema.organizationApiKey.type, type),
isNull(schema.organizationApiKey.revokedAt)
)
);
return Number(row?.total ?? 0);
} catch (error) {
console.error('countActiveOrganizationApiKeys error:', error);
throw new Error('Failed to count active organization API keys');
}
};
export const getOrganizationApiKeyById = async (
organizationId: string,
keyId: string
@@ -0,0 +1,124 @@
import * as schema from '@db/schema';
import { and, count, desc, eq, gte, sql } from 'drizzle-orm';
import { db } from '@db/drizzle';
import type { TAutomationUsageCategory, TNewOrganizationAutomationUsage, TOrganizationApiKeyType } from '@db/types';
export const createOrganizationAutomationUsage = async (data: TNewOrganizationAutomationUsage) => {
try {
const [row] = await db.insert(schema.organizationAutomationUsage).values(data).returning();
if (!row) {
throw new Error('Failed to create organization automation usage');
}
return row;
} catch (error) {
console.error('createOrganizationAutomationUsage error:', error);
throw new Error('Failed to create organization automation usage');
}
};
export const countOrganizationAutomationUsageSinceByKey = async (
organizationApiKeyId: string,
category: TAutomationUsageCategory,
since: string
): Promise<number> => {
try {
const [row] = await db
.select({ total: count() })
.from(schema.organizationAutomationUsage)
.where(
and(
eq(schema.organizationAutomationUsage.organizationApiKeyId, organizationApiKeyId),
eq(schema.organizationAutomationUsage.category, category),
gte(schema.organizationAutomationUsage.createdAt, since)
)
);
return Number(row?.total ?? 0);
} catch (error) {
console.error('countOrganizationAutomationUsageSinceByKey error:', error);
throw new Error('Failed to count organization automation usage by key');
}
};
export const countOrganizationAutomationUsageSince = async (
organizationId: string,
type: TOrganizationApiKeyType,
category: TAutomationUsageCategory,
since: string
): Promise<number> => {
try {
const [row] = await db
.select({ total: count() })
.from(schema.organizationAutomationUsage)
.where(
and(
eq(schema.organizationAutomationUsage.organizationId, organizationId),
eq(schema.organizationAutomationUsage.type, type),
eq(schema.organizationAutomationUsage.category, category),
gte(schema.organizationAutomationUsage.createdAt, since)
)
);
return Number(row?.total ?? 0);
} catch (error) {
console.error('countOrganizationAutomationUsageSince error:', error);
throw new Error('Failed to count organization automation usage');
}
};
export const getOrganizationAutomationCreditsUsedSince = async (
organizationId: string,
type: TOrganizationApiKeyType,
since: string
): Promise<number> => {
try {
const [row] = await db
.select({
total: sql<number>`COALESCE(SUM(${schema.organizationAutomationUsage.creditsConsumed}), 0)`
})
.from(schema.organizationAutomationUsage)
.where(
and(
eq(schema.organizationAutomationUsage.organizationId, organizationId),
eq(schema.organizationAutomationUsage.type, type),
gte(schema.organizationAutomationUsage.createdAt, since)
)
);
return Number(row?.total ?? 0);
} catch (error) {
console.error('getOrganizationAutomationCreditsUsedSince error:', error);
throw new Error('Failed to get organization automation credits used');
}
};
export const listRecentOrganizationAutomationUsage = async (
organizationId: string,
type: TOrganizationApiKeyType,
limit = 10
) => {
try {
return await db
.select({
action: schema.organizationAutomationUsage.action,
creditsConsumed: schema.organizationAutomationUsage.creditsConsumed,
createdAt: schema.organizationAutomationUsage.createdAt
})
.from(schema.organizationAutomationUsage)
.where(
and(
eq(schema.organizationAutomationUsage.organizationId, organizationId),
eq(schema.organizationAutomationUsage.type, type)
)
)
.orderBy(desc(schema.organizationAutomationUsage.createdAt))
.limit(limit);
} catch (error) {
console.error('listRecentOrganizationAutomationUsage error:', error);
throw new Error('Failed to list recent organization automation usage');
}
};
@@ -2,3 +2,4 @@ export * from './organization';
export * from './quiz';
export * from './invite';
export * from './automation-key';
export * from './automation-usage';
@@ -677,3 +677,13 @@ export const getOrganizationPlanStatus = async (orgId: string) => {
return result;
};
export const getActiveOrganizationPlan = async (orgId: string) => {
const [plan] = await db
.select()
.from(schema.organizationPlan)
.where(and(eq(schema.organizationPlan.orgId, orgId), eq(schema.organizationPlan.isActive, true)))
.limit(1);
return plan ?? null;
};