0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-01-06 20:40:08 -05:00

refactor(core,console,schemas): remove logDto (#2889)

This commit is contained in:
simeng-li 2023-01-10 13:57:24 +08:00 committed by GitHub
parent 39f15acb40
commit 3ff6554c08
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 48 additions and 338 deletions

View file

@ -1,5 +1,5 @@
import type { Log } from '@logto/schemas';
import { LogResult } from '@logto/schemas';
import type { LogDto } from '@logto/schemas/lib/types/log-legacy';
import { conditional, conditionalString } from '@silverhand/essentials';
import classNames from 'classnames';
import { useTranslation } from 'react-i18next';
@ -45,9 +45,7 @@ const AuditLogTable = ({ userId, className }: Props) => {
]
.filter(Boolean)
.join('&');
const { data, error, mutate } = useSWR<[LogDto[], number], RequestError>(
`/api/logs?${queryString}`
);
const { data, error, mutate } = useSWR<[Log[], number], RequestError>(`/api/logs?${queryString}`);
const isLoading = !data && !error;
const navigate = useNavigate();
const [logs, totalCount] = data ?? [];

View file

@ -1,5 +1,4 @@
import type { User } from '@logto/schemas';
import type { LogDto } from '@logto/schemas/lib/types/log-legacy';
import type { User, Log } from '@logto/schemas';
import classNames from 'classnames';
import { useTranslation } from 'react-i18next';
import { useLocation, useParams } from 'react-router-dom';
@ -31,7 +30,7 @@ const AuditLogDetails = () => {
const { id, logId } = useParams();
const { pathname } = useLocation();
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const { data, error } = useSWR<LogDto, RequestError>(logId && `/api/logs/${logId}`);
const { data, error } = useSWR<Log, RequestError>(logId && `/api/logs/${logId}`);
const { data: userData } = useSWR<User, RequestError>(id && `/api/users/${id}`);
const isLoading = !data && !error;

View file

@ -38,7 +38,7 @@ export class LogEntry {
}
}
export type LogPayload = Partial<LogContextPayload> & Record<string, unknown>;
export type LogPayload = Partial<LogContextPayload>;
export type LogContext = {
createLog: (key: LogKey) => LogEntry;

View file

@ -5,6 +5,7 @@ import { conditional } from '@silverhand/essentials';
import { getLogtoConnectorById } from '#src/libraries/connector.js';
import { assignInteractionResults } from '#src/libraries/session.js';
import { encryptUserPassword } from '#src/libraries/user.js';
import type { LogEntry } from '#src/middleware/koa-audit-log.js';
import type TenantContext from '#src/tenants/TenantContext.js';
import type { WithInteractionDetailsContext } from '../middleware/koa-interaction-details.js';
@ -129,9 +130,11 @@ const parseUserProfile = async (
export default async function submitInteraction(
interaction: VerifiedInteractionResult,
ctx: WithInteractionDetailsContext,
{ provider, libraries, queries }: TenantContext
{ provider, libraries, queries }: TenantContext,
log?: LogEntry
) {
const { hasActiveUsers, findUserById, updateUserById } = queries.users;
const {
users: { generateUserId, insertUser },
} = libraries;
@ -155,10 +158,13 @@ export default async function submitInteraction(
await assignInteractionResults(ctx, provider, { login: { accountId: id } });
log?.append({ userId: id });
return;
}
const { accountId } = interaction;
log?.append({ userId: accountId });
if (event === InteractionEvent.SignIn) {
const user = await findUserById(accountId);

View file

@ -291,17 +291,13 @@ export default function interactionRoutes<T extends AnonymousRouter>(
const accountVerifiedInteraction = await verifyIdentifier(ctx, provider, interactionStorage);
if (event !== InteractionEvent.Register) {
log.append({ accountId: accountVerifiedInteraction.accountId });
}
const verifiedInteraction = await verifyProfile(accountVerifiedInteraction);
if (event !== InteractionEvent.ForgotPassword) {
await validateMandatoryUserProfile(ctx, verifiedInteraction);
}
await submitInteraction(verifiedInteraction, ctx, tenant);
await submitInteraction(verifiedInteraction, ctx, tenant, log);
return next();
}

View file

@ -1,3 +1,5 @@
import { LogResult } from '@logto/schemas';
import type { Log } from '@logto/schemas';
import { pickDefault, createMockUtils } from '@logto/shared/esm';
import { createRequester } from '#src/utils/test-utils.js';
@ -5,8 +7,8 @@ import { createRequester } from '#src/utils/test-utils.js';
const { jest } = import.meta;
const { mockEsm } = createMockUtils(jest);
const mockBody = { key: 'a', payload: {}, createdAt: 123 };
const mockLog = { id: '1', ...mockBody };
const mockBody = { key: 'a', payload: { key: 'a', result: LogResult.Success }, createdAt: 123 };
const mockLog: Log = { id: '1', ...mockBody };
const mockLogs = [mockLog, { id: '2', ...mockBody }];
const { countLogs, findLogs, findLogById } = mockEsm('#src/queries/log.js', () => ({

View file

@ -188,3 +188,33 @@ export type Translation = {
export const translationGuard: z.ZodType<Translation> = z.lazy(() =>
z.record(z.string().or(translationGuard))
);
/**
* Logs
*/
export enum LogResult {
Success = 'Success',
Error = 'Error',
}
export const logContextPayloadGuard = z
.object({
key: z.string(),
result: z.nativeEnum(LogResult),
error: z.record(z.string(), z.unknown()).or(z.string()).optional(),
ip: z.string().optional(),
userAgent: z.string().optional(),
userId: z.string().optional(),
applicationId: z.string().optional(),
sessionId: z.string().optional(),
})
.catchall(z.unknown());
/**
* The basic log context type. It's more about a type hint instead of forcing the log shape.
*
* Note when setting up a log function, the type of log key in function arguments should be `LogKey`.
* Here we use `string` to make it compatible with the Zod guard.
**/
export type LogContextPayload = z.infer<typeof logContextPayloadGuard>;

View file

@ -1,286 +0,0 @@
import { z } from 'zod';
import type { Log } from '../db-entries/index.js';
/** @deprecated This will be removed soon. Use log types that can be directly imported from `@logto/schemas` instead. */
export enum LogResult {
Success = 'Success',
Error = 'Error',
}
/** @deprecated This will be removed soon. Use log types that can be directly imported from `@logto/schemas` instead. */
export const logResultGuard = z.nativeEnum(LogResult);
/** @deprecated This will be removed soon. Use log types that can be directly imported from `@logto/schemas` instead. */
export const baseLogPayloadGuard = z.object({
result: logResultGuard.optional(),
error: z.record(z.string(), z.unknown()).optional(),
ip: z.string().optional(),
userAgent: z.string().optional(),
applicationId: z.string().optional(),
sessionId: z.string().optional(),
});
/** @deprecated This will be removed soon. Use log types that can be directly imported from `@logto/schemas` instead. */
export type BaseLogPayload = z.infer<typeof baseLogPayloadGuard>;
const arbitraryLogPayloadGuard = z.record(z.string(), z.unknown());
/** @deprecated This will be removed soon. Use log types that can be directly imported from `@logto/schemas` instead. */
export type ArbitraryLogPayload = z.infer<typeof arbitraryLogPayloadGuard>;
const registerUsernamePasswordLogPayloadGuard = arbitraryLogPayloadGuard.and(
z.object({ userId: z.string().optional(), username: z.string().optional() })
);
const registerEmailSendPasscodeLogPayloadGuard = arbitraryLogPayloadGuard.and(
z.object({ email: z.string().optional(), connectorId: z.string().optional() })
);
const registerEmailLogPayloadGuard = arbitraryLogPayloadGuard.and(
z.object({
email: z.string().optional(),
code: z.string().optional(),
userId: z.string().optional(),
})
);
const registerSmsSendPasscodeLogPayloadGuard = arbitraryLogPayloadGuard.and(
z.object({
phone: z.string().optional(),
connectorId: z.string().optional(),
})
);
const registerSmsLogPayloadGuard = arbitraryLogPayloadGuard.and(
z.object({
phone: z.string().optional(),
code: z.string().optional(),
userId: z.string().optional(),
})
);
const registerSocialBindLogPayloadGuard = arbitraryLogPayloadGuard.and(
z.object({
connectorId: z.string().optional(),
userId: z.string().optional(),
userInfo: z.unknown().optional(),
})
);
const registerSocialLogPayloadGuard = registerSocialBindLogPayloadGuard.and(
z.object({
code: z.string().optional(),
state: z.string().optional(),
redirectUri: z.string().optional(),
redirectTo: z.string().optional(),
})
);
const signInUsernamePasswordLogPayloadGuard = arbitraryLogPayloadGuard.and(
z.object({
userId: z.string().optional(),
username: z.string().optional(),
})
);
const signInEmailPasswordLogPayloadGuard = arbitraryLogPayloadGuard.and(
z.object({
userId: z.string().optional(),
email: z.string().optional(),
})
);
const signInSmsPasswordLogPayloadGuard = arbitraryLogPayloadGuard.and(
z.object({
userId: z.string().optional(),
sms: z.string().optional(),
})
);
const signInEmailSendPasscodeLogPayloadGuard = arbitraryLogPayloadGuard.and(
z.object({
email: z.string().optional(),
connectorId: z.string().optional(),
})
);
const signInEmailLogPayloadGuard = arbitraryLogPayloadGuard.and(
z.object({
email: z.string().optional(),
code: z.string().optional(),
userId: z.string().optional(),
})
);
const signInSmsSendPasscodeLogPayloadGuard = arbitraryLogPayloadGuard.and(
z.object({
phone: z.string().optional(),
connectorId: z.string().optional(),
})
);
const signInSmsLogPayloadGuard = arbitraryLogPayloadGuard.and(
z.object({
phone: z.string().optional(),
code: z.string().optional(),
userId: z.string().optional(),
})
);
const signInSocialBindLogPayloadGuard = arbitraryLogPayloadGuard.and(
z.object({
connectorId: z.string().optional(),
userId: z.string().optional(),
userInfo: z.unknown().optional(),
})
);
const signInSocialLogPayloadGuard = signInSocialBindLogPayloadGuard.and(
z.object({
code: z.string().optional(),
state: z.string().optional(),
redirectUri: z.string().optional(),
redirectTo: z.string().optional(),
})
);
const forgotPasswordSmsSendPasscodeLogPayloadGuard = arbitraryLogPayloadGuard.and(
z.object({
phone: z.string().optional(),
connectorId: z.string().optional(),
})
);
const forgotPasswordSmsLogPayloadGuard = arbitraryLogPayloadGuard.and(
z.object({
phone: z.string().optional(),
code: z.string().optional(),
userId: z.string().optional(),
})
);
const forgotPasswordEmailSendPasscodeLogPayloadGuard = arbitraryLogPayloadGuard.and(
z.object({
email: z.string().optional(),
connectorId: z.string().optional(),
})
);
const forgotPasswordEmailLogPayloadGuard = arbitraryLogPayloadGuard.and(
z.object({
email: z.string().optional(),
code: z.string().optional(),
userId: z.string().optional(),
})
);
const forgotPasswordResetLogPayloadGuard = arbitraryLogPayloadGuard.and(
z.object({
userId: z.string().optional(),
})
);
const continueEmailSendPasscodeLogPayloadGuard = arbitraryLogPayloadGuard.and(
z.object({
email: z.string().optional(),
})
);
const continueSmsSendPasscodeLogPayloadGuard = arbitraryLogPayloadGuard.and(
z.object({
phone: z.string().optional(),
})
);
const continueEmailLogPayloadGuard = arbitraryLogPayloadGuard.and(
z.object({
email: z.string().optional(),
})
);
const continueSmsLogPayloadGuard = arbitraryLogPayloadGuard.and(
z.object({
phone: z.string().optional(),
})
);
export enum TokenType {
AccessToken = 'AccessToken',
RefreshToken = 'RefreshToken',
IdToken = 'IdToken',
}
export const tokenTypeGuard = z.nativeEnum(TokenType);
const exchangeTokenLogPayloadGuard = arbitraryLogPayloadGuard.and(
z.object({
userId: z.string().optional(),
params: z.record(z.string(), z.unknown()).optional(),
issued: tokenTypeGuard.array().optional(),
scope: z.string().optional(),
})
);
const revokeTokenLogPayloadGuard = arbitraryLogPayloadGuard.and(
z.object({
userId: z.string().optional(),
params: z.record(z.string(), z.unknown()).optional(),
tokenType: tokenTypeGuard.optional(),
grantId: z.string().optional(),
})
);
const logPayloadsGuard = z.object({
RegisterUsernamePassword: registerUsernamePasswordLogPayloadGuard,
RegisterEmailSendPasscode: registerEmailSendPasscodeLogPayloadGuard,
RegisterEmail: registerEmailLogPayloadGuard,
RegisterSmsSendPasscode: registerSmsSendPasscodeLogPayloadGuard,
RegisterSms: registerSmsLogPayloadGuard,
RegisterSocialBind: registerSocialBindLogPayloadGuard,
RegisterSocial: registerSocialLogPayloadGuard,
SignInUsernamePassword: signInUsernamePasswordLogPayloadGuard,
SignInEmailPassword: signInEmailPasswordLogPayloadGuard,
SignInSmsPassword: signInSmsPasswordLogPayloadGuard,
SignInEmailSendPasscode: signInEmailSendPasscodeLogPayloadGuard,
SignInEmail: signInEmailLogPayloadGuard,
SignInSmsSendPasscode: signInSmsSendPasscodeLogPayloadGuard,
SignInSms: signInSmsLogPayloadGuard,
SignInSocialBind: signInSocialBindLogPayloadGuard,
SignInSocial: signInSocialLogPayloadGuard,
ForgotPasswordSmsSendPasscode: forgotPasswordSmsSendPasscodeLogPayloadGuard,
ForgotPasswordSms: forgotPasswordSmsLogPayloadGuard,
ForgotPasswordEmailSendPasscode: forgotPasswordEmailSendPasscodeLogPayloadGuard,
ForgotPasswordEmail: forgotPasswordEmailLogPayloadGuard,
ForgotPasswordReset: forgotPasswordResetLogPayloadGuard,
ContinueEmailSendPasscode: continueEmailSendPasscodeLogPayloadGuard,
ContinueSmsSendPasscode: continueSmsSendPasscodeLogPayloadGuard,
ContinueEmail: continueEmailLogPayloadGuard,
ContinueSms: continueSmsLogPayloadGuard,
CodeExchangeToken: exchangeTokenLogPayloadGuard,
RefreshTokenExchangeToken: exchangeTokenLogPayloadGuard,
RevokeToken: revokeTokenLogPayloadGuard,
});
/** @deprecated This will be removed soon. Use log types that can be directly imported from `@logto/schemas` instead. */
export type LogPayloads = z.infer<typeof logPayloadsGuard>;
/** @deprecated This will be removed soon. Use log types that can be directly imported from `@logto/schemas` instead. */
export const logTypeGuard = logPayloadsGuard.keyof();
/** @deprecated This will be removed soon. Use log types that can be directly imported from `@logto/schemas` instead. */
export type LogType = z.infer<typeof logTypeGuard>;
/** @deprecated This will be removed soon. Use log types that can be directly imported from `@logto/schemas` instead. */
export type LogPayload = LogPayloads[LogType];
/** @deprecated This will be removed soon. Use log types that can be directly imported from `@logto/schemas` instead. */
export type LogDto = Omit<Log, 'payload'> & {
payload: {
userId?: string;
applicationId?: string;
result?: string;
userAgent?: string;
ip?: string;
};
};

View file

@ -1,6 +1,3 @@
import type { ZodType } from 'zod';
import { z } from 'zod';
import type * as hook from './hook.js';
import type * as interaction from './interaction.js';
import type * as token from './token.js';
@ -20,35 +17,3 @@ export const LogKeyUnknown = 'Unknown';
* @see {@link token.LogKey} for token log keys.
**/
export type LogKey = typeof LogKeyUnknown | interaction.LogKey | token.LogKey | hook.LogKey;
export enum LogResult {
Success = 'Success',
Error = 'Error',
}
/**
* The basic log context type. It's more about a type hint instead of forcing the log shape.
*
* Note when setting up a log function, the type of log key in function arguments should be `LogKey`.
* Here we use `string` to make it compatible with the Zod guard.
**/
export type LogContextPayload = {
key: string;
result: LogResult;
error?: Record<string, unknown> | string;
ip?: string;
userAgent?: string;
applicationId?: string;
sessionId?: string;
};
/** Type guard for {@link LogContextPayload} */
export const logContextGuard: ZodType<LogContextPayload> = z.object({
key: z.string(),
result: z.nativeEnum(LogResult),
error: z.record(z.string(), z.unknown()).or(z.string()).optional(),
ip: z.string().optional(),
userAgent: z.string().optional(),
applicationId: z.string().optional(),
sessionId: z.string().optional(),
});

View file

@ -2,7 +2,7 @@ create table logs
(
id varchar(21) not null,
key varchar(128) not null,
payload jsonb /* @use ArbitraryObject */ not null default '{}'::jsonb,
payload jsonb /* @use LogContextPayload */ not null default '{}'::jsonb,
created_at timestamptz not null default (now()),
primary key (id)
);