back,front: report client errors to the server (#714)

This commit is contained in:
Eric Doughty-Papassideris
2024-11-11 01:21:32 +01:00
committed by Anton Shepilov
parent b04f64a2b0
commit ea8fc2e2f1
4 changed files with 79 additions and 0 deletions
@@ -36,6 +36,7 @@ import { formatCompany, getCompanyStats } from "../utils";
import { formatUser } from "../../../utils/users"; import { formatUser } from "../../../utils/users";
import gr from "../../global-resolver"; import gr from "../../global-resolver";
import config from "config"; import config from "config";
import { getLogger } from "../../../core/platform/framework";
export class UsersCrudController export class UsersCrudController
implements implements
@@ -93,6 +94,30 @@ export class UsersCrudController
}; };
} }
/** This allows a logged in user to upload log entries */
async reportClientLog(
request: FastifyRequest<{ Body: { [key: string]: string } }>,
): Promise<object> {
const headers = { ...request.headers };
const boringOrSecretHeaders =
"cookie authorization cache-control connection pragma content-length content-type accept accept-encoding accept-language".split(
/\s+/g,
);
boringOrSecretHeaders.forEach(header => delete headers[header]);
const message =
request.body.message ||
"(missing message property in UsersCrudController.reportClientLog request body)";
delete request.body.message;
getLogger("FromBrowser").error(
{
headers,
...request.body,
},
message,
);
return {};
}
async setPreferences( async setPreferences(
request: FastifyRequest<{ Body: User["preferences"] }>, request: FastifyRequest<{ Body: User["preferences"] }>,
): Promise<User["preferences"]> { ): Promise<User["preferences"]> {
@@ -9,6 +9,7 @@ import {
getUserSchema, getUserSchema,
getUsersSchema, getUsersSchema,
postDevicesSchema, postDevicesSchema,
sendUserClientReportSchema,
setUserPreferencesSchema, setUserPreferencesSchema,
} from "./schemas"; } from "./schemas";
@@ -52,6 +53,15 @@ const routes: FastifyPluginCallback = (fastify: FastifyInstance, options, next)
handler: usersController.setPreferences.bind(usersController), handler: usersController.setPreferences.bind(usersController),
}); });
fastify.route({
method: "POST",
url: `${usersUrl}/me/reportLog`,
preHandler: accessControl,
preValidation: [fastify.authenticate],
schema: sendUserClientReportSchema,
handler: usersController.reportClientLog.bind(usersController),
});
fastify.route({ fastify.route({
method: "POST", method: "POST",
url: `${usersUrl}/me`, url: `${usersUrl}/me`,
@@ -191,6 +191,19 @@ export const setUserPreferencesSchema = {
}, },
}; };
export const sendUserClientReportSchema = {
request: {
type: "object",
properties: {
message: { type: "string" },
},
required: ["message"],
},
response: {
"2xx": {},
},
};
export const getUsersSchema = { export const getUsersSchema = {
type: "object", type: "object",
properties: { properties: {
@@ -8,6 +8,7 @@ import CurrentUser from '../../../deprecated/user/CurrentUser';
import { setUserList } from '../hooks/use-user-list'; import { setUserList } from '../hooks/use-user-list';
import Logger from 'features/global/framework/logger-service'; import Logger from 'features/global/framework/logger-service';
import { UserQuota } from "features/users/types/user-quota"; import { UserQuota } from "features/users/types/user-quota";
import version from '../../../environment/version';
export type SearchContextType = { export type SearchContextType = {
scope: 'company' | 'workspace' | 'all'; scope: 'company' | 'workspace' | 'all';
@@ -142,6 +143,30 @@ class UserAPIClientService {
}); });
} }
/** Send log entry to the server's logs. Try to include a `message` property please */
async reportLog(body: any | { message: string, error?: Error, err?: Error, e?: Error, }): Promise<void> {
const actualBody = {
version: version.version_detail || version.version,
...body,
} as any;
for (const [key, val] of Object.entries(actualBody)) {
const error = val as Error;
if ((error as Error).stack) {
actualBody[key] = {
message: error.message,
stack: error.stack,
error,
};
if (!actualBody.message) actualBody.message = "Error: " + error.message;
}
}
if (!actualBody.message) actualBody.message = "(missing message property in UserAPIClient.reportLog)";
return Api.post<object, {}>(
'/internal/services/users/v1/users/me/reportLog',
actualBody,
).then(result => undefined);
}
async getQuota(companyId: string, userId: string): Promise<UserQuota> { async getQuota(companyId: string, userId: string): Promise<UserQuota> {
return Api.get<UserQuota>( return Api.get<UserQuota>(
`/internal/services/users/v1/users/${userId}/quota?companyId=${companyId}`, `/internal/services/users/v1/users/${userId}/quota?companyId=${companyId}`,
@@ -255,4 +280,10 @@ class UserAPIClientService {
} }
} }
const UserAPIClient = new UserAPIClientService(); const UserAPIClient = new UserAPIClientService();
window.onerror = (message, url, line, col, error) => {
UserAPIClient.reportLog({ message, url, line, col, error });
return false; // don't suppress normal alert
}
export default UserAPIClient; export default UserAPIClient;