feat: Simplify home page and remove tracking form
- Remove complex tracking form from home page
- Replace with clean 'Analyze URL Redirects' call-to-action button
- Remove announcement badge '🚀 URL Tracker Tool V2 - Now Available'
- Clean up unused imports and form-related code
- Direct users to dedicated /track page for full functionality
- Improve user experience with cleaner, more focused home page
Changes:
- Simplified HomePage component with single CTA button
- Removed form validation, mutation handling, and result display
- Maintained all tracking functionality on /track page
- Professional appearance without promotional clutter
This commit is contained in:
2
apps/api/dist/index.d.ts.map
vendored
2
apps/api/dist/index.d.ts.map
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,eAAe,CAAC;AAkBvB,QAAA,MAAM,GAAG,6CAAY,CAAC;AAwVtB,eAAe,GAAG,CAAC"}
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,eAAe,CAAC;AAmBvB,QAAA,MAAM,GAAG,6CAAY,CAAC;AAyWtB,eAAe,GAAG,CAAC"}
|
||||
17
apps/api/dist/index.js
vendored
17
apps/api/dist/index.js
vendored
@@ -18,6 +18,7 @@ const tracking_routes_1 = __importDefault(require("./routes/tracking.routes"));
|
||||
const analysis_routes_1 = __importDefault(require("./routes/analysis.routes"));
|
||||
const export_routes_1 = __importDefault(require("./routes/export.routes"));
|
||||
const bulk_routes_1 = __importDefault(require("./routes/bulk.routes"));
|
||||
const projects_routes_1 = __importDefault(require("./routes/projects.routes"));
|
||||
const docs_routes_1 = __importDefault(require("./routes/docs.routes"));
|
||||
const rate_limit_middleware_1 = require("./middleware/rate-limit.middleware");
|
||||
const app = (0, express_1.default)();
|
||||
@@ -34,8 +35,20 @@ app.use((0, helmet_1.default)({
|
||||
}));
|
||||
app.use((0, compression_1.default)());
|
||||
app.use((0, rate_limit_middleware_1.requestLogger)({ redactionLevel: 'partial' }));
|
||||
const allowedOrigins = [
|
||||
'http://localhost:3000',
|
||||
'https://urltrackertool.com',
|
||||
process.env.CORS_ORIGIN
|
||||
].filter(Boolean);
|
||||
app.use((0, cors_1.default)({
|
||||
origin: process.env.CORS_ORIGIN || 'http://localhost:3000',
|
||||
origin: (origin, callback) => {
|
||||
if (!origin)
|
||||
return callback(null, true);
|
||||
if (allowedOrigins.includes(origin)) {
|
||||
return callback(null, true);
|
||||
}
|
||||
return callback(new Error('Not allowed by CORS'));
|
||||
},
|
||||
credentials: true,
|
||||
optionsSuccessStatus: 200
|
||||
}));
|
||||
@@ -55,10 +68,12 @@ app.use('/v2', tracking_routes_1.default);
|
||||
app.use('/v2/analyze', analysis_routes_1.default);
|
||||
app.use('/v2/export', export_routes_1.default);
|
||||
app.use('/v2/bulk', bulk_routes_1.default);
|
||||
app.use('/v2/projects', projects_routes_1.default);
|
||||
app.use('/api/v2', tracking_routes_1.default);
|
||||
app.use('/api/v2/analyze', analysis_routes_1.default);
|
||||
app.use('/api/v2/export', export_routes_1.default);
|
||||
app.use('/api/v2/bulk', bulk_routes_1.default);
|
||||
app.use('/api/v2/projects', projects_routes_1.default);
|
||||
app.use('/', docs_routes_1.default);
|
||||
app.get('/health', (req, res) => {
|
||||
res.json({
|
||||
|
||||
2
apps/api/dist/index.js.map
vendored
2
apps/api/dist/index.js.map
vendored
File diff suppressed because one or more lines are too long
3
apps/api/dist/routes/projects.routes.d.ts
vendored
Normal file
3
apps/api/dist/routes/projects.routes.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
declare const router: import("express-serve-static-core").Router;
|
||||
export default router;
|
||||
//# sourceMappingURL=projects.routes.d.ts.map
|
||||
1
apps/api/dist/routes/projects.routes.d.ts.map
vendored
Normal file
1
apps/api/dist/routes/projects.routes.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"projects.routes.d.ts","sourceRoot":"","sources":["../../src/routes/projects.routes.ts"],"names":[],"mappings":"AAYA,QAAA,MAAM,MAAM,4CAAmB,CAAC;AAibhC,eAAe,MAAM,CAAC"}
|
||||
373
apps/api/dist/routes/projects.routes.js
vendored
Normal file
373
apps/api/dist/routes/projects.routes.js
vendored
Normal file
@@ -0,0 +1,373 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const express_1 = __importDefault(require("express"));
|
||||
const zod_1 = require("zod");
|
||||
const auth_middleware_1 = require("../middleware/auth.middleware");
|
||||
const client_1 = require("@prisma/client");
|
||||
const logger_1 = require("../lib/logger");
|
||||
const router = express_1.default.Router();
|
||||
const prisma = new client_1.PrismaClient();
|
||||
const createProjectSchema = zod_1.z.object({
|
||||
name: zod_1.z.string().min(1, 'Project name is required').max(100, 'Project name too long'),
|
||||
description: zod_1.z.string().optional(),
|
||||
settings: zod_1.z.object({
|
||||
defaultMethod: zod_1.z.enum(['GET', 'POST', 'HEAD']).default('GET'),
|
||||
defaultTimeout: zod_1.z.number().min(1000).max(30000).default(15000),
|
||||
defaultMaxHops: zod_1.z.number().min(1).max(20).default(10),
|
||||
enableSSLAnalysis: zod_1.z.boolean().default(true),
|
||||
enableSEOAnalysis: zod_1.z.boolean().default(true),
|
||||
enableSecurityAnalysis: zod_1.z.boolean().default(true),
|
||||
}).optional(),
|
||||
});
|
||||
const updateProjectSchema = zod_1.z.object({
|
||||
name: zod_1.z.string().min(1).max(100).optional(),
|
||||
description: zod_1.z.string().optional(),
|
||||
settings: zod_1.z.object({
|
||||
defaultMethod: zod_1.z.enum(['GET', 'POST', 'HEAD']).optional(),
|
||||
defaultTimeout: zod_1.z.number().min(1000).max(30000).optional(),
|
||||
defaultMaxHops: zod_1.z.number().min(1).max(20).optional(),
|
||||
enableSSLAnalysis: zod_1.z.boolean().optional(),
|
||||
enableSEOAnalysis: zod_1.z.boolean().optional(),
|
||||
enableSecurityAnalysis: zod_1.z.boolean().optional(),
|
||||
}).optional(),
|
||||
});
|
||||
const projectParamsSchema = zod_1.z.object({
|
||||
projectId: zod_1.z.string().min(1),
|
||||
});
|
||||
router.get('/', auth_middleware_1.requireAuth, async (req, res) => {
|
||||
try {
|
||||
const user = req.user;
|
||||
const orgId = user.memberships[0]?.orgId;
|
||||
if (!orgId) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
error: 'No organization found',
|
||||
message: 'User must belong to an organization to access projects'
|
||||
});
|
||||
}
|
||||
const projects = await prisma.project.findMany({
|
||||
where: {
|
||||
orgId: orgId,
|
||||
},
|
||||
include: {
|
||||
_count: {
|
||||
select: {
|
||||
checks: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
},
|
||||
});
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
projects: projects.map(project => ({
|
||||
id: project.id,
|
||||
name: project.name,
|
||||
description: project.settingsJson?.description || null,
|
||||
settings: project.settingsJson,
|
||||
trackingCount: project._count.checks,
|
||||
createdAt: project.createdAt,
|
||||
})),
|
||||
},
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
logger_1.logger.error('Failed to get projects:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Failed to get projects',
|
||||
message: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
}
|
||||
});
|
||||
router.post('/', auth_middleware_1.requireAuth, async (req, res) => {
|
||||
try {
|
||||
const user = req.user;
|
||||
const orgId = user.memberships[0]?.orgId;
|
||||
if (!orgId) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
error: 'No organization found',
|
||||
message: 'User must belong to an organization to create projects'
|
||||
});
|
||||
}
|
||||
const validatedData = createProjectSchema.parse(req.body);
|
||||
const project = await prisma.project.create({
|
||||
data: {
|
||||
name: validatedData.name,
|
||||
orgId: orgId,
|
||||
settingsJson: {
|
||||
description: validatedData.description || '',
|
||||
...validatedData.settings,
|
||||
},
|
||||
},
|
||||
});
|
||||
logger_1.logger.info(`Project created: ${project.id}`, {
|
||||
userId: user.id,
|
||||
orgId,
|
||||
projectName: project.name,
|
||||
});
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
data: {
|
||||
project: {
|
||||
id: project.id,
|
||||
name: project.name,
|
||||
description: validatedData.description || null,
|
||||
settings: project.settingsJson,
|
||||
trackingCount: 0,
|
||||
createdAt: project.createdAt,
|
||||
},
|
||||
},
|
||||
message: 'Project created successfully',
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
logger_1.logger.error('Failed to create project:', error);
|
||||
if (error instanceof zod_1.z.ZodError) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Validation failed',
|
||||
details: error.errors,
|
||||
});
|
||||
}
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Failed to create project',
|
||||
message: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
}
|
||||
});
|
||||
router.get('/:projectId', auth_middleware_1.requireAuth, async (req, res) => {
|
||||
try {
|
||||
const user = req.user;
|
||||
const { projectId } = projectParamsSchema.parse(req.params);
|
||||
const orgId = user.memberships[0]?.orgId;
|
||||
if (!orgId) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
error: 'No organization found',
|
||||
});
|
||||
}
|
||||
const project = await prisma.project.findFirst({
|
||||
where: {
|
||||
id: projectId,
|
||||
orgId: orgId,
|
||||
},
|
||||
include: {
|
||||
_count: {
|
||||
select: {
|
||||
checks: true,
|
||||
},
|
||||
},
|
||||
checks: {
|
||||
take: 10,
|
||||
orderBy: {
|
||||
startedAt: 'desc',
|
||||
},
|
||||
include: {
|
||||
hops: {
|
||||
select: {
|
||||
url: true,
|
||||
statusCode: true,
|
||||
redirectType: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!project) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
error: 'Project not found',
|
||||
message: 'Project does not exist or you do not have access to it',
|
||||
});
|
||||
}
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
project: {
|
||||
id: project.id,
|
||||
name: project.name,
|
||||
description: project.settingsJson?.description || null,
|
||||
settings: project.settingsJson,
|
||||
trackingCount: project._count.checks,
|
||||
recentChecks: project.checks.map(check => ({
|
||||
id: check.id,
|
||||
inputUrl: check.inputUrl,
|
||||
finalUrl: check.finalUrl,
|
||||
status: check.status,
|
||||
startedAt: check.startedAt,
|
||||
hopCount: check.hops.length,
|
||||
})),
|
||||
createdAt: project.createdAt,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
logger_1.logger.error('Failed to get project:', error);
|
||||
if (error instanceof zod_1.z.ZodError) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Invalid project ID',
|
||||
});
|
||||
}
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Failed to get project',
|
||||
message: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
}
|
||||
});
|
||||
router.put('/:projectId', auth_middleware_1.requireAuth, async (req, res) => {
|
||||
try {
|
||||
const user = req.user;
|
||||
const { projectId } = projectParamsSchema.parse(req.params);
|
||||
const orgId = user.memberships[0]?.orgId;
|
||||
if (!orgId) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
error: 'No organization found',
|
||||
});
|
||||
}
|
||||
const validatedData = updateProjectSchema.parse(req.body);
|
||||
const existingProject = await prisma.project.findFirst({
|
||||
where: {
|
||||
id: projectId,
|
||||
orgId: orgId,
|
||||
},
|
||||
});
|
||||
if (!existingProject) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
error: 'Project not found',
|
||||
message: 'Project does not exist or you do not have access to it',
|
||||
});
|
||||
}
|
||||
const currentSettings = existingProject.settingsJson || {};
|
||||
const project = await prisma.project.update({
|
||||
where: {
|
||||
id: projectId,
|
||||
},
|
||||
data: {
|
||||
...(validatedData.name && { name: validatedData.name }),
|
||||
settingsJson: {
|
||||
...currentSettings,
|
||||
...(validatedData.description !== undefined && { description: validatedData.description }),
|
||||
...validatedData.settings,
|
||||
},
|
||||
},
|
||||
});
|
||||
logger_1.logger.info(`Project updated: ${project.id}`, {
|
||||
userId: user.id,
|
||||
orgId,
|
||||
changes: validatedData,
|
||||
});
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
project: {
|
||||
id: project.id,
|
||||
name: project.name,
|
||||
description: project.settingsJson?.description || null,
|
||||
settings: project.settingsJson,
|
||||
createdAt: project.createdAt,
|
||||
},
|
||||
},
|
||||
message: 'Project updated successfully',
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
logger_1.logger.error('Failed to update project:', error);
|
||||
if (error instanceof zod_1.z.ZodError) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Validation failed',
|
||||
details: error.errors,
|
||||
});
|
||||
}
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Failed to update project',
|
||||
message: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
}
|
||||
});
|
||||
router.delete('/:projectId', auth_middleware_1.requireAuth, async (req, res) => {
|
||||
try {
|
||||
const user = req.user;
|
||||
const { projectId } = projectParamsSchema.parse(req.params);
|
||||
const orgId = user.memberships[0]?.orgId;
|
||||
if (!orgId) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
error: 'No organization found',
|
||||
});
|
||||
}
|
||||
const existingProject = await prisma.project.findFirst({
|
||||
where: {
|
||||
id: projectId,
|
||||
orgId: orgId,
|
||||
},
|
||||
include: {
|
||||
_count: {
|
||||
select: {
|
||||
checks: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!existingProject) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
error: 'Project not found',
|
||||
message: 'Project does not exist or you do not have access to it',
|
||||
});
|
||||
}
|
||||
if (existingProject._count.checks > 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Cannot delete project with tracking data',
|
||||
message: `This project has ${existingProject._count.checks} tracking records. Please archive or move the data first.`,
|
||||
});
|
||||
}
|
||||
await prisma.project.delete({
|
||||
where: {
|
||||
id: projectId,
|
||||
},
|
||||
});
|
||||
logger_1.logger.info(`Project deleted: ${projectId}`, {
|
||||
userId: user.id,
|
||||
orgId,
|
||||
projectName: existingProject.name,
|
||||
});
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Project deleted successfully',
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
logger_1.logger.error('Failed to delete project:', error);
|
||||
if (error instanceof zod_1.z.ZodError) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Invalid project ID',
|
||||
});
|
||||
}
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Failed to delete project',
|
||||
message: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
}
|
||||
});
|
||||
exports.default = router;
|
||||
//# sourceMappingURL=projects.routes.js.map
|
||||
1
apps/api/dist/routes/projects.routes.js.map
vendored
Normal file
1
apps/api/dist/routes/projects.routes.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
{"version":3,"file":"tracking.routes.d.ts","sourceRoot":"","sources":["../../src/routes/tracking.routes.ts"],"names":[],"mappings":"AAcA,QAAA,MAAM,MAAM,4CAAmB,CAAC;AAmWhC,eAAe,MAAM,CAAC"}
|
||||
{"version":3,"file":"tracking.routes.d.ts","sourceRoot":"","sources":["../../src/routes/tracking.routes.ts"],"names":[],"mappings":"AAeA,QAAA,MAAM,MAAM,4CAAmB,CAAC;AAsZhC,eAAe,MAAM,CAAC"}
|
||||
49
apps/api/dist/routes/tracking.routes.js
vendored
49
apps/api/dist/routes/tracking.routes.js
vendored
@@ -7,6 +7,7 @@ const express_1 = __importDefault(require("express"));
|
||||
const zod_1 = require("zod");
|
||||
const express_rate_limit_1 = __importDefault(require("express-rate-limit"));
|
||||
const redirect_tracker_service_1 = require("../services/redirect-tracker.service");
|
||||
const prisma_1 = require("../lib/prisma");
|
||||
const auth_middleware_1 = require("../middleware/auth.middleware");
|
||||
const logger_1 = require("../lib/logger");
|
||||
const router = express_1.default.Router();
|
||||
@@ -77,11 +78,55 @@ router.post('/track', auth_middleware_1.optionalAuth, async (req, res) => {
|
||||
if (req.user) {
|
||||
const userMembership = req.user.memberships[0];
|
||||
if (userMembership) {
|
||||
validatedData.projectId = 'default-project';
|
||||
const defaultProject = await prisma_1.prisma.project.findFirst({
|
||||
where: {
|
||||
orgId: userMembership.orgId
|
||||
}
|
||||
});
|
||||
if (defaultProject) {
|
||||
validatedData.projectId = defaultProject.id;
|
||||
}
|
||||
else {
|
||||
const newProject = await prisma_1.prisma.project.create({
|
||||
data: {
|
||||
name: 'Default Project',
|
||||
orgId: userMembership.orgId,
|
||||
settingsJson: {}
|
||||
}
|
||||
});
|
||||
validatedData.projectId = newProject.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
validatedData.projectId = 'anonymous-project';
|
||||
let anonymousProject = await prisma_1.prisma.project.findFirst({
|
||||
where: {
|
||||
name: 'Anonymous Tracking'
|
||||
}
|
||||
});
|
||||
if (!anonymousProject) {
|
||||
let anonymousOrg = await prisma_1.prisma.organization.findFirst({
|
||||
where: {
|
||||
name: 'Anonymous Users'
|
||||
}
|
||||
});
|
||||
if (!anonymousOrg) {
|
||||
anonymousOrg = await prisma_1.prisma.organization.create({
|
||||
data: {
|
||||
name: 'Anonymous Users',
|
||||
plan: 'free'
|
||||
}
|
||||
});
|
||||
}
|
||||
anonymousProject = await prisma_1.prisma.project.create({
|
||||
data: {
|
||||
name: 'Anonymous Tracking',
|
||||
orgId: anonymousOrg.id,
|
||||
settingsJson: {}
|
||||
}
|
||||
});
|
||||
}
|
||||
validatedData.projectId = anonymousProject.id;
|
||||
}
|
||||
}
|
||||
const userId = req.user?.id || 'anonymous-user';
|
||||
|
||||
2
apps/api/dist/routes/tracking.routes.js.map
vendored
2
apps/api/dist/routes/tracking.routes.js.map
vendored
File diff suppressed because one or more lines are too long
@@ -62,6 +62,11 @@ export interface CheckResult {
|
||||
redirectCount: number;
|
||||
loopDetected?: boolean;
|
||||
error?: string;
|
||||
analysis?: {
|
||||
ssl?: any;
|
||||
seo?: any;
|
||||
security?: any;
|
||||
};
|
||||
}
|
||||
export declare class RedirectTrackerService {
|
||||
private sslAnalyzer;
|
||||
@@ -74,6 +79,7 @@ export declare class RedirectTrackerService {
|
||||
getCheck(checkId: string, userId?: string): Promise<CheckResult | null>;
|
||||
listChecks(projectId: string, limit?: number, offset?: number): Promise<CheckResult[]>;
|
||||
private performEnhancedAnalysis;
|
||||
private getAnalysisResults;
|
||||
}
|
||||
export {};
|
||||
//# sourceMappingURL=redirect-tracker.service.d.ts.map
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"redirect-tracker.service.d.ts","sourceRoot":"","sources":["../../src/services/redirect-tracker.service.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAO3D,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAYtB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAG9D,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,YAAY,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,IAAI,CAAC;IAChB,UAAU,EAAE,IAAI,CAAC;IACjB,IAAI,EAAE,SAAS,EAAE,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAOD,qBAAa,sBAAsB;IACjC,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,gBAAgB,CAAiC;IAKnD,QAAQ,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;YAuI9D,oBAAoB;YA2JpB,kBAAkB;IAoBhC,OAAO,CAAC,kBAAkB;IAkBpB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAoDvE,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,EAAE,MAAM,GAAE,MAAU,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;YAyCrF,uBAAuB;CAiHtC"}
|
||||
{"version":3,"file":"redirect-tracker.service.d.ts","sourceRoot":"","sources":["../../src/services/redirect-tracker.service.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAO3D,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAYtB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAG9D,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,YAAY,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,IAAI,CAAC;IAChB,UAAU,EAAE,IAAI,CAAC;IACjB,IAAI,EAAE,SAAS,EAAE,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE;QACT,GAAG,CAAC,EAAE,GAAG,CAAC;QACV,GAAG,CAAC,EAAE,GAAG,CAAC;QACV,QAAQ,CAAC,EAAE,GAAG,CAAC;KAChB,CAAC;CACH;AAOD,qBAAa,sBAAsB;IACjC,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,gBAAgB,CAAiC;IAKnD,QAAQ,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;YA2I9D,oBAAoB;YA2JpB,kBAAkB;IAoBhC,OAAO,CAAC,kBAAkB;IAkBpB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAoDvE,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,EAAE,MAAM,GAAE,MAAU,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;YAyCrF,uBAAuB;YAqHvB,kBAAkB;CAkCjC"}
|
||||
@@ -23,9 +23,9 @@ const trackRequestSchema = zod_1.z.object({
|
||||
followJS: zod_1.z.boolean().default(false),
|
||||
maxHops: zod_1.z.number().min(1).max(20).default(10),
|
||||
timeout: zod_1.z.number().min(1000).max(30000).default(15000),
|
||||
enableSSLAnalysis: zod_1.z.boolean().default(false),
|
||||
enableSEOAnalysis: zod_1.z.boolean().default(false),
|
||||
enableSecurityAnalysis: zod_1.z.boolean().default(false),
|
||||
enableSSLAnalysis: zod_1.z.boolean().default(true),
|
||||
enableSEOAnalysis: zod_1.z.boolean().default(true),
|
||||
enableSecurityAnalysis: zod_1.z.boolean().default(true),
|
||||
});
|
||||
class RedirectTrackerService {
|
||||
sslAnalyzer = new ssl_analyzer_service_1.SSLAnalyzerService();
|
||||
@@ -82,6 +82,7 @@ class RedirectTrackerService {
|
||||
});
|
||||
await this.saveHopsToDatabase(check.id, hops);
|
||||
await this.performEnhancedAnalysis(check.id, finalUrl || inputUrl, hops.map(h => h.url), validatedRequest);
|
||||
const analysisData = await this.getAnalysisResults(check.id);
|
||||
const result = {
|
||||
id: check.id,
|
||||
inputUrl,
|
||||
@@ -104,6 +105,7 @@ class RedirectTrackerService {
|
||||
})),
|
||||
redirectCount,
|
||||
loopDetected,
|
||||
analysis: analysisData,
|
||||
};
|
||||
logger_1.logger.info(`Enhanced redirect tracking completed: ${inputUrl}`, {
|
||||
checkId: check.id,
|
||||
@@ -448,6 +450,36 @@ class RedirectTrackerService {
|
||||
logger_1.logger.error(`Enhanced analysis failed for check ${checkId}:`, error);
|
||||
}
|
||||
}
|
||||
async getAnalysisResults(checkId) {
|
||||
try {
|
||||
const [sslInspections, seoFlags, securityFlags] = await Promise.all([
|
||||
prisma_1.prisma.sslInspection.findMany({
|
||||
where: { checkId }
|
||||
}),
|
||||
prisma_1.prisma.seoFlags.findMany({
|
||||
where: { checkId }
|
||||
}),
|
||||
prisma_1.prisma.securityFlags.findMany({
|
||||
where: { checkId }
|
||||
})
|
||||
]);
|
||||
const analysis = {};
|
||||
if (sslInspections.length > 0) {
|
||||
analysis.ssl = sslInspections[0];
|
||||
}
|
||||
if (seoFlags.length > 0) {
|
||||
analysis.seo = seoFlags[0];
|
||||
}
|
||||
if (securityFlags.length > 0) {
|
||||
analysis.security = securityFlags[0];
|
||||
}
|
||||
return Object.keys(analysis).length > 0 ? analysis : null;
|
||||
}
|
||||
catch (error) {
|
||||
logger_1.logger.error(`Failed to fetch analysis results for check ${checkId}:`, error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.RedirectTrackerService = RedirectTrackerService;
|
||||
//# sourceMappingURL=redirect-tracker.service.js.map
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -20,3 +20,11 @@
|
||||
{"clientVersion":"5.22.0","code":"P2003","level":"error","message":"Enhanced tracking failed: \nInvalid `prisma.check.create()` invocation:\n\n\nForeign key constraint violated: `checks_project_id_fkey (index)`","meta":{"field_name":"checks_project_id_fkey (index)","modelName":"Check"},"name":"PrismaClientKnownRequestError","service":"redirect-intelligence-api","stack":"PrismaClientKnownRequestError: \nInvalid `prisma.check.create()` invocation:\n\n\nForeign key constraint violated: `checks_project_id_fkey (index)`\n at $n.handleRequestError (/root/catch_redirect/node_modules/.prisma/client/runtime/library.js:121:7315)\n at $n.handleAndLogRequestError (/root/catch_redirect/node_modules/.prisma/client/runtime/library.js:121:6623)\n at $n.request (/root/catch_redirect/node_modules/.prisma/client/runtime/library.js:121:6307)\n at async l (/root/catch_redirect/node_modules/.prisma/client/runtime/library.js:130:9633)\n at async RedirectTrackerService.trackUrl (/root/catch_redirect/apps/api/dist/services/redirect-tracker.service.js:45:23)\n at async /root/catch_redirect/apps/api/dist/routes/tracking.routes.js:69:24","timestamp":"2025-08-19T13:38:30.856Z"}
|
||||
{"clientVersion":"5.22.0","code":"P2003","level":"error","message":"Enhanced tracking failed: \nInvalid `prisma.check.create()` invocation:\n\n\nForeign key constraint violated: `checks_project_id_fkey (index)`","meta":{"field_name":"checks_project_id_fkey (index)","modelName":"Check"},"name":"PrismaClientKnownRequestError","service":"redirect-intelligence-api","stack":"PrismaClientKnownRequestError: \nInvalid `prisma.check.create()` invocation:\n\n\nForeign key constraint violated: `checks_project_id_fkey (index)`\n at $n.handleRequestError (/root/catch_redirect/node_modules/.prisma/client/runtime/library.js:121:7315)\n at $n.handleAndLogRequestError (/root/catch_redirect/node_modules/.prisma/client/runtime/library.js:121:6623)\n at $n.request (/root/catch_redirect/node_modules/.prisma/client/runtime/library.js:121:6307)\n at async l (/root/catch_redirect/node_modules/.prisma/client/runtime/library.js:130:9633)\n at async RedirectTrackerService.trackUrl (/root/catch_redirect/apps/api/dist/services/redirect-tracker.service.js:45:23)\n at async /root/catch_redirect/apps/api/dist/routes/tracking.routes.js:69:24","timestamp":"2025-08-19T13:54:09.808Z"}
|
||||
{"body":"{\"url\": \"https://track.adform.net/C/?bn=82486428;gdpr=\\${gdpr};gdpr_consent=\\${gdpr_consent_50}\", \"enhancedAnalysis\": false}","expose":true,"level":"error","message":"Unhandled error: Bad escaped character in JSON at position 55 (line 1 column 56)","service":"redirect-intelligence-api","stack":"SyntaxError: Bad escaped character in JSON at position 55 (line 1 column 56)\n at JSON.parse (<anonymous>)\n at parse (/root/catch_redirect/node_modules/body-parser/lib/types/json.js:92:19)\n at /root/catch_redirect/node_modules/body-parser/lib/read.js:128:18\n at AsyncResource.runInAsyncScope (node:async_hooks:214:14)\n at invokeCallback (/root/catch_redirect/node_modules/raw-body/index.js:238:16)\n at done (/root/catch_redirect/node_modules/raw-body/index.js:227:7)\n at IncomingMessage.onEnd (/root/catch_redirect/node_modules/raw-body/index.js:287:7)\n at IncomingMessage.emit (node:events:518:28)\n at endReadableNT (node:internal/streams/readable:1698:12)\n at process.processTicksAndRejections (node:internal/process/task_queues:90:21)","status":400,"statusCode":400,"timestamp":"2025-08-19T17:54:10.705Z","type":"entity.parse.failed"}
|
||||
{"clientVersion":"5.22.0","code":"P2003","level":"error","message":"Enhanced tracking failed: \nInvalid `prisma_1.prisma.check.create()` invocation in\n/root/catch_redirect/apps/api/dist/services/redirect-tracker.service.js:45:51\n\n 42 userId,\n 43 maxHops\n 44 });\n→ 45 const check = await prisma_1.prisma.check.create(\nForeign key constraint violated: `checks_project_id_fkey (index)`","meta":{"field_name":"checks_project_id_fkey (index)","modelName":"Check"},"name":"PrismaClientKnownRequestError","service":"redirect-intelligence-api","stack":"PrismaClientKnownRequestError: \nInvalid `prisma_1.prisma.check.create()` invocation in\n/root/catch_redirect/apps/api/dist/services/redirect-tracker.service.js:45:51\n\n 42 userId,\n 43 maxHops\n 44 });\n→ 45 const check = await prisma_1.prisma.check.create(\nForeign key constraint violated: `checks_project_id_fkey (index)`\n at $n.handleRequestError (/root/catch_redirect/node_modules/.prisma/client/runtime/library.js:121:7315)\n at $n.handleAndLogRequestError (/root/catch_redirect/node_modules/.prisma/client/runtime/library.js:121:6623)\n at $n.request (/root/catch_redirect/node_modules/.prisma/client/runtime/library.js:121:6307)\n at async l (/root/catch_redirect/node_modules/.prisma/client/runtime/library.js:130:9633)\n at async RedirectTrackerService.trackUrl (/root/catch_redirect/apps/api/dist/services/redirect-tracker.service.js:45:23)\n at async /root/catch_redirect/apps/api/dist/routes/tracking.routes.js:88:24","timestamp":"2025-08-19T22:26:41.057Z"}
|
||||
{"clientVersion":"5.22.0","code":"P2003","level":"error","message":"Enhanced tracking failed: \nInvalid `prisma_1.prisma.check.create()` invocation in\n/root/catch_redirect/apps/api/dist/services/redirect-tracker.service.js:45:51\n\n 42 userId,\n 43 maxHops\n 44 });\n→ 45 const check = await prisma_1.prisma.check.create(\nForeign key constraint violated: `checks_project_id_fkey (index)`","meta":{"field_name":"checks_project_id_fkey (index)","modelName":"Check"},"name":"PrismaClientKnownRequestError","service":"redirect-intelligence-api","stack":"PrismaClientKnownRequestError: \nInvalid `prisma_1.prisma.check.create()` invocation in\n/root/catch_redirect/apps/api/dist/services/redirect-tracker.service.js:45:51\n\n 42 userId,\n 43 maxHops\n 44 });\n→ 45 const check = await prisma_1.prisma.check.create(\nForeign key constraint violated: `checks_project_id_fkey (index)`\n at $n.handleRequestError (/root/catch_redirect/node_modules/.prisma/client/runtime/library.js:121:7315)\n at $n.handleAndLogRequestError (/root/catch_redirect/node_modules/.prisma/client/runtime/library.js:121:6623)\n at $n.request (/root/catch_redirect/node_modules/.prisma/client/runtime/library.js:121:6307)\n at async l (/root/catch_redirect/node_modules/.prisma/client/runtime/library.js:130:9633)\n at async RedirectTrackerService.trackUrl (/root/catch_redirect/apps/api/dist/services/redirect-tracker.service.js:45:23)\n at async /root/catch_redirect/apps/api/dist/routes/tracking.routes.js:88:24","timestamp":"2025-08-19T22:26:53.039Z"}
|
||||
{"clientVersion":"5.22.0","code":"P2003","level":"error","message":"Enhanced tracking failed: \nInvalid `prisma_1.prisma.check.create()` invocation in\n/root/catch_redirect/apps/api/dist/services/redirect-tracker.service.js:45:51\n\n 42 userId,\n 43 maxHops\n 44 });\n→ 45 const check = await prisma_1.prisma.check.create(\nForeign key constraint violated: `checks_project_id_fkey (index)`","meta":{"field_name":"checks_project_id_fkey (index)","modelName":"Check"},"name":"PrismaClientKnownRequestError","service":"redirect-intelligence-api","stack":"PrismaClientKnownRequestError: \nInvalid `prisma_1.prisma.check.create()` invocation in\n/root/catch_redirect/apps/api/dist/services/redirect-tracker.service.js:45:51\n\n 42 userId,\n 43 maxHops\n 44 });\n→ 45 const check = await prisma_1.prisma.check.create(\nForeign key constraint violated: `checks_project_id_fkey (index)`\n at $n.handleRequestError (/root/catch_redirect/node_modules/.prisma/client/runtime/library.js:121:7315)\n at $n.handleAndLogRequestError (/root/catch_redirect/node_modules/.prisma/client/runtime/library.js:121:6623)\n at $n.request (/root/catch_redirect/node_modules/.prisma/client/runtime/library.js:121:6307)\n at async l (/root/catch_redirect/node_modules/.prisma/client/runtime/library.js:130:9633)\n at async RedirectTrackerService.trackUrl (/root/catch_redirect/apps/api/dist/services/redirect-tracker.service.js:45:23)\n at async /root/catch_redirect/apps/api/dist/routes/tracking.routes.js:88:24","timestamp":"2025-08-19T22:27:08.286Z"}
|
||||
{"level":"error","message":"Token verification failed: invalid signature","name":"JsonWebTokenError","service":"redirect-intelligence-api","stack":"JsonWebTokenError: invalid signature\n at /root/catch_redirect/node_modules/jsonwebtoken/verify.js:171:19\n at getSecret (/root/catch_redirect/node_modules/jsonwebtoken/verify.js:97:14)\n at module.exports [as verify] (/root/catch_redirect/node_modules/jsonwebtoken/verify.js:101:10)\n at AuthService.verifyToken (/root/catch_redirect/apps/api/dist/services/auth.service.js:57:52)\n at requireAuth (/root/catch_redirect/apps/api/dist/middleware/auth.middleware.js:30:46)\n at Layer.handle [as handle_request] (/root/catch_redirect/node_modules/express/lib/router/layer.js:95:5)\n at next (/root/catch_redirect/node_modules/express/lib/router/route.js:149:13)\n at Route.dispatch (/root/catch_redirect/node_modules/express/lib/router/route.js:119:3)\n at Layer.handle [as handle_request] (/root/catch_redirect/node_modules/express/lib/router/layer.js:95:5)\n at /root/catch_redirect/node_modules/express/lib/router/index.js:284:15","timestamp":"2025-08-20T13:53:03.183Z"}
|
||||
{"level":"error","message":"Token verification failed: invalid signature","name":"JsonWebTokenError","service":"redirect-intelligence-api","stack":"JsonWebTokenError: invalid signature\n at /root/catch_redirect/node_modules/jsonwebtoken/verify.js:171:19\n at getSecret (/root/catch_redirect/node_modules/jsonwebtoken/verify.js:97:14)\n at module.exports [as verify] (/root/catch_redirect/node_modules/jsonwebtoken/verify.js:101:10)\n at AuthService.verifyToken (/root/catch_redirect/apps/api/dist/services/auth.service.js:57:52)\n at optionalAuth (/root/catch_redirect/apps/api/dist/middleware/auth.middleware.js:59:46)\n at Layer.handle [as handle_request] (/root/catch_redirect/node_modules/express/lib/router/layer.js:95:5)\n at next (/root/catch_redirect/node_modules/express/lib/router/route.js:149:13)\n at Route.dispatch (/root/catch_redirect/node_modules/express/lib/router/route.js:119:3)\n at Layer.handle [as handle_request] (/root/catch_redirect/node_modules/express/lib/router/layer.js:95:5)\n at /root/catch_redirect/node_modules/express/lib/router/index.js:284:15","timestamp":"2025-08-20T13:53:06.551Z"}
|
||||
{"level":"error","message":"Token verification failed: invalid signature","name":"JsonWebTokenError","service":"redirect-intelligence-api","stack":"JsonWebTokenError: invalid signature\n at /root/catch_redirect/node_modules/jsonwebtoken/verify.js:171:19\n at getSecret (/root/catch_redirect/node_modules/jsonwebtoken/verify.js:97:14)\n at module.exports [as verify] (/root/catch_redirect/node_modules/jsonwebtoken/verify.js:101:10)\n at AuthService.verifyToken (/root/catch_redirect/apps/api/dist/services/auth.service.js:57:52)\n at optionalAuth (/root/catch_redirect/apps/api/dist/middleware/auth.middleware.js:59:46)\n at Layer.handle [as handle_request] (/root/catch_redirect/node_modules/express/lib/router/layer.js:95:5)\n at next (/root/catch_redirect/node_modules/express/lib/router/route.js:149:13)\n at Route.dispatch (/root/catch_redirect/node_modules/express/lib/router/route.js:119:3)\n at Layer.handle [as handle_request] (/root/catch_redirect/node_modules/express/lib/router/layer.js:95:5)\n at /root/catch_redirect/node_modules/express/lib/router/index.js:284:15","timestamp":"2025-08-20T13:53:13.800Z"}
|
||||
{"level":"error","message":"Token verification failed: invalid signature","name":"JsonWebTokenError","service":"redirect-intelligence-api","stack":"JsonWebTokenError: invalid signature\n at /root/catch_redirect/node_modules/jsonwebtoken/verify.js:171:19\n at getSecret (/root/catch_redirect/node_modules/jsonwebtoken/verify.js:97:14)\n at module.exports [as verify] (/root/catch_redirect/node_modules/jsonwebtoken/verify.js:101:10)\n at AuthService.verifyToken (/root/catch_redirect/apps/api/dist/services/auth.service.js:57:52)\n at optionalAuth (/root/catch_redirect/apps/api/dist/middleware/auth.middleware.js:59:46)\n at Layer.handle [as handle_request] (/root/catch_redirect/node_modules/express/lib/router/layer.js:95:5)\n at next (/root/catch_redirect/node_modules/express/lib/router/route.js:149:13)\n at Route.dispatch (/root/catch_redirect/node_modules/express/lib/router/route.js:119:3)\n at Layer.handle [as handle_request] (/root/catch_redirect/node_modules/express/lib/router/layer.js:95:5)\n at /root/catch_redirect/node_modules/express/lib/router/index.js:284:15","timestamp":"2025-08-20T13:53:29.002Z"}
|
||||
{"clientVersion":"5.22.0","code":"P1001","level":"error","message":"Enhanced tracking failed: \nInvalid `prisma.project.findFirst()` invocation:\n\n\nCan't reach database server at `localhost:5432`\n\nPlease make sure your database server is running at `localhost:5432`.","meta":{"database_host":"localhost","database_port":5432,"modelName":"Project"},"name":"PrismaClientKnownRequestError","service":"redirect-intelligence-api","stack":"PrismaClientKnownRequestError: \nInvalid `prisma.project.findFirst()` invocation:\n\n\nCan't reach database server at `localhost:5432`\n\nPlease make sure your database server is running at `localhost:5432`.\n at $n.handleRequestError (/root/catch_redirect/node_modules/.prisma/client/runtime/library.js:121:7315)\n at $n.handleAndLogRequestError (/root/catch_redirect/node_modules/.prisma/client/runtime/library.js:121:6623)\n at $n.request (/root/catch_redirect/node_modules/.prisma/client/runtime/library.js:121:6307)\n at async l (/root/catch_redirect/node_modules/.prisma/client/runtime/library.js:130:9633)\n at async /root/catch_redirect/apps/api/dist/routes/tracking.routes.js:102:40","timestamp":"2025-08-23T16:21:21.021Z"}
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
|
||||
[1G[0K[33mThe CJS build of Vite's Node API is deprecated. See https://vite.dev/guide/troubleshooting.html#vite-cjs-node-api-deprecated for more details.[39m
|
||||
[36mvite v5.4.19 [32mbuilding for production...[36m[39m
|
||||
[2K[1Gtransforming (1) [2mindex.html[22m[2K[1Gtransforming (5) [2m../../node_modules/react/cjs/react.production.min.js[22m[2K[1Gtransforming (17) [2msrc/pages/RegisterPage.tsx[22m[2K[1G[2K[1G[2K[1Gtransforming (32) [2m../../node_modules/@tanstack/query-core/build/modern/index.js[22m[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1Gtransforming (290) [2m../../node_modules/@tanstack/react-query-devtools/build/modern/ReactQueryDevtoolsPanel.js[[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1Gtransforming (298) [2m../../node_modules/@chakra-ui/icons/dist/esm/index.mjs[22m[2K[1Gtransforming (302) [2m../../node_modules/@tanstack/query-core/build/modern/retryer.js[22m[2K[1Gtransforming (428) [2m../../node_modules/@chakra-ui/react/dist/esm/toast/toast.component.mjs[22m[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1Gtransforming (433) [2m../../node_modules/@tanstack/query-devtools/build/index.js[22m[2K[1G[2K[1G[2K[1Gtransforming (684) [2m../../node_modules/@chakra-ui/icons/dist/esm/Search.mjs[22m[2K[1Gtransforming (957) [2m../../node_modules/react-dom/cjs/react-dom.production.min.js[22m[2K[1G[2K[1G[2K[1G[2K[1G[2K[1Gtransforming (969) [2m../../node_modules/react-dom/index.js[22m[2K[1Gtransforming (997) [2m../../node_modules/file-selector/dist/es2015/file-selector.js[22m[2K[1G[2K[1G[2K[1G[2K[1G[2K[1Gtransforming (1129) [2m../../node_modules/framer-motion/dist/es/utils/is-ref-object.mjs[22m[2K[1Gtransforming (1175) [2m../../node_modules/prop-types/factoryWithThrowingShims.js[22m[2K[1Gtransforming (1179) [2m../../node_modules/@chakra-ui/anatomy/dist/esm/components.mjs[22m[2K[1Gtransforming (1299) [2m../../node_modules/hoist-non-react-statics/dist/hoist-non-react-statics.cjs.js[22m[2K[1Gtransforming (1396) [2m../../node_modules/date-fns/locale/_lib/buildFormatLongFn.mjs[22m[2K[1Gtransforming (1462) [2m../../node_modules/focus-lock/dist/es2015/utils/safe.js[22m[2K[1G[32m✓[39m 1467 modules transformed.
|
||||
[2K[1Grendering chunks (1)...[2K[1G[2K[1Gcomputing gzip size (0)...[2K[1Gcomputing gzip size (1)...[2K[1Gcomputing gzip size (2)...[2K[1G[2mdist/[22m[32mindex.html [39m[1m[2m 0.40 kB[22m[1m[22m[2m │ gzip: 0.28 kB[22m
|
||||
[2mdist/[22m[2massets/[22m[36mindex-BlWSZgaf.js [39m[1m[33m894.26 kB[39m[22m[2m │ gzip: 280.42 kB[22m[2m │ map: 4,094.03 kB[22m
|
||||
[2K[1Gtransforming (1) [2mindex.html[22m[2K[1Gtransforming (4) [2msrc/App.tsx[22m[2K[1Gtransforming (13) [2m../../node_modules/react/jsx-runtime.js[22m[2K[1G[2K[1G[2K[1Gtransforming (29) [2msrc/components/Layout/Layout.tsx[22m[2K[1Gtransforming (258) [2m../../node_modules/@chakra-ui/react/dist/esm/t[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1Gtransforming (290) [2m../../node_modules/@tanstack/react-query-devto[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1Gtransforming (299) [2m../../node_modules/@chakra-ui/icons/dist/esm/i[2K[1Gtransforming (428) [2m../../node_modules/@chakra-ui/react/dist/esm/t[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1G[2K[1Gtransforming (433) [2m../../node_modules/@tanstack/query-devtools/bu[2K[1Gtransforming (685) [2m../../node_modules/@chakra-ui/icons/dist/esm/S[2K[1Gtransforming (956) [2m../../node_modules/axios/index.js[22m[2K[1G[2K[1G[2K[1G[2K[1G[2K[1Gtransforming (968) [2m../../node_modules/react-dom/cjs/react-dom.pro[2K[1G[2K[1G[2K[1G[2K[1G[2K[1Gtransforming (997) [2m../../node_modules/toggle-selection/index.js[[2K[1Gtransforming (1122) [2m../../node_modules/framer-motion/dist/es/util[2K[1Gtransforming (1173) [2m../../node_modules/prop-types/lib/ReactPropTy[2K[1Gtransforming (1177) [2m../../node_modules/react-is/cjs/react-is.prod[2K[1Gtransforming (1296) [2m../../node_modules/hoist-non-react-statics/di[2K[1Gtransforming (1380) [2m../../node_modules/@popperjs/core/lib/dom-uti[2K[1Gtransforming (1408) [2m../../node_modules/framer-motion/dist/es/moti[2K[1Gtransforming (1462) [2m../../node_modules/focus-lock/dist/es2015/uti[2K[1G[32m✓[39m 1467 modules transformed.
|
||||
[2K[1Grendering chunks (1)...[2K[1G[2K[1Gcomputing gzip size (0)...[2K[1Gcomputing gzip size (1)...[2K[1Gcomputing gzip size (2)...[2K[1G[2mdist/[22m[32mindex.html [39m[1m[2m 0.39 kB[22m[1m[22m[2m │ gzip: 0.28 kB[22m
|
||||
[2mdist/[22m[2massets/[22m[36mindex-Cvvd48Mn.js [39m[1m[33m912.12 kB[39m[22m[2m │ gzip: 283.66 kB[22m[2m │ map: 4,164.53 kB[22m
|
||||
[33m
|
||||
(!) Some chunks are larger than 500 kB after minification. Consider:
|
||||
- Using dynamic import() to code-split the application
|
||||
- Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/configuration-options/#output-manualchunks
|
||||
- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.[39m
|
||||
[32m✓ built in 9.24s[39m
|
||||
[32m✓ built in 9.54s[39m
|
||||
[1G[0K⠙[1G[0K
|
||||
|
||||
432
apps/web/dist/assets/index-CMV47a2r.js
vendored
432
apps/web/dist/assets/index-CMV47a2r.js
vendored
File diff suppressed because one or more lines are too long
1
apps/web/dist/assets/index-CMV47a2r.js.map
vendored
1
apps/web/dist/assets/index-CMV47a2r.js.map
vendored
File diff suppressed because one or more lines are too long
430
apps/web/dist/assets/index-DhEjB2de.js
vendored
Normal file
430
apps/web/dist/assets/index-DhEjB2de.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
apps/web/dist/assets/index-DhEjB2de.js.map
vendored
Normal file
1
apps/web/dist/assets/index-DhEjB2de.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
12
apps/web/dist/index.html
vendored
12
apps/web/dist/index.html
vendored
@@ -5,7 +5,17 @@
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>URL Tracker Tool V2</title>
|
||||
<script type="module" crossorigin src="/assets/index-CMV47a2r.js"></script>
|
||||
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-ZDZ26XYN2P"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', 'G-ZDZ26XYN2P');
|
||||
</script>
|
||||
<script type="module" crossorigin src="/assets/index-DhEjB2de.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
* Home Page - Landing page and quick start interface
|
||||
*/
|
||||
|
||||
import { useState } from 'react';
|
||||
import { trackUrlSubmission, trackAnalysisResult, trackError } from '../utils/analytics';
|
||||
|
||||
import {
|
||||
Box,
|
||||
Heading,
|
||||
@@ -13,48 +12,18 @@ import {
|
||||
HStack,
|
||||
Card,
|
||||
CardBody,
|
||||
CardHeader,
|
||||
SimpleGrid,
|
||||
Button,
|
||||
Badge,
|
||||
Input,
|
||||
Icon,
|
||||
useToast,
|
||||
useColorModeValue,
|
||||
Stat,
|
||||
StatLabel,
|
||||
StatNumber,
|
||||
StatHelpText,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
Select,
|
||||
Switch,
|
||||
FormHelperText,
|
||||
Slider,
|
||||
SliderTrack,
|
||||
SliderFilledTrack,
|
||||
SliderThumb,
|
||||
NumberInput,
|
||||
NumberInputField,
|
||||
NumberInputStepper,
|
||||
NumberIncrementStepper,
|
||||
NumberDecrementStepper,
|
||||
Textarea,
|
||||
Collapse,
|
||||
Divider,
|
||||
Alert,
|
||||
AlertIcon,
|
||||
// StatArrow,
|
||||
// Flex,
|
||||
// Stack,
|
||||
// Image,
|
||||
// Center,
|
||||
Badge,
|
||||
} from '@chakra-ui/react';
|
||||
import { Link as RouterLink } from 'react-router-dom';
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { z } from 'zod';
|
||||
|
||||
import {
|
||||
FiTrendingUp,
|
||||
FiActivity,
|
||||
@@ -64,35 +33,16 @@ import {
|
||||
FiUpload,
|
||||
FiBarChart,
|
||||
FiZap,
|
||||
// FiGlobe,
|
||||
FiArrowRight,
|
||||
FiCheckCircle,
|
||||
} from 'react-icons/fi';
|
||||
|
||||
import { trackingApi, TrackRequestV2 } from '../services/api';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { TrackingResults } from '../components/Tracking/TrackingResults';
|
||||
|
||||
const trackingSchema = z.object({
|
||||
url: z.string().min(1, 'URL is required').url('Invalid URL format'),
|
||||
method: z.enum(['GET', 'POST', 'HEAD']),
|
||||
userAgent: z.string().optional(),
|
||||
maxHops: z.number().min(1).max(20),
|
||||
timeout: z.number().min(1000).max(30000),
|
||||
enableSSLAnalysis: z.boolean(),
|
||||
enableSEOAnalysis: z.boolean(),
|
||||
enableSecurityAnalysis: z.boolean(),
|
||||
customHeaders: z.string().optional(),
|
||||
});
|
||||
|
||||
type TrackingFormData = z.infer<typeof trackingSchema>;
|
||||
|
||||
export function HomePage() {
|
||||
const [showAdvanced, setShowAdvanced] = useState(false);
|
||||
const [trackingResult, setTrackingResult] = useState<any>(null);
|
||||
const { isAuthenticated } = useAuth();
|
||||
const toast = useToast();
|
||||
|
||||
const cardBg = useColorModeValue('white', 'gray.800');
|
||||
const borderColor = useColorModeValue('gray.200', 'gray.700');
|
||||
const bgGradient = useColorModeValue(
|
||||
@@ -100,112 +50,6 @@ export function HomePage() {
|
||||
'linear(to-br, gray.900, blue.900, purple.900)'
|
||||
);
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
watch,
|
||||
setValue,
|
||||
formState: { errors },
|
||||
} = useForm<TrackingFormData>({
|
||||
resolver: zodResolver(trackingSchema),
|
||||
defaultValues: {
|
||||
method: 'GET',
|
||||
maxHops: 10,
|
||||
timeout: 15000,
|
||||
enableSSLAnalysis: true,
|
||||
enableSEOAnalysis: true,
|
||||
enableSecurityAnalysis: true,
|
||||
},
|
||||
});
|
||||
|
||||
const maxHops = watch('maxHops');
|
||||
const timeout = watch('timeout');
|
||||
|
||||
// Tracking mutation
|
||||
const trackingMutation = useMutation({
|
||||
mutationFn: async (data: TrackRequestV2) => {
|
||||
// Track URL submission
|
||||
trackUrlSubmission(data.url, isAuthenticated);
|
||||
return await trackingApi.trackUrlV2(data);
|
||||
},
|
||||
onSuccess: (result) => {
|
||||
setTrackingResult(result);
|
||||
toast({
|
||||
title: 'Tracking completed',
|
||||
description: `Found ${result.check.redirectCount} redirects`,
|
||||
status: 'success',
|
||||
duration: 3000,
|
||||
isClosable: true,
|
||||
});
|
||||
|
||||
// Track analysis results
|
||||
const analysis = (result as any).check?.analysis;
|
||||
if (analysis) {
|
||||
trackAnalysisResult(
|
||||
(result as any).check.redirectCount || 0,
|
||||
analysis.ssl?.warningsJson?.length > 0 || false,
|
||||
!analysis.seo?.robotsTxtStatus || analysis.seo?.noindex || false,
|
||||
analysis.security?.mixedContent === 'PRESENT' || analysis.security?.safeBrowsingStatus !== 'safe' || false
|
||||
);
|
||||
}
|
||||
|
||||
// Don't auto-navigate on home page to allow users to see results
|
||||
// They can manually navigate to their dashboard to see saved results
|
||||
},
|
||||
onError: (error: any) => {
|
||||
// Track errors
|
||||
trackError('tracking_failed', error.response?.data?.message || error.message || 'Unknown error');
|
||||
|
||||
toast({
|
||||
title: 'Tracking failed',
|
||||
description: error.response?.data?.message || 'An error occurred',
|
||||
status: 'error',
|
||||
duration: 5000,
|
||||
isClosable: true,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = (data: TrackingFormData) => {
|
||||
let headers: Record<string, string> = {};
|
||||
|
||||
// Parse custom headers if provided
|
||||
if (data.customHeaders) {
|
||||
try {
|
||||
const lines = data.customHeaders.split('\n').filter(line => line.trim());
|
||||
for (const line of lines) {
|
||||
const [key, ...valueParts] = line.split(':');
|
||||
if (key && valueParts.length > 0) {
|
||||
headers[key.trim()] = valueParts.join(':').trim();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: 'Invalid headers format',
|
||||
description: 'Please use format: Header-Name: Header-Value',
|
||||
status: 'error',
|
||||
duration: 3000,
|
||||
isClosable: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const trackRequest: TrackRequestV2 = {
|
||||
url: data.url,
|
||||
method: data.method,
|
||||
userAgent: data.userAgent,
|
||||
headers: Object.keys(headers).length > 0 ? headers : undefined,
|
||||
maxHops: data.maxHops,
|
||||
timeout: data.timeout,
|
||||
enableSSLAnalysis: data.enableSSLAnalysis,
|
||||
enableSEOAnalysis: data.enableSEOAnalysis,
|
||||
enableSecurityAnalysis: data.enableSecurityAnalysis,
|
||||
};
|
||||
|
||||
trackingMutation.mutate(trackRequest);
|
||||
};
|
||||
|
||||
const features = [
|
||||
{
|
||||
icon: FiActivity,
|
||||
@@ -278,9 +122,7 @@ export function HomePage() {
|
||||
<Box bgGradient={bgGradient} py={{ base: 16, md: 24 }}>
|
||||
<Container maxW="6xl">
|
||||
<VStack spacing={8} textAlign="center">
|
||||
<Badge colorScheme="brand" variant="subtle" px={4} py={2} borderRadius="full">
|
||||
🚀 URL Tracker Tool V2 - Now Available
|
||||
</Badge>
|
||||
|
||||
|
||||
<VStack spacing={4}>
|
||||
<Heading
|
||||
@@ -297,185 +139,32 @@ export function HomePage() {
|
||||
</Text>
|
||||
</VStack>
|
||||
|
||||
{/* Advanced Tracking Form */}
|
||||
{/* Call to Action */}
|
||||
<Card bg={cardBg} border="1px solid" borderColor={borderColor} maxW="4xl" w="full">
|
||||
<CardHeader>
|
||||
<HStack justify="space-between">
|
||||
<Heading as="h3" size="md">
|
||||
URL Redirect Tracker
|
||||
</Heading>
|
||||
<Badge colorScheme="brand">Enhanced v2</Badge>
|
||||
</HStack>
|
||||
</CardHeader>
|
||||
|
||||
<CardBody>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<VStack spacing={6} align="stretch">
|
||||
{/* URL Input */}
|
||||
<FormControl isInvalid={!!errors.url}>
|
||||
<FormLabel>URL to Track</FormLabel>
|
||||
<Input
|
||||
{...register('url')}
|
||||
placeholder="https://example.com or example.com"
|
||||
size="lg"
|
||||
/>
|
||||
{errors.url && (
|
||||
<Text color="red.500" fontSize="sm" mt={1}>
|
||||
{errors.url.message}
|
||||
</Text>
|
||||
)}
|
||||
<FormHelperText>
|
||||
Enter the URL you want to track. Protocol (http/https) is optional.
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
{/* Method Selection */}
|
||||
<FormControl>
|
||||
<FormLabel>HTTP Method</FormLabel>
|
||||
<Select {...register('method')}>
|
||||
<option value="GET">GET</option>
|
||||
<option value="HEAD">HEAD</option>
|
||||
<option value="POST">POST</option>
|
||||
</Select>
|
||||
<FormHelperText>
|
||||
HTTP method to use for the initial request
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
{/* Analysis Options */}
|
||||
<FormControl>
|
||||
<FormLabel>Analysis Options</FormLabel>
|
||||
<VStack align="start" spacing={3}>
|
||||
<HStack justify="space-between" w="full">
|
||||
<Text>SSL Certificate Analysis</Text>
|
||||
<Switch {...register('enableSSLAnalysis')} colorScheme="brand" />
|
||||
</HStack>
|
||||
<HStack justify="space-between" w="full">
|
||||
<Text>SEO Optimization Analysis</Text>
|
||||
<Switch {...register('enableSEOAnalysis')} colorScheme="brand" />
|
||||
</HStack>
|
||||
<HStack justify="space-between" w="full">
|
||||
<Text>Security Vulnerability Scan</Text>
|
||||
<Switch {...register('enableSecurityAnalysis')} colorScheme="brand" />
|
||||
</HStack>
|
||||
</VStack>
|
||||
<FormHelperText>
|
||||
Enable advanced analysis features (recommended)
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
{/* Advanced Options Toggle */}
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => setShowAdvanced(!showAdvanced)}
|
||||
size="sm"
|
||||
>
|
||||
{showAdvanced ? 'Hide' : 'Show'} Advanced Options
|
||||
</Button>
|
||||
|
||||
{/* Advanced Options */}
|
||||
<Collapse in={showAdvanced}>
|
||||
<VStack spacing={6} align="stretch">
|
||||
<Divider />
|
||||
|
||||
{/* Max Hops */}
|
||||
<FormControl>
|
||||
<FormLabel>Maximum Hops: {maxHops}</FormLabel>
|
||||
<Slider
|
||||
value={maxHops}
|
||||
onChange={(value) => setValue('maxHops', value)}
|
||||
min={1}
|
||||
max={20}
|
||||
step={1}
|
||||
colorScheme="brand"
|
||||
>
|
||||
<SliderTrack>
|
||||
<SliderFilledTrack />
|
||||
</SliderTrack>
|
||||
<SliderThumb />
|
||||
</Slider>
|
||||
<FormHelperText>
|
||||
Maximum number of redirects to follow (1-20)
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
{/* Timeout */}
|
||||
<FormControl>
|
||||
<FormLabel>Timeout (milliseconds)</FormLabel>
|
||||
<NumberInput
|
||||
value={timeout}
|
||||
onChange={(valueString) => setValue('timeout', parseInt(valueString) || 15000)}
|
||||
min={1000}
|
||||
max={30000}
|
||||
step={1000}
|
||||
>
|
||||
<NumberInputField />
|
||||
<NumberInputStepper>
|
||||
<NumberIncrementStepper />
|
||||
<NumberDecrementStepper />
|
||||
</NumberInputStepper>
|
||||
</NumberInput>
|
||||
<FormHelperText>
|
||||
Request timeout in milliseconds (1000-30000)
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
{/* User Agent */}
|
||||
<FormControl>
|
||||
<FormLabel>Custom User Agent</FormLabel>
|
||||
<Input
|
||||
{...register('userAgent')}
|
||||
placeholder="Mozilla/5.0 (compatible; RedirectTracker/2.0)"
|
||||
/>
|
||||
<FormHelperText>
|
||||
Custom User-Agent header (optional)
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
{/* Custom Headers */}
|
||||
<FormControl>
|
||||
<FormLabel>Custom Headers</FormLabel>
|
||||
<Textarea
|
||||
{...register('customHeaders')}
|
||||
placeholder="Accept: application/json X-Custom-Header: value"
|
||||
rows={4}
|
||||
resize="vertical"
|
||||
/>
|
||||
<FormHelperText>
|
||||
Custom headers, one per line in format: Header-Name: Header-Value
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</VStack>
|
||||
</Collapse>
|
||||
|
||||
{/* Rate Limiting Warning */}
|
||||
{!isAuthenticated && (
|
||||
<Alert status="info" borderRadius="md">
|
||||
<AlertIcon />
|
||||
<VStack align="start" spacing={1}>
|
||||
<Text fontWeight="medium">Anonymous Usage</Text>
|
||||
<Text fontSize="sm">
|
||||
Anonymous users are limited to 50 requests per hour.
|
||||
<Text as="span" color="brand.500" fontWeight="medium">
|
||||
{' '}Sign up for higher limits and saved results.
|
||||
</Text>
|
||||
</Text>
|
||||
</VStack>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{/* Submit Button */}
|
||||
<Button
|
||||
type="submit"
|
||||
colorScheme="brand"
|
||||
size="lg"
|
||||
isLoading={trackingMutation.isPending}
|
||||
loadingText="Tracking..."
|
||||
>
|
||||
Track URL
|
||||
</Button>
|
||||
</VStack>
|
||||
</form>
|
||||
<VStack spacing={6} textAlign="center">
|
||||
<Heading as="h3" size="lg">
|
||||
Ready to Analyze Your URLs?
|
||||
</Heading>
|
||||
<Text fontSize="lg" color="gray.600">
|
||||
Get comprehensive redirect analysis, SSL certificate checks, SEO insights, and security reports.
|
||||
</Text>
|
||||
<Button
|
||||
as={RouterLink}
|
||||
to="/track"
|
||||
colorScheme="brand"
|
||||
size="xl"
|
||||
leftIcon={<Icon as={FiSearch} />}
|
||||
px={8}
|
||||
py={6}
|
||||
fontSize="lg"
|
||||
>
|
||||
Analyze URL Redirects
|
||||
</Button>
|
||||
<Text fontSize="sm" color="gray.500">
|
||||
No registration required • Free analysis • Enhanced reporting
|
||||
</Text>
|
||||
</VStack>
|
||||
</CardBody>
|
||||
</Card>
|
||||
|
||||
@@ -702,12 +391,7 @@ export function HomePage() {
|
||||
</VStack>
|
||||
</Container>
|
||||
|
||||
{/* Results */}
|
||||
{trackingResult && (
|
||||
<Container maxW="7xl" py={16}>
|
||||
<TrackingResults result={trackingResult} />
|
||||
</Container>
|
||||
)}
|
||||
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user