@@ -2,18 +2,33 @@ import { DailyTokenUsage } from '@logto/schemas';
22import { generateStandardId } from '@logto/shared' ;
33import type { CommonQueryMethods } from '@silverhand/slonik' ;
44import { sql } from '@silverhand/slonik' ;
5+ import { z } from 'zod' ;
56
67import { getUtcStartOfTheDay } from '#src/oidc/utils.js' ;
78import { convertToIdentifiers } from '#src/utils/sql.js' ;
89
910const { table, fields } = convertToIdentifiers ( DailyTokenUsage ) ;
1011const { fields : fieldsWithPrefix } = convertToIdentifiers ( DailyTokenUsage , true ) ;
1112
13+ export enum TokenUsageType {
14+ User = 'User' ,
15+ M2m = 'M2m' ,
16+ }
17+
18+ export const tokenUsageCountsGuard = z . object ( {
19+ totalUsage : z . number ( ) . nonnegative ( ) ,
20+ userTokenUsage : z . number ( ) . nonnegative ( ) ,
21+ m2mTokenUsage : z . number ( ) . nonnegative ( ) ,
22+ } ) ;
23+
24+ export type TokenUsageCounts = z . infer < typeof tokenUsageCountsGuard > ;
25+
1226export const createDailyTokenUsageQueries = ( pool : CommonQueryMethods ) => {
1327 /**
1428 * Record the token usage of the current date.
1529 *
1630 * @param date The current date.
31+ * @param options Options for recording token usage, including the type of token.
1732 * @returns The updated token usage of the current date.
1833 */
1934 /**
@@ -23,35 +38,59 @@ export const createDailyTokenUsageQueries = (pool: CommonQueryMethods) => {
2338 * If we were to use the pre-built query methods, completing this operation would
2439 * require two database requests:
2540 * 1. to request the record
26- * 2. to update it if the record exists, or insert a new one if it doesn’ t
41+ * 2. to update it if the record exists, or insert a new one if it doesn' t
2742 *
2843 * The approach we used allows us to accomplish the task within a single database query.
2944 */
30- const recordTokenUsage = async ( date : Date ) =>
31- // Insert a new record if not exists (with usage to be 1, since this
32- // should be the first token use of the day), otherwise increment the usage by 1.
33- pool . one < DailyTokenUsage > ( sql `
34- insert into ${ table } (${ fields . id } , ${ fields . date } , ${ fields . usage } )
35- values (${ generateStandardId ( ) } , to_timestamp(${ getUtcStartOfTheDay (
36- date
37- ) . getTime ( ) } ::double precision / 1000), 1)
38- on conflict (${ fields . date } , ${ fields . tenantId } ) do update set ${ fields . usage } = ${
39- fieldsWithPrefix . usage
40- } + 1
45+ const recordTokenUsage = async ( date : Date , { type } : { type : TokenUsageType } ) => {
46+ // For user tokens: increment both usage and user_token_usage
47+ // For M2M tokens: increment both usage and m2m_token_usage
48+ const userTokenIncrement =
49+ type === TokenUsageType . User
50+ ? sql `${ fieldsWithPrefix . userTokenUsage } + 1`
51+ : sql `${ fieldsWithPrefix . userTokenUsage } ` ;
52+ const m2mTokenIncrement =
53+ type === TokenUsageType . M2m
54+ ? sql `${ fieldsWithPrefix . m2mTokenUsage } + 1`
55+ : sql `${ fieldsWithPrefix . m2mTokenUsage } ` ;
56+
57+ return pool . one < DailyTokenUsage > ( sql `
58+ insert into ${ table } (
59+ ${ fields . id } ,
60+ ${ fields . date } ,
61+ ${ fields . usage } ,
62+ ${ fields . userTokenUsage } ,
63+ ${ fields . m2mTokenUsage }
64+ )
65+ values (
66+ ${ generateStandardId ( ) } ,
67+ to_timestamp(${ getUtcStartOfTheDay ( date ) . getTime ( ) } ::double precision / 1000),
68+ 1,
69+ ${ type === TokenUsageType . User ? 1 : 0 } ,
70+ ${ type === TokenUsageType . M2m ? 1 : 0 }
71+ )
72+ on conflict (${ fields . date } , ${ fields . tenantId } ) do update set
73+ ${ fields . usage } = ${ fieldsWithPrefix . usage } + 1,
74+ ${ fields . userTokenUsage } = ${ userTokenIncrement } ,
75+ ${ fields . m2mTokenUsage } = ${ m2mTokenIncrement }
4176 returning ${ sql . join ( Object . values ( fields ) , sql `, ` ) }
4277 ` ) ;
78+ } ;
4379
4480 const countTokenUsage = async ( { from, to } : { from : Date ; to : Date } ) => {
45- return pool . one < { tokenUsage : number } > ( sql `
46- select sum( ${ fields . usage } ) as token_usage
47- from ${ table }
48- where ${ fields . date } >= to_timestamp( ${ getUtcStartOfTheDay (
49- from
50- ) . getTime ( ) } ::double precision / 1000)
51- and ${ fields . date } < to_timestamp(${ getUtcStartOfTheDay (
52- to
81+ return pool . one < TokenUsageCounts > ( sql `
82+ select
83+ coalesce(sum( ${ fields . usage } ), 0) as total_usage,
84+ coalesce(sum( ${ fields . userTokenUsage } ), 0) as user_token_usage,
85+ coalesce(sum( ${ fields . m2mTokenUsage } ), 0) as m2m_token_usage
86+ from ${ table }
87+ where ${ fields . date } >= to_timestamp(${ getUtcStartOfTheDay (
88+ from
5389 ) . getTime ( ) } ::double precision / 1000)
54- ` ) ;
90+ and ${ fields . date } < to_timestamp(${ getUtcStartOfTheDay (
91+ to
92+ ) . getTime ( ) } ::double precision / 1000)
93+ ` ) ;
5594 } ;
5695
5796 return {
0 commit comments