Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
npm-debug.log
10 changes: 10 additions & 0 deletions .talismanrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
version: "1.0.0-beta"

fileignoreconfig:
- filename: ui/src/components/AuditLogs/auditLogs.interface.ts
checksum: 8eaf7502faeaa062a42ee5629f12ba1e8b961b8b8e0d209c4680c59c8de6269c

- filename: api/src/services/migration.service.ts
checksum: bd5374b31c0fad4a266bc9affdcf595f71456edb68b075c1fdfba762f7462e38

- filename: ui/src/components/AuditLogs/index.tsx
checksum: e1402f3cd5b96328d5e1d1e5f2a7fa5179858d6133db831b1b96714eb0f8c260

- filename: .github/workflows/secrets-scan.yml
ignore_detectors:
- filecontent
Expand Down Expand Up @@ -40,3 +49,4 @@ fileignoreconfig:

- filename: upload-api/src/helper/index.ts
checksum: beef34c30cc18c55d66df0124e8bfb69899be9aaef074252afe291c93d4c0f77

7 changes: 7 additions & 0 deletions api/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
node_modules
npm-debug.log
migration-data
logs
database
cmsMigrationData
combine.log
13 changes: 13 additions & 0 deletions api/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM --platform=linux/amd64 node:24.1.0-alpine3.22

WORKDIR /usr/src/app

COPY package*.json ./

RUN npm install

COPY . .

EXPOSE 5001

CMD [ "npm","run", "dev"]
9 changes: 9 additions & 0 deletions api/src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,3 +273,12 @@ 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",
}
8 changes: 6 additions & 2 deletions api/src/controllers/migration.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ const createTestStack = async (req: Request, res: Response): Promise<void> => {
const resp = await migrationService.createTestStack(req);
res.status(resp?.status).json(resp);
};

const getAuditData = async (req: Request, res: Response): Promise<void> => {
const resp = await migrationService.getAuditData(req);
res.status(resp?.status).json(resp);
};
/**
* Start Test Migartion.
*
Expand Down Expand Up @@ -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 @@ -63,7 +63,10 @@ router.get(
"/get_migration_logs/:orgId/:projectId/:stackId/:skip/:limit/:startIndex/:stopIndex/:searchText/:filter",
asyncRouter(migrationController.getLogs)
)

router.get(
"/get_audit_data/:orgId/:projectId/:stackId/:moduleName/:skip/:limit/:startIndex/:stopIndex/:searchText/:filter",
asyncRouter(migrationController?.getAuditData)
)
/**
* Route for updating the source locales from legacy CMS
* @route POST /validator
Expand Down
154 changes: 146 additions & 8 deletions api/src/services/migration.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
LOCALE_MAPPER,
STEPPER_STEPS,
CMS,
GET_AUDIT_DATA
} from '../constants/index.js';
import {
BadRequestError,
Expand Down Expand Up @@ -119,9 +120,8 @@ 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`,
url: `${config.CS_URL[token_payload?.region as keyof typeof config.CS_URL]
}/stack/${res.data.stack.api_key}/dashboard`,
},
status: res.status,
};
Expand Down Expand Up @@ -633,7 +633,144 @@ const startMigration = async (req: Request): Promise<any> => {
);
}
};
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()
const logsDir = path.join(mainPath, 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");
}
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 }> = [];
if (Array.isArray(data)) {
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 [];
}
};
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 Down Expand Up @@ -717,14 +854,14 @@ const getLogs = async (req: Request): Promise<any> => {
status: HTTP_CODES?.OK
};
} else {
logger.error(getLogMessage(srcFunc, HTTP_TEXTS.LOGS_NOT_FOUND));
throw new BadRequestError(HTTP_TEXTS.LOGS_NOT_FOUND);
logger.error(getLogMessage(srcFunc, HTTP_TEXTS?.LOGS_NOT_FOUND));
throw new BadRequestError(HTTP_TEXTS?.LOGS_NOT_FOUND);
}
} catch (error: any) {
logger.error(getLogMessage(srcFunc, HTTP_TEXTS.LOGS_NOT_FOUND, error));
logger.error(getLogMessage(srcFunc, HTTP_TEXTS?.LOGS_NOT_FOUND, error));
throw new ExceptionFunction(
error?.message || HTTP_TEXTS.INTERNAL_ERROR,
error?.statusCode || error?.status || HTTP_CODES.SERVER_ERROR
error?.message || HTTP_TEXTS?.INTERNAL_ERROR,
error?.statusCode || error?.status || HTTP_CODES?.SERVER_ERROR
);
}
};
Expand Down Expand Up @@ -829,4 +966,5 @@ export const migrationService = {
getLogs,
createSourceLocales,
updateLocaleMapper,
getAuditData
};
27 changes: 27 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
version: "3.8"
services:
api:
container_name: migration-api
build:
context: ./api
ports:
- "5001:5001"
restart: always

upload-api:
container_name: migration-upload-api
build:
context: ./upload-api
ports:
- "4002:4002"
restart: always
volumes:
- ${CMS_DATA_PATH}:${CONTAINER_PATH}

ui:
container_name: migration-ui
build:
context: ./ui
ports:
- "3000:3000"
restart: always
59 changes: 59 additions & 0 deletions setup-docker.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#!/bin/bash

echo "Choose the CMS (numbers):"
select CMS_TYPE in "sitecore" "contentful" "wordpress"; do
case $CMS_TYPE in
sitecore)
EXAMPLE_FILE="sitecore.zip"
break
;;
contentful)
EXAMPLE_FILE="contentful.json"
break
;;
wordpress)
EXAMPLE_FILE="wordpress.xml"
break
;;
*)
echo "Invalid option. Please select 1, 2, or 3."
;;
esac
done

read -p "Enter the full path to your $CMS_TYPE data file (e.g., $EXAMPLE_FILE): " CMS_DATA_PATH

if [ ! -f "$CMS_DATA_PATH" ]; then
echo "❌ File does not exist: $CMS_DATA_PATH"
exit 1
fi

FILENAME=$(basename "$CMS_DATA_PATH")
CONTAINER_PATH="/data/$FILENAME"

export CMS_TYPE
export CMS_DATA_PATH
export CONTAINER_PATH

ENV_PATH="./upload-api/.env"

set_env_var() {
VAR_NAME="$1"
VAR_VALUE="$2"
if grep -q "^${VAR_NAME}=" "$ENV_PATH" 2>/dev/null; then
# Update existing variable (cross-platform)
sed -i.bak "s|^${VAR_NAME}=.*|${VAR_NAME}=${VAR_VALUE}|" "$ENV_PATH"
rm -f "$ENV_PATH.bak"
else
# Append new variable
echo "${VAR_NAME}=${VAR_VALUE}" >> "$ENV_PATH"
fi
}

set_env_var "CMS_TYPE" "$CMS_TYPE"
set_env_var "CMS_DATA_PATH" "$CMS_DATA_PATH"
set_env_var "CONTAINER_PATH" "$CONTAINER_PATH"
set_env_var "NODE_BACKEND_API" "http://migration-api:5001"


docker compose up --build
3 changes: 3 additions & 0 deletions ui/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
npm-debug.log
build
13 changes: 13 additions & 0 deletions ui/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM --platform=linux/amd64 node:22-alpine

WORKDIR /usr/src/app

COPY package*.json ./

RUN apk update && apk upgrade && npm install

COPY . .

EXPOSE 3000

CMD [ "npm","run", "start"]
Loading