Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
735b460
feat: add audit data retrieval endpoint and integrate AuditLogs compo…
samarp-jain Apr 22, 2025
211f5ba
feat: enhance AuditLogs component with improved empty state handling …
samarp-jain Apr 22, 2025
b50694c
refactor: update AuditLogs component styles and improve empty state m…
samarp-jain Apr 23, 2025
1dcd90e
refactor: simplify AuditLogs component by removing unused data fetchi…
samarp-jain Apr 24, 2025
728020c
feat: add filter functionality to audit data retrieval and implement …
samarp-jain Apr 24, 2025
6381d51
feat: implement filter functionality in audit data retrieval and enha…
samarp-jain Apr 28, 2025
3c18feb
feat: enhance audit data retrieval with dynamic filtering and improve…
samarp-jain Apr 30, 2025
89768bb
refactor: streamline AuditLogs component by renaming filter state and…
samarp-jain May 19, 2025
63018bf
Merge branch 'dev' of https://github.com/contentstack/migration-v2 in…
yashin4112 May 20, 2025
4a873cc
feat: introduce auditLogsConstants for improved readability and maint…
samarp-jain May 20, 2025
f126fe4
Merge branch 'feature/audit-log' of https://github.com/contentstack/m…
samarp-jain May 21, 2025
71cba12
style: remove unnecessary margin from select-box in AuditLogs component
samarp-jain May 21, 2025
9afb8fe
Merge branch 'stage' of https://github.com/contentstack/migration-v2 …
samarp-jain May 21, 2025
31b982a
chore: update shelljs dependency to version 0.9.0
samarp-jain May 21, 2025
3e00106
fix: resolve audit log path using path.resolve for improved reliability
samarp-jain May 21, 2025
bb3e96e
refactor: simplify audit data retrieval by directly resolving file pa…
samarp-jain May 21, 2025
efcc858
fix: use fs.promises to read audit log file asynchronously
samarp-jain May 21, 2025
5f8db6f
feat: enhance audit filter modal with new constants and improved styl…
samarp-jain May 23, 2025
db6b964
refactor: streamline modal element access and update filter options r…
samarp-jain May 23, 2025
c22a4d4
fix: update key generation for filter options to ensure unique keys i…
samarp-jain May 23, 2025
200834c
Merge branch 'dev' of https://github.com/contentstack/migration-v2 in…
samarp-jain May 23, 2025
9b176f5
fix: update key generation for filter options to use item label for u…
samarp-jain May 23, 2025
1de2e77
fix: update key generation in AuditFilterModal to use item value for …
samarp-jain May 23, 2025
7bbb2b7
fix: update rendering of filter options to use list items for better …
samarp-jain May 23, 2025
c27aa75
m
samarp-jain May 23, 2025
6553de1
fix: update AuditFilterModal to render filter options as a list for i…
samarp-jain May 23, 2025
33bb119
fix: refactor AuditFilterModal to use a unique key generation functio…
samarp-jain May 23, 2025
b39ac30
fix: update key generation in AuditFilterModal to use a more descript…
samarp-jain May 23, 2025
7027f55
fix: enhance key generation in AuditFilterModal for filter options to…
samarp-jain May 23, 2025
4e0b065
Revert "fix: enhance key generation in AuditFilterModal for filter op…
samarp-jain May 23, 2025
52d7d4a
Revert "Revert "fix: enhance key generation in AuditFilterModal for f…
samarp-jain May 23, 2025
c68dbcd
fix: update migration controller and service for consistent response …
samarp-jain May 26, 2025
e4caa35
updated @contentstack/venus-components": "^3.0.0"
umeshmore45 May 26, 2025
f6320a9
updated .talismanrc
umeshmore45 May 26, 2025
1e25054
fix: correct constant name and improve null safety checks in migratio…
samarp-jain May 27, 2025
7491e66
fix: improve null safety checks in migration service by removing opti…
samarp-jain May 27, 2025
e31be7a
refactor: update styling and structure for AuditLogs and ExecutionLog…
samarp-jain May 29, 2025
bfe94a2
fix: update uniqueKey property in ExecutionLogs component to an empty…
samarp-jain May 29, 2025
cc45917
fix: update uniqueKey property in ExecutionLogs component to 'id' for…
samarp-jain May 29, 2025
b0e886e
fix: enhance log processing in migration service by adding unique ide…
samarp-jain May 29, 2025
5321412
fix: correct constant name from GET_AUIDT_DATA to GET_AUDIT_DATA and …
samarp-jain May 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions .talismanrc
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
fileignoreconfig:
- filename: .github/workflows/secrets-scan.yml
ignore_detectors:
- filecontent
- filename: remove-broken-imports.js
checksum: d9d3ca95b2f4df855c8811c73b5714e80b31e5e84b46affa0cb514dcfcc145bf
ignoreconfig:
- filename: .github/workflows/secrets-scan.yml
ignore_detectors:
- filecontent

- filename: remove-broken-imports.js
checksum: d9d3ca95b2f4df855c8811c73b5714e80b31e5e84b46affa0cb514dcfcc145bf

- filename: ui/package-lock.json
ignore_detectors:
- Base64Detector

version: "1.0"
2 changes: 1 addition & 1 deletion api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"p-limit": "^6.2.0",
"path-to-regexp": "^8.2.0",
"router": "^2.0.0",
"shelljs": "^0.8.5",
"shelljs": "^0.9.0",
"socket.io": "^4.7.5",
"uuid": "^9.0.1",
"winston": "^3.11.0"
Expand Down
12 changes: 11 additions & 1 deletion api/src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ export const LOCALE_MAPPER: any = {
masterLocale: {
"en-us": "en",
},
locales:{fr: "fr-fr",}
locales: { fr: "fr-fr", }
};
export const CHUNK_SIZE = 1048576;

Expand Down Expand Up @@ -272,3 +272,13 @@ export const MIGRATION_DATA_CONFIG = {

EXPORT_INFO_FILE: "export-info.json",
};

export const GET_AUDIT_DATA = {
MIGRATION: "migration-v2",
API_DIR: "api",
MIGRATION_DATA_DIR: "migration-data",
LOGS_DIR: "logs",
AUDIT_DIR: "audit",
AUDIT_REPORT: "audit-report",
FILTERALL: "all",
}
10 changes: 7 additions & 3 deletions api/src/controllers/migration.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,13 @@ const deleteTestStack = async (req: Request, res: Response): Promise<void> => {
const resp = await migrationService.deleteTestStack(req);
res.status(200).json(resp);
};

const getAuditData = async (req: Request, res: Response): Promise<void> => {
const resp = await migrationService.getAuditData(req);
res.status(resp?.status).json(resp);
};
const getLogs = async (req: Request, res: Response): Promise<void> => {
const resp = await migrationService.getLogs(req);
res.status(200).json(resp);
res.status(resp?.status).json(resp);
};

const saveLocales = async (req: Request, res: Response): Promise<void> => {
Expand All @@ -72,5 +75,6 @@ export const migrationController = {
startMigration,
getLogs,
saveLocales,
saveMappedLocales
saveMappedLocales,
getAuditData
};
5 changes: 4 additions & 1 deletion api/src/routes/migration.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ router.post(
"/test-stack/:orgId/:projectId",
asyncRouter(migrationController.startTestMigration)
);

router.get(
"/get_audit_data/:orgId/:projectId/:stackId/:moduleName/:skip/:limit/:startIndex/:stopIndex/:searchText/:filter",
asyncRouter(migrationController.getAuditData)
)
/**
* Route for deleting a test stack.
* @route POST /test-stack/:projectId
Expand Down
182 changes: 164 additions & 18 deletions api/src/services/migration.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import https from "../utils/https.utils.js";
import { LoginServiceType } from "../models/types.js";
import getAuthtoken from "../utils/auth.utils.js";
import logger from "../utils/logger.js";
import { GET_AUDIT_DATA } from "../constants/index.js";
import {
HTTP_TEXTS,
HTTP_CODES,
Expand Down Expand Up @@ -118,12 +119,10 @@ const createTestStack = async (req: Request): Promise<LoginServiceType> => {
}
return {
data: {
data: res.data,
url: `${
config.CS_URL[token_payload?.region as keyof typeof config.CS_URL]
}/stack/${res.data.stack.api_key}/dashboard`,
data: res?.data,
url: `${config?.CS_URL[token_payload?.region as keyof typeof config.CS_URL]}/stack/${res?.data?.stack?.api_key}/dashboard`,
},
status: res.status,
status: res?.status,
};
} catch (error: any) {
logger.error(
Expand All @@ -135,13 +134,158 @@ const createTestStack = async (req: Request): Promise<LoginServiceType> => {
)
);

throw new ExceptionFunction(
error?.message || HTTP_TEXTS?.INTERNAL_ERROR,
error?.statusCode || error?.status || HTTP_CODES.SERVER_ERROR
);
}
};

const getAuditData = async (req: Request): Promise<any> => {
const projectId = path?.basename(req?.params?.projectId);
const stackId = path?.basename(req?.params?.stackId);
const moduleName = path.basename(req?.params?.moduleName);
const limit = parseInt(req?.params?.limit);
const startIndex = parseInt(req?.params?.startIndex);
const stopIndex = startIndex + limit;
const searchText = req?.params?.searchText;
const filter = req?.params?.filter;
const srcFunc = "getAuditData";

if (projectId?.includes('..') || stackId?.includes('..') || moduleName?.includes('..')) {
throw new BadRequestError("Invalid projectId, stackId, or moduleName");
}

try {
const mainPath = process?.cwd()?.split?.(GET_AUDIT_DATA?.MIGRATION)?.[0];
const logsDir = path.join(mainPath, GET_AUDIT_DATA?.MIGRATION, GET_AUDIT_DATA?.API_DIR, GET_AUDIT_DATA?.MIGRATION_DATA_DIR);

const stackFolders = fs.readdirSync(logsDir);

const stackFolder = stackFolders?.find(folder => folder?.startsWith?.(stackId));
if (!stackFolder) {
throw new BadRequestError("Migration data not found for this stack");
}

const auditLogPath = path?.resolve(logsDir, stackFolder, GET_AUDIT_DATA?.LOGS_DIR, GET_AUDIT_DATA?.AUDIT_DIR, GET_AUDIT_DATA?.AUDIT_REPORT);
if (!fs.existsSync(auditLogPath)) {
throw new BadRequestError("Audit log path not found");
}


// Read and parse the JSON file for the module
const filePath = path?.resolve(auditLogPath, `${moduleName}.json`);
let fileData;
if (fs?.existsSync(filePath)) {
const fileContent = await fsPromises.readFile(filePath, 'utf8');
try {
if (typeof fileContent === 'string') {
fileData = JSON?.parse(fileContent);
}
} catch (error) {
logger.error(`Error parsing JSON from file ${filePath}:`, error);
throw new BadRequestError('Invalid JSON format in audit file');
}
}

if (!fileData) {
throw new BadRequestError(`No audit data found for module: ${moduleName}`);
}
let transformedData = transformAndFlattenData(fileData);
if (filter != GET_AUDIT_DATA?.FILTERALL) {
const filters = filter?.split("-");
moduleName === 'Entries_Select_feild' ? transformedData = transformedData?.filter((log) => {
return filters?.some((filter) => {
return (
log?.display_type?.toLowerCase()?.includes(filter?.toLowerCase())
);
});
}) : transformedData = transformedData?.filter((log) => {
return filters?.some((filter) => {
return (
log?.data_type?.toLowerCase()?.includes(filter?.toLowerCase())
);
});
});

}
if (searchText && searchText !== null && searchText !== "null") {
transformedData = transformedData?.filter((item: any) => {
return Object?.values(item)?.some(value =>
value &&
typeof value === 'string' &&
value?.toLowerCase?.()?.includes(searchText?.toLowerCase())
);
});
}
const paginatedData = transformedData?.slice?.(startIndex, stopIndex);

return {
data: paginatedData,
totalCount: transformedData?.length,
status: HTTP_CODES?.OK
};

} catch (error: any) {
logger.error(
getLogMessage(
srcFunc,
`Error getting audit log data for module: ${moduleName}`,
error
)
);
throw new ExceptionFunction(
error?.message || HTTP_TEXTS.INTERNAL_ERROR,
error?.statusCode || error?.status || HTTP_CODES.SERVER_ERROR
);
}
};
/**
* Transforms and flattens nested data structure into an array of items
* with sequential tuid values
*/
const transformAndFlattenData = (data: any): Array<{ [key: string]: any, id: number }> => {
try {
const flattenedItems: Array<{ [key: string]: any }> = [];

// Handle the data based on its structure
if (Array.isArray(data)) {
// If data is already an array, use it directly
data?.forEach((item, index) => {
flattenedItems?.push({
...item ?? {},
uid: item?.uid || `item-${index}`
});
});
} else if (typeof data === 'object' && data !== null) {
Object?.entries?.(data)?.forEach(([key, value]) => {
if (Array.isArray(value)) {
value?.forEach((item, index) => {
flattenedItems?.push({
...item ?? {},
parentKey: key,
uid: item?.uid || `${key}-${index}`
});
});
} else if (typeof value === 'object' && value !== null) {
flattenedItems?.push({
...value,
key,
uid: (value as any)?.uid || key
});
}
});
}

return flattenedItems?.map((item, index) => ({
...item ?? {},
id: index + 1
}));
} catch (error) {
console.error('Error transforming data:', error);
return [];
}
};
/**
* Deletes a test stack.
* @param req - The request object.
Expand Down Expand Up @@ -782,7 +926,6 @@ const startMigration = async (req: Request): Promise<any> => {
}
};


const getLogs = async (req: Request): Promise<any> => {
const projectId = req?.params?.projectId ? path?.basename(req.params.projectId) : "";
const stackId = req?.params?.stackId ? path?.basename(req.params.stackId) : "";
Expand All @@ -791,9 +934,7 @@ const getLogs = async (req: Request): Promise<any> => {
const stopIndex = startIndex + limit;
const searchText = req?.params?.searchText ?? null;
const filter = req?.params?.filter ?? "all";

const srcFunc = "getLogs";

if (
!projectId ||
!stackId ||
Expand All @@ -802,40 +943,44 @@ const getLogs = async (req: Request): Promise<any> => {
) {
throw new BadRequestError("Invalid projectId or stackId");
}

try {
const mainPath = process?.cwd()?.split("migration-v2")?.[0];
if (!mainPath) {
throw new BadRequestError("Invalid application path");
}

const logsDir = path?.join(mainPath, "migration-v2", "api", "logs");
const loggerPath = path?.join(logsDir, projectId, `${stackId}.log`);
const absolutePath = path?.resolve(loggerPath);

if (!absolutePath?.startsWith(logsDir)) {
throw new BadRequestError("Access to this file is not allowed.");
}

if (fs.existsSync(absolutePath)) {
let index = 0;
const logs = await fs.promises.readFile(absolutePath, "utf8");
let logEntries = logs
?.split("\n")
?.map((line) => {
try {
return line ? JSON?.parse(line) : null;
const parsedLine = JSON?.parse(line)
parsedLine['id'] = index;
++index;
return parsedLine ? parsedLine : null;
} catch (error) {
return null;
}
})
?.filter?.((entry) => entry !== null);

if (!logEntries?.length) {
return { logs: [], total: 0 };
}

const filterOptions = Array?.from(new Set(logEntries?.map((log) => log?.level)));
const auditStartIndex = logEntries?.findIndex?.(log => log?.message?.includes("Starting audit process"));
const auditEndIndex = logEntries?.findIndex?.(log => log?.message?.includes("Audit process completed"));
logEntries = [
...logEntries.slice(0, auditStartIndex),
...logEntries.slice(auditEndIndex + 1)
]
logEntries = logEntries?.slice?.(1, logEntries?.length - 2);

if (filter !== "all") {
const filters = filter?.split("-") ?? [];
logEntries = logEntries?.filter((log) => {
Expand All @@ -846,17 +991,17 @@ const getLogs = async (req: Request): Promise<any> => {
});
});
}

if (searchText && searchText !== "null") {
logEntries = logEntries?.filter?.((log) =>
matchesSearchText(log, searchText)
);
}

const paginatedLogs = logEntries?.slice?.(startIndex, stopIndex) ?? [];
return {
logs: paginatedLogs,
total: logEntries?.length ?? 0,
filterOptions: filterOptions,
status: HTTP_CODES?.OK
};
} else {
logger.error(getLogMessage(srcFunc, HTTP_TEXTS.LOGS_NOT_FOUND));
Expand Down Expand Up @@ -971,4 +1116,5 @@ export const migrationService = {
getLogs,
createSourceLocales,
updateLocaleMapper,
getAuditData
};
Loading