0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-01-06 20:40:08 -05:00
logto/packages/core/src/queries/user.ts

199 lines
5.7 KiB
TypeScript
Raw Normal View History

2022-10-21 00:14:17 -05:00
import type { User, CreateUser } from '@logto/schemas';
import { Users, UserRole } from '@logto/schemas';
import type { OmitAutoSetFields } from '@logto/shared';
import { conditionalSql, convertToIdentifiers } from '@logto/shared';
import { sql } from 'slonik';
2021-08-29 22:30:54 -05:00
2022-11-21 03:38:24 -05:00
import { buildUpdateWhere } from '#src/database/update-where.js';
import envSet from '#src/env-set/index.js';
import { DeletionError } from '#src/errors/SlonikError/index.js';
const { table, fields } = convertToIdentifiers(Users);
export const findUserByUsername = async (username: string) =>
envSet.pool.maybeOne<User>(sql`
select ${sql.join(Object.values(fields), sql`,`)}
from ${table}
where ${fields.username}=${username}
`);
export const findUserByEmail = async (email: string) =>
envSet.pool.maybeOne<User>(sql`
select ${sql.join(Object.values(fields), sql`,`)}
from ${table}
where lower(${fields.primaryEmail})=lower(${email})
`);
export const findUserByPhone = async (phone: string) =>
envSet.pool.maybeOne<User>(sql`
select ${sql.join(Object.values(fields), sql`,`)}
from ${table}
where ${fields.primaryPhone}=${phone}
`);
export const findUserById = async (id: string) =>
envSet.pool.one<User>(sql`
select ${sql.join(Object.values(fields), sql`,`)}
from ${table}
where ${fields.id}=${id}
`);
2021-07-04 04:41:46 -05:00
export const findUserByIdentity = async (target: string, userId: string) =>
envSet.pool.maybeOne<User>(
2022-02-11 02:19:18 -05:00
sql`
select ${sql.join(Object.values(fields), sql`,`)}
from ${table}
where ${fields.identities}::json#>>'{${sql.identifier([target])},userId}' = ${userId}
2022-02-11 02:19:18 -05:00
`
);
export const hasUser = async (username: string, excludeUserId?: string) =>
envSet.pool.exists(sql`
select ${fields.id}
from ${table}
where ${fields.username}=${username}
${conditionalSql(excludeUserId, (id) => sql`and ${fields.id}<>${id}`)}
`);
2021-07-04 04:41:46 -05:00
export const hasUserWithId = async (id: string) =>
envSet.pool.exists(sql`
select ${fields.id}
from ${table}
where ${fields.id}=${id}
`);
2021-07-04 04:41:46 -05:00
export const hasUserWithEmail = async (email: string, excludeUserId?: string) =>
envSet.pool.exists(sql`
select ${fields.primaryEmail}
from ${table}
where lower(${fields.primaryEmail})=lower(${email})
${conditionalSql(excludeUserId, (id) => sql`and ${fields.id}<>${id}`)}
`);
export const hasUserWithPhone = async (phone: string, excludeUserId?: string) =>
envSet.pool.exists(sql`
select ${fields.primaryPhone}
from ${table}
where ${fields.primaryPhone}=${phone}
${conditionalSql(excludeUserId, (id) => sql`and ${fields.id}<>${id}`)}
`);
export const hasUserWithIdentity = async (target: string, userId: string) =>
envSet.pool.exists(
2022-02-11 02:19:18 -05:00
sql`
select ${fields.id}
from ${table}
where ${fields.identities}::json#>>'{${sql.identifier([target])},userId}' = ${userId}
2022-02-11 02:19:18 -05:00
`
);
const buildUserSearchConditionSql = (search: string, isCaseSensitive = false) => {
const searchFields = [fields.primaryEmail, fields.primaryPhone, fields.username, fields.name];
2022-02-16 02:55:08 -05:00
return sql`${sql.join(
searchFields.map(
(filedName) =>
sql`${filedName} ${isCaseSensitive ? sql`like` : sql`ilike`} ${'%' + search + '%'}`
),
sql` or `
)}`;
};
2022-02-16 02:55:08 -05:00
const buildUserConditions = (
search?: string,
hideAdminUser?: boolean,
isCaseSensitive?: boolean
) => {
if (hideAdminUser) {
return sql`
where not (${fields.roleNames}::jsonb?${UserRole.Admin})
${conditionalSql(
search,
(search) => sql`and (${buildUserSearchConditionSql(search, isCaseSensitive)})`
)}
`;
}
return sql`
${conditionalSql(
search,
(search) => sql`where ${buildUserSearchConditionSql(search, isCaseSensitive)}`
)}
`;
};
export const countUsers = async (
search?: string,
hideAdminUser?: boolean,
isCaseSensitive?: boolean
) =>
envSet.pool.one<{ count: number }>(sql`
select count(*)
from ${table}
${buildUserConditions(search, hideAdminUser, isCaseSensitive)}
`);
export const findUsers = async (
limit: number,
offset: number,
search?: string,
hideAdminUser?: boolean,
isCaseSensitive?: boolean
) =>
envSet.pool.any<User>(
sql`
select ${sql.join(Object.values(fields), sql`,`)}
from ${table}
${buildUserConditions(search, hideAdminUser, isCaseSensitive)}
order by ${fields.createdAt} desc
limit ${limit}
offset ${offset}
`
);
2021-11-17 22:26:34 -05:00
const updateUser = buildUpdateWhere<CreateUser, User>(Users, true);
2021-11-17 22:26:34 -05:00
export const updateUserById = async (
id: string,
set: Partial<OmitAutoSetFields<CreateUser>>,
jsonbMode: 'replace' | 'merge' = 'merge'
) => updateUser({ set, where: { id }, jsonbMode });
2021-11-17 22:26:34 -05:00
export const deleteUserById = async (id: string) => {
const { rowCount } = await envSet.pool.query(sql`
delete from ${table}
where ${fields.id}=${id}
`);
2022-01-27 06:26:34 -05:00
2021-11-17 22:26:34 -05:00
if (rowCount < 1) {
throw new DeletionError(Users.table, id);
2021-11-17 22:26:34 -05:00
}
};
export const deleteUserIdentity = async (userId: string, target: string) =>
envSet.pool.one<User>(sql`
update ${table}
set ${fields.identities}=${fields.identities}::jsonb-${target}
where ${fields.id}=${userId}
returning *
`);
export const hasActiveUsers = async () =>
envSet.pool.exists(sql`
select ${fields.id}
from ${table}
limit 1
`);
export const getDailyNewUserCountsByTimeInterval = async (
startTimeExclusive: number,
endTimeInclusive: number
) =>
envSet.pool.any<{ date: string; count: number }>(sql`
select date(${fields.createdAt}), count(*)
from ${table}
where ${fields.createdAt} > to_timestamp(${startTimeExclusive}::double precision / 1000)
and ${fields.createdAt} <= to_timestamp(${endTimeInclusive}::double precision / 1000)
group by date(${fields.createdAt})
`);