update
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
"use client";
|
||||
import { useEffect } from 'react';
|
||||
import Header from "@/components/shared/layout/header/Header";
|
||||
import AboutBanner from "@/components/pages/about/AboutBanner";
|
||||
import AboutServiceComponent from "@/components/pages/about/AboutService";
|
||||
@@ -7,7 +8,15 @@ import AboutScrollProgressButton from "@/components/pages/about/AboutScrollProgr
|
||||
import AboutInitAnimations from "@/components/pages/about/AboutInitAnimations";
|
||||
import AboutStarter from "@/components/pages/about/AboutStarter";
|
||||
|
||||
// Note: Since this is a client component, we'll set metadata via useEffect
|
||||
const page = () => {
|
||||
useEffect(() => {
|
||||
document.title = "About Us - Enterprise Software Development Company | GNX Soft";
|
||||
const metaDescription = document.querySelector('meta[name="description"]');
|
||||
if (metaDescription) {
|
||||
metaDescription.setAttribute('content', 'Learn about GNX Soft - a leading enterprise software development company with expertise in custom software, data replication, AI business intelligence, and comprehensive IT solutions.');
|
||||
}
|
||||
}, []);
|
||||
return (
|
||||
<div className="enterprise-about-page">
|
||||
<Header />
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Metadata } from 'next';
|
||||
import Header from "@/components/shared/layout/header/Header";
|
||||
import CareerBanner from "@/components/pages/career/CareerBanner";
|
||||
import OpenPosition from "@/components/pages/career/OpenPosition";
|
||||
@@ -5,6 +6,21 @@ import Thrive from "@/components/pages/career/Thrive";
|
||||
import Footer from "@/components/shared/layout/footer/Footer";
|
||||
import CareerScrollProgressButton from "@/components/pages/career/CareerScrollProgressButton";
|
||||
import CareerInitAnimations from "@/components/pages/career/CareerInitAnimations";
|
||||
import { generateMetadata as createMetadata } from "@/lib/seo/metadata";
|
||||
|
||||
export const metadata: Metadata = createMetadata({
|
||||
title: "Careers - Join Our Team",
|
||||
description: "Explore career opportunities at GNX Soft. Join our team of talented professionals working on cutting-edge enterprise software solutions. View open positions and apply today.",
|
||||
keywords: [
|
||||
"Careers",
|
||||
"Job Openings",
|
||||
"Software Development Jobs",
|
||||
"Join Our Team",
|
||||
"Tech Careers",
|
||||
"Employment Opportunities",
|
||||
],
|
||||
url: "/career",
|
||||
});
|
||||
|
||||
const page = () => {
|
||||
return (
|
||||
|
||||
@@ -1,8 +1,24 @@
|
||||
import { Metadata } from 'next';
|
||||
import Header from "@/components/shared/layout/header/Header";
|
||||
import CaseItems from "@/components/pages/case-study/CaseItems";
|
||||
import Footer from "@/components/shared/layout/footer/Footer";
|
||||
import CaseStudyScrollProgressButton from "@/components/pages/case-study/CaseStudyScrollProgressButton";
|
||||
import CaseStudyInitAnimations from "@/components/pages/case-study/CaseStudyInitAnimations";
|
||||
import { generateMetadata as createMetadata } from "@/lib/seo/metadata";
|
||||
|
||||
export const metadata: Metadata = createMetadata({
|
||||
title: "Case Studies - Success Stories & Client Projects",
|
||||
description: "Explore our case studies showcasing successful enterprise software development projects, client success stories, and real-world implementations of our technology solutions.",
|
||||
keywords: [
|
||||
"Case Studies",
|
||||
"Success Stories",
|
||||
"Client Projects",
|
||||
"Software Development Portfolio",
|
||||
"Enterprise Solutions Examples",
|
||||
"Client Testimonials",
|
||||
],
|
||||
url: "/case-study",
|
||||
});
|
||||
|
||||
const page = () => {
|
||||
return (
|
||||
|
||||
@@ -1,8 +1,23 @@
|
||||
import { Metadata } from 'next';
|
||||
import Header from "@/components/shared/layout/header/Header";
|
||||
import ContactSection from "@/components/pages/contact/ContactSection";
|
||||
import Footer from "@/components/shared/layout/footer/Footer";
|
||||
import ContactScrollProgressButton from "@/components/pages/contact/ContactScrollProgressButton";
|
||||
import ContactInitAnimations from "@/components/pages/contact/ContactInitAnimations";
|
||||
import { generateMetadata as createMetadata } from "@/lib/seo/metadata";
|
||||
|
||||
export const metadata: Metadata = createMetadata({
|
||||
title: "Contact Us - Get in Touch with Our Team",
|
||||
description: "Contact GNX Soft for enterprise software development solutions. Get a free consultation, discuss your project requirements, or request a quote for our services.",
|
||||
keywords: [
|
||||
"Contact GNX Soft",
|
||||
"Software Development Quote",
|
||||
"Enterprise Solutions Consultation",
|
||||
"Custom Software Inquiry",
|
||||
"Get in Touch",
|
||||
],
|
||||
url: "/contact-us",
|
||||
});
|
||||
|
||||
const page = () => {
|
||||
return (
|
||||
|
||||
@@ -1,8 +1,24 @@
|
||||
import { Metadata } from 'next';
|
||||
import Header from "@/components/shared/layout/header/Header";
|
||||
import BlogItems from "@/components/pages/blog/BlogItems";
|
||||
import Footer from "@/components/shared/layout/footer/Footer";
|
||||
import BlogScrollProgressButton from "@/components/pages/blog/BlogScrollProgressButton";
|
||||
import BlogInitAnimations from "@/components/pages/blog/BlogInitAnimations";
|
||||
import { generateMetadata as createMetadata } from "@/lib/seo/metadata";
|
||||
|
||||
export const metadata: Metadata = createMetadata({
|
||||
title: "Insights & Blog - Technology Trends & Best Practices",
|
||||
description: "Stay updated with the latest insights on enterprise software development, technology trends, best practices, and industry news from GNX Soft's expert team.",
|
||||
keywords: [
|
||||
"Technology Blog",
|
||||
"Software Development Insights",
|
||||
"Tech Trends",
|
||||
"Enterprise Software Blog",
|
||||
"Development Best Practices",
|
||||
"Industry News",
|
||||
],
|
||||
url: "/insights",
|
||||
});
|
||||
|
||||
const page = () => {
|
||||
return (
|
||||
|
||||
@@ -4,6 +4,8 @@ import "@/public/styles/main.scss";
|
||||
import { CookieConsentProvider } from "@/components/shared/layout/CookieConsentContext";
|
||||
import { CookieConsent } from "@/components/shared/layout/CookieConsent";
|
||||
import LayoutWrapper from "@/components/shared/layout/LayoutWrapper";
|
||||
import { generateMetadata as createMetadata } from "@/lib/seo/metadata";
|
||||
import { OrganizationSchema, WebsiteSchema, LocalBusinessSchema } from "@/components/shared/seo/StructuredData";
|
||||
|
||||
const montserrat = Montserrat({
|
||||
subsets: ["latin"],
|
||||
@@ -37,26 +39,22 @@ const inter = Inter({
|
||||
],
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "EnterpriseSoft Solutions | Enterprise Software Development & IT Solutions",
|
||||
description: "Leading enterprise software development company providing custom solutions, system integrations, and digital transformation services for Fortune 500 companies",
|
||||
// Enhanced SEO metadata for root layout
|
||||
export const metadata: Metadata = createMetadata({
|
||||
title: "Enterprise Software Development & IT Solutions",
|
||||
description: "Leading enterprise software development company specializing in custom software, data replication, incident management, AI business intelligence, and comprehensive system integrations for modern businesses.",
|
||||
keywords: [
|
||||
"Enterprise Software",
|
||||
"Custom Development",
|
||||
"System Integration",
|
||||
"Digital Transformation",
|
||||
"Enterprise Solutions",
|
||||
"Software Consulting",
|
||||
"API Development",
|
||||
"Cloud Migration",
|
||||
"Enterprise Software Development",
|
||||
"Custom Software Solutions",
|
||||
"Data Replication Services",
|
||||
"Incident Management SaaS",
|
||||
"AI Business Intelligence",
|
||||
"Backend Engineering",
|
||||
"Frontend Engineering",
|
||||
"Systems Integration",
|
||||
],
|
||||
authors: [
|
||||
{
|
||||
name: "EnterpriseSoft Solutions",
|
||||
url: "https://enterprisesoft.com",
|
||||
},
|
||||
],
|
||||
};
|
||||
url: "/",
|
||||
});
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
@@ -76,14 +74,85 @@ export default function RootLayout({
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
{/* Content Protection Script */}
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
(function() {
|
||||
if (typeof window === 'undefined') return;
|
||||
|
||||
// Wait for DOM to be ready
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Disable right-click
|
||||
document.addEventListener('contextmenu', function(e) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
});
|
||||
|
||||
// Disable keyboard shortcuts
|
||||
document.addEventListener('keydown', function(e) {
|
||||
// Ctrl+C, Ctrl+X, Ctrl+S, Ctrl+A, Ctrl+P, Ctrl+U, Ctrl+I, Ctrl+J
|
||||
if ((e.ctrlKey || e.metaKey) && ['c','x','s','a','p','u','i','j','k'].includes(e.key)) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
// F12
|
||||
if (e.key === 'F12' || e.keyCode === 123) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
// Ctrl+Shift+I, Ctrl+Shift+J, Ctrl+Shift+C
|
||||
if ((e.ctrlKey || e.metaKey) && e.shiftKey && ['I','J','C'].includes(e.key)) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Disable text selection
|
||||
document.addEventListener('selectstart', function(e) {
|
||||
if (e.target.tagName !== 'INPUT' && e.target.tagName !== 'TEXTAREA') {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Disable image dragging
|
||||
document.addEventListener('dragstart', function(e) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
});
|
||||
|
||||
// Disable copy/cut
|
||||
document.addEventListener('copy', function(e) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
});
|
||||
document.addEventListener('cut', function(e) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
});
|
||||
|
||||
// Console warning
|
||||
console.log('%cSTOP!', 'color: red; font-size: 40px; font-weight: bold;');
|
||||
console.log('%c© GNX Soft - All Rights Reserved', 'font-size: 14px;');
|
||||
});
|
||||
})();
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
</head>
|
||||
<body className={`${inter.variable} ${montserrat.variable}`} style={{ scrollBehavior: 'auto', overflow: 'auto' }}>
|
||||
<body className={`${inter.variable} ${montserrat.variable} content-protected`} style={{ scrollBehavior: 'auto', overflow: 'auto' }}>
|
||||
{/* Structured Data for SEO */}
|
||||
<OrganizationSchema />
|
||||
<WebsiteSchema />
|
||||
<LocalBusinessSchema />
|
||||
|
||||
<CookieConsentProvider
|
||||
config={{
|
||||
companyName: "EnterpriseSoft Solutions",
|
||||
privacyPolicyUrl: "/privacy-policy",
|
||||
cookiePolicyUrl: "/cookie-policy",
|
||||
dataControllerEmail: "privacy@enterprisesoft.com",
|
||||
companyName: "GNX Soft",
|
||||
privacyPolicyUrl: "/policy",
|
||||
cookiePolicyUrl: "/policy",
|
||||
dataControllerEmail: "privacy@gnxsoft.com",
|
||||
retentionPeriod: 365,
|
||||
enableAuditLog: true,
|
||||
enableDetailedSettings: true,
|
||||
|
||||
35
gnx-react/app/robots.ts
Normal file
35
gnx-react/app/robots.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { MetadataRoute } from 'next';
|
||||
|
||||
export default function robots(): MetadataRoute.Robots {
|
||||
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://gnxsoft.com';
|
||||
|
||||
return {
|
||||
rules: [
|
||||
{
|
||||
userAgent: '*',
|
||||
allow: '/',
|
||||
disallow: [
|
||||
'/api/',
|
||||
'/admin/',
|
||||
'/_next/',
|
||||
'/private/',
|
||||
'/*.json$',
|
||||
'/*?*',
|
||||
],
|
||||
},
|
||||
{
|
||||
userAgent: 'Googlebot',
|
||||
allow: '/',
|
||||
disallow: ['/api/', '/admin/', '/private/'],
|
||||
},
|
||||
{
|
||||
userAgent: 'Bingbot',
|
||||
allow: '/',
|
||||
disallow: ['/api/', '/admin/', '/private/'],
|
||||
},
|
||||
],
|
||||
sitemap: `${baseUrl}/sitemap.xml`,
|
||||
host: baseUrl,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ import Footer from "@/components/shared/layout/footer/Footer";
|
||||
import ServicesScrollProgressButton from "@/components/pages/services/ServicesScrollProgressButton";
|
||||
import ServicesInitAnimations from "@/components/pages/services/ServicesInitAnimations";
|
||||
import { serviceService, Service } from "@/lib/api/serviceService";
|
||||
import { generateServiceMetadata } from "@/lib/seo/metadata";
|
||||
import { ServiceSchema, BreadcrumbSchema } from "@/components/shared/seo/StructuredData";
|
||||
|
||||
interface ServicePageProps {
|
||||
params: Promise<{
|
||||
@@ -30,21 +32,13 @@ export async function generateStaticParams() {
|
||||
}
|
||||
}
|
||||
|
||||
// Generate metadata for each service page
|
||||
// Generate enhanced metadata for each service page
|
||||
export async function generateMetadata({ params }: ServicePageProps) {
|
||||
try {
|
||||
const { slug } = await params;
|
||||
const service = await serviceService.getServiceBySlug(slug);
|
||||
|
||||
return {
|
||||
title: `${service.title} - GNX Services`,
|
||||
description: service.description,
|
||||
openGraph: {
|
||||
title: service.title,
|
||||
description: service.description,
|
||||
images: service.image_url ? [service.image_url] : [],
|
||||
},
|
||||
};
|
||||
return generateServiceMetadata(service);
|
||||
} catch (error) {
|
||||
return {
|
||||
title: 'Service Not Found - GNX',
|
||||
@@ -64,8 +58,19 @@ const ServicePage = async ({ params }: ServicePageProps) => {
|
||||
notFound();
|
||||
}
|
||||
|
||||
// Breadcrumb data for structured data
|
||||
const breadcrumbItems = [
|
||||
{ name: 'Home', url: '/' },
|
||||
{ name: 'Services', url: '/services' },
|
||||
{ name: service.title, url: `/services/${service.slug}` },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="enterprise-app">
|
||||
{/* SEO Structured Data */}
|
||||
<ServiceSchema service={service} />
|
||||
<BreadcrumbSchema items={breadcrumbItems} />
|
||||
|
||||
<Header />
|
||||
<main className="enterprise-main">
|
||||
<ServiceDetailsBanner service={service} />
|
||||
|
||||
@@ -1,9 +1,27 @@
|
||||
import { Metadata } from 'next';
|
||||
import Header from "@/components/shared/layout/header/Header";
|
||||
import ServicesBanner from "@/components/pages/services/ServicesBanner";
|
||||
import ServiceMain from "@/components/pages/services/ServiceMain";
|
||||
import Footer from "@/components/shared/layout/footer/Footer";
|
||||
import ServicesScrollProgressButton from "@/components/pages/services/ServicesScrollProgressButton";
|
||||
import ServicesInitAnimations from "@/components/pages/services/ServicesInitAnimations";
|
||||
import { generateMetadata as createMetadata } from "@/lib/seo/metadata";
|
||||
|
||||
export const metadata: Metadata = createMetadata({
|
||||
title: "Our Services - Enterprise Software Development",
|
||||
description: "Explore our comprehensive range of enterprise software development services including custom software, data replication, incident management, AI business intelligence, backend & frontend engineering, and systems integration.",
|
||||
keywords: [
|
||||
"Software Development Services",
|
||||
"Custom Software Development",
|
||||
"Data Replication",
|
||||
"Incident Management SaaS",
|
||||
"AI Business Intelligence",
|
||||
"Backend Engineering Services",
|
||||
"Frontend Development",
|
||||
"Systems Integration Services",
|
||||
],
|
||||
url: "/services",
|
||||
});
|
||||
|
||||
const page = () => {
|
||||
return (
|
||||
|
||||
137
gnx-react/app/sitemap.ts
Normal file
137
gnx-react/app/sitemap.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import { MetadataRoute } from 'next';
|
||||
|
||||
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
||||
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://gnxsoft.com';
|
||||
const apiUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000/api';
|
||||
|
||||
// Static pages
|
||||
const staticPages: MetadataRoute.Sitemap = [
|
||||
{
|
||||
url: baseUrl,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'daily',
|
||||
priority: 1.0,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/about-us`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'monthly',
|
||||
priority: 0.9,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/services`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'weekly',
|
||||
priority: 0.9,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/case-study`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'weekly',
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/insights`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'daily',
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/career`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'weekly',
|
||||
priority: 0.7,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/contact-us`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'monthly',
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/support-center`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'monthly',
|
||||
priority: 0.7,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/policy`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'yearly',
|
||||
priority: 0.5,
|
||||
},
|
||||
];
|
||||
|
||||
try {
|
||||
// Fetch dynamic services
|
||||
const servicesResponse = await fetch(`${apiUrl}/services/`, {
|
||||
next: { revalidate: 3600 }, // Revalidate every hour
|
||||
});
|
||||
|
||||
let servicePages: MetadataRoute.Sitemap = [];
|
||||
if (servicesResponse.ok) {
|
||||
const services = await servicesResponse.json();
|
||||
servicePages = services.map((service: any) => ({
|
||||
url: `${baseUrl}/services/${service.slug}`,
|
||||
lastModified: new Date(service.updated_at || service.created_at),
|
||||
changeFrequency: 'weekly' as const,
|
||||
priority: service.featured ? 0.9 : 0.7,
|
||||
}));
|
||||
}
|
||||
|
||||
// Fetch dynamic blog posts
|
||||
const blogResponse = await fetch(`${apiUrl}/blog/`, {
|
||||
next: { revalidate: 3600 },
|
||||
});
|
||||
|
||||
let blogPages: MetadataRoute.Sitemap = [];
|
||||
if (blogResponse.ok) {
|
||||
const posts = await blogResponse.json();
|
||||
blogPages = posts.map((post: any) => ({
|
||||
url: `${baseUrl}/insights/${post.slug}`,
|
||||
lastModified: new Date(post.updated_at || post.published_at),
|
||||
changeFrequency: 'weekly' as const,
|
||||
priority: 0.7,
|
||||
}));
|
||||
}
|
||||
|
||||
// Fetch dynamic case studies
|
||||
const caseStudiesResponse = await fetch(`${apiUrl}/case-studies/`, {
|
||||
next: { revalidate: 3600 },
|
||||
});
|
||||
|
||||
let caseStudyPages: MetadataRoute.Sitemap = [];
|
||||
if (caseStudiesResponse.ok) {
|
||||
const caseStudies = await caseStudiesResponse.json();
|
||||
caseStudyPages = caseStudies.map((study: any) => ({
|
||||
url: `${baseUrl}/case-study/${study.slug}`,
|
||||
lastModified: new Date(study.updated_at || study.created_at),
|
||||
changeFrequency: 'monthly' as const,
|
||||
priority: 0.7,
|
||||
}));
|
||||
}
|
||||
|
||||
// Fetch dynamic career postings
|
||||
const careerResponse = await fetch(`${apiUrl}/career/positions/`, {
|
||||
next: { revalidate: 3600 },
|
||||
});
|
||||
|
||||
let careerPages: MetadataRoute.Sitemap = [];
|
||||
if (careerResponse.ok) {
|
||||
const positions = await careerResponse.json();
|
||||
careerPages = positions.map((position: any) => ({
|
||||
url: `${baseUrl}/career/${position.slug}`,
|
||||
lastModified: new Date(position.updated_at || position.created_at),
|
||||
changeFrequency: 'weekly' as const,
|
||||
priority: 0.6,
|
||||
}));
|
||||
}
|
||||
|
||||
return [...staticPages, ...servicePages, ...blogPages, ...caseStudyPages, ...careerPages];
|
||||
} catch (error) {
|
||||
console.error('Error generating sitemap:', error);
|
||||
// Return at least static pages if API fails
|
||||
return staticPages;
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
105
gnx-react/backend/gnx/middleware/ip_whitelist.py
Normal file
105
gnx-react/backend/gnx/middleware/ip_whitelist.py
Normal file
@@ -0,0 +1,105 @@
|
||||
"""
|
||||
IP Whitelist Middleware
|
||||
Only allows requests from internal network
|
||||
Backend is NOT accessible from public internet
|
||||
"""
|
||||
|
||||
from django.http import HttpResponseForbidden
|
||||
from django.conf import settings
|
||||
import ipaddress
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger('django.security')
|
||||
|
||||
|
||||
class IPWhitelistMiddleware:
|
||||
"""
|
||||
Enterprise Security: Only allow requests from whitelisted IPs (internal network)
|
||||
This ensures backend API is NEVER directly accessible from internet
|
||||
"""
|
||||
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
# Define allowed internal network ranges
|
||||
self.allowed_networks = [
|
||||
ipaddress.ip_network('127.0.0.0/8'), # localhost
|
||||
ipaddress.ip_network('10.0.0.0/8'), # Private Class A
|
||||
ipaddress.ip_network('172.16.0.0/12'), # Private Class B
|
||||
ipaddress.ip_network('192.168.0.0/16'), # Private Class C
|
||||
ipaddress.ip_network('::1/128'), # IPv6 localhost
|
||||
ipaddress.ip_network('fe80::/10'), # IPv6 link-local
|
||||
]
|
||||
|
||||
# Add custom allowed IPs from settings if any
|
||||
custom_allowed = getattr(settings, 'CUSTOM_ALLOWED_IPS', [])
|
||||
for ip in custom_allowed:
|
||||
try:
|
||||
self.allowed_networks.append(ipaddress.ip_network(ip))
|
||||
except ValueError:
|
||||
logger.warning(f"Invalid IP network in CUSTOM_ALLOWED_IPS: {ip}")
|
||||
|
||||
def __call__(self, request):
|
||||
# Get client IP address
|
||||
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
|
||||
if x_forwarded_for:
|
||||
# Get the first IP in the chain (original client)
|
||||
ip = x_forwarded_for.split(',')[0].strip()
|
||||
else:
|
||||
ip = request.META.get('REMOTE_ADDR')
|
||||
|
||||
# In development, allow all
|
||||
if settings.DEBUG:
|
||||
return self.get_response(request)
|
||||
|
||||
try:
|
||||
client_ip = ipaddress.ip_address(ip)
|
||||
|
||||
# Check if IP is in allowed networks
|
||||
is_allowed = any(
|
||||
client_ip in network
|
||||
for network in self.allowed_networks
|
||||
)
|
||||
|
||||
if not is_allowed:
|
||||
logger.warning(
|
||||
f"Blocked external access attempt from IP: {ip} "
|
||||
f"to path: {request.path}"
|
||||
)
|
||||
return HttpResponseForbidden(
|
||||
"Access Denied: This API is for internal use only. "
|
||||
"Backend is not accessible from public internet."
|
||||
)
|
||||
|
||||
except ValueError:
|
||||
logger.error(f"Invalid IP address format: {ip}")
|
||||
return HttpResponseForbidden("Access Denied: Invalid IP address")
|
||||
|
||||
# IP is whitelisted, process request
|
||||
return self.get_response(request)
|
||||
|
||||
|
||||
class SecurityHeadersMiddleware:
|
||||
"""
|
||||
Additional security headers for defense in depth
|
||||
"""
|
||||
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
response = self.get_response(request)
|
||||
|
||||
# Additional security headers
|
||||
response['X-Content-Type-Options'] = 'nosniff'
|
||||
response['X-Frame-Options'] = 'DENY'
|
||||
response['X-XSS-Protection'] = '1; mode=block'
|
||||
response['Referrer-Policy'] = 'strict-origin-when-cross-origin'
|
||||
response['Permissions-Policy'] = 'geolocation=(), microphone=(), camera=()'
|
||||
|
||||
# Remove server identification
|
||||
if 'Server' in response:
|
||||
del response['Server']
|
||||
|
||||
return response
|
||||
|
||||
@@ -60,6 +60,7 @@ INSTALLED_APPS = [
|
||||
MIDDLEWARE = [
|
||||
'corsheaders.middleware.CorsMiddleware',
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'gnx.middleware.ip_whitelist.IPWhitelistMiddleware', # Production: Block external access
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
@@ -109,6 +110,9 @@ AUTH_PASSWORD_VALIDATORS = [
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
'OPTIONS': {
|
||||
'min_length': 12 if not DEBUG else 8, # Enterprise-grade in production
|
||||
}
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
@@ -118,6 +122,44 @@ AUTH_PASSWORD_VALIDATORS = [
|
||||
},
|
||||
]
|
||||
|
||||
# ============================================================================
|
||||
# PRODUCTION SECURITY SETTINGS
|
||||
# Backend accessible ONLY from internal network in production
|
||||
# ============================================================================
|
||||
|
||||
# Security Headers
|
||||
SECURE_SSL_REDIRECT = config('SECURE_SSL_REDIRECT', default=False, cast=bool)
|
||||
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
||||
SECURE_HSTS_SECONDS = config('SECURE_HSTS_SECONDS', default=31536000 if not DEBUG else 0, cast=int)
|
||||
SECURE_HSTS_INCLUDE_SUBDOMAINS = config('SECURE_HSTS_INCLUDE_SUBDOMAINS', default=not DEBUG, cast=bool)
|
||||
SECURE_HSTS_PRELOAD = config('SECURE_HSTS_PRELOAD', default=not DEBUG, cast=bool)
|
||||
SECURE_CONTENT_TYPE_NOSNIFF = True
|
||||
SECURE_BROWSER_XSS_FILTER = True
|
||||
X_FRAME_OPTIONS = 'DENY'
|
||||
SECURE_REFERRER_POLICY = 'strict-origin-when-cross-origin'
|
||||
|
||||
# Session Security
|
||||
SESSION_COOKIE_SECURE = config('SESSION_COOKIE_SECURE', default=not DEBUG, cast=bool)
|
||||
SESSION_COOKIE_HTTPONLY = True
|
||||
SESSION_COOKIE_SAMESITE = 'Strict'
|
||||
SESSION_COOKIE_AGE = 1209600 # 2 weeks
|
||||
|
||||
# CSRF Security
|
||||
CSRF_COOKIE_SECURE = config('CSRF_COOKIE_SECURE', default=not DEBUG, cast=bool)
|
||||
CSRF_COOKIE_HTTPONLY = True
|
||||
CSRF_COOKIE_SAMESITE = 'Strict'
|
||||
CSRF_TRUSTED_ORIGINS = config(
|
||||
'CSRF_TRUSTED_ORIGINS',
|
||||
default='https://gnxsoft.com',
|
||||
cast=lambda v: [s.strip() for s in v.split(',')]
|
||||
)
|
||||
|
||||
# Internal IPs - Backend should only be accessed from these
|
||||
INTERNAL_IPS = ['127.0.0.1', '::1']
|
||||
|
||||
# Custom allowed IPs for IP whitelist middleware (comma-separated)
|
||||
CUSTOM_ALLOWED_IPS = config('CUSTOM_ALLOWED_IPS', default='', cast=lambda v: [s.strip() for s in v.split(',') if s.strip()])
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/4.2/topics/i18n/
|
||||
@@ -170,7 +212,20 @@ REST_FRAMEWORK = {
|
||||
'DEFAULT_RENDERER_CLASSES': [
|
||||
'rest_framework.renderers.JSONRenderer',
|
||||
'rest_framework.renderers.BrowsableAPIRenderer',
|
||||
] if DEBUG else [
|
||||
'rest_framework.renderers.JSONRenderer', # Production: JSON only, no browsable API
|
||||
],
|
||||
# Rate Limiting (Production)
|
||||
'DEFAULT_THROTTLE_CLASSES': [
|
||||
'rest_framework.throttling.AnonRateThrottle',
|
||||
'rest_framework.throttling.UserRateThrottle',
|
||||
] if not DEBUG else [],
|
||||
'DEFAULT_THROTTLE_RATES': {
|
||||
'anon': '100/hour', # Anonymous users
|
||||
'user': '1000/hour', # Authenticated users
|
||||
'burst': '60/min', # Short-term burst
|
||||
'sustained': '1000/day', # Long-term sustained
|
||||
},
|
||||
}
|
||||
|
||||
# CORS Configuration
|
||||
@@ -181,6 +236,11 @@ CORS_ALLOWED_ORIGINS = [
|
||||
"http://127.0.0.1:3001",
|
||||
]
|
||||
|
||||
# Add production origins if configured
|
||||
PRODUCTION_ORIGINS = config('PRODUCTION_ORIGINS', default='', cast=lambda v: [s.strip() for s in v.split(',') if s.strip()])
|
||||
if PRODUCTION_ORIGINS:
|
||||
CORS_ALLOWED_ORIGINS.extend(PRODUCTION_ORIGINS)
|
||||
|
||||
CORS_ALLOW_CREDENTIALS = True
|
||||
|
||||
CORS_ALLOW_ALL_ORIGINS = DEBUG # Only allow all origins in development
|
||||
@@ -229,8 +289,18 @@ LOGGING = {
|
||||
'handlers': {
|
||||
'file': {
|
||||
'level': 'INFO',
|
||||
'class': 'logging.FileHandler',
|
||||
'class': 'logging.handlers.RotatingFileHandler',
|
||||
'filename': BASE_DIR / 'logs' / 'django.log',
|
||||
'maxBytes': 1024 * 1024 * 15, # 15MB
|
||||
'backupCount': 10,
|
||||
'formatter': 'verbose',
|
||||
},
|
||||
'security_file': {
|
||||
'level': 'WARNING',
|
||||
'class': 'logging.handlers.RotatingFileHandler',
|
||||
'filename': BASE_DIR / 'logs' / 'security.log',
|
||||
'maxBytes': 1024 * 1024 * 15, # 15MB
|
||||
'backupCount': 10,
|
||||
'formatter': 'verbose',
|
||||
},
|
||||
'console': {
|
||||
@@ -249,6 +319,11 @@ LOGGING = {
|
||||
'level': 'INFO',
|
||||
'propagate': False,
|
||||
},
|
||||
'django.security': {
|
||||
'handlers': ['security_file', 'console'],
|
||||
'level': 'WARNING',
|
||||
'propagate': False,
|
||||
},
|
||||
'contact': {
|
||||
'handlers': ['file', 'console'],
|
||||
'level': 'DEBUG',
|
||||
@@ -256,3 +331,17 @@ LOGGING = {
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# PRODUCTION NOTES
|
||||
# ============================================================================
|
||||
# In production:
|
||||
# 1. Set DEBUG=False in environment
|
||||
# 2. Backend runs on 127.0.0.1:8000 (internal only)
|
||||
# 3. Firewall blocks external access to port 8000
|
||||
# 4. Frontend proxies API calls through Nginx
|
||||
# 5. IP Whitelist Middleware blocks non-internal IPs
|
||||
# 6. Rate limiting protects against abuse
|
||||
# 7. Browsable API disabled (JSON only)
|
||||
# ============================================================================
|
||||
|
||||
|
||||
61
gnx-react/backend/gnx/throttling.py
Normal file
61
gnx-react/backend/gnx/throttling.py
Normal file
@@ -0,0 +1,61 @@
|
||||
"""
|
||||
Rate Limiting for Enterprise Security
|
||||
Prevents abuse and DDoS attacks
|
||||
"""
|
||||
|
||||
from rest_framework.throttling import SimpleRateThrottle
|
||||
|
||||
|
||||
class BurstRateThrottle(SimpleRateThrottle):
|
||||
"""
|
||||
Short-term burst protection
|
||||
"""
|
||||
scope = 'burst'
|
||||
|
||||
def get_cache_key(self, request, view):
|
||||
if request.user.is_authenticated:
|
||||
ident = request.user.pk
|
||||
else:
|
||||
ident = self.get_ident(request)
|
||||
|
||||
return self.cache_format % {
|
||||
'scope': self.scope,
|
||||
'ident': ident
|
||||
}
|
||||
|
||||
|
||||
class SustainedRateThrottle(SimpleRateThrottle):
|
||||
"""
|
||||
Long-term sustained rate limiting
|
||||
"""
|
||||
scope = 'sustained'
|
||||
|
||||
def get_cache_key(self, request, view):
|
||||
if request.user.is_authenticated:
|
||||
ident = request.user.pk
|
||||
else:
|
||||
ident = self.get_ident(request)
|
||||
|
||||
return self.cache_format % {
|
||||
'scope': self.scope,
|
||||
'ident': ident
|
||||
}
|
||||
|
||||
|
||||
# Add to settings_production.py:
|
||||
"""
|
||||
REST_FRAMEWORK = {
|
||||
...
|
||||
'DEFAULT_THROTTLE_CLASSES': [
|
||||
'gnx.throttling.BurstRateThrottle',
|
||||
'gnx.throttling.SustainedRateThrottle',
|
||||
],
|
||||
'DEFAULT_THROTTLE_RATES': {
|
||||
'burst': '60/min',
|
||||
'sustained': '1000/day',
|
||||
'anon': '100/hour',
|
||||
'user': '1000/hour',
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
0
gnx-react/backend/logs/security.log
Normal file
0
gnx-react/backend/logs/security.log
Normal file
@@ -6,8 +6,8 @@ SECRET_KEY=your-super-secret-production-key-here
|
||||
DEBUG=False
|
||||
ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com,your-server-ip
|
||||
|
||||
# Database (Production)
|
||||
DATABASE_URL=postgresql://username:password@localhost:5432/gnx_production
|
||||
# Database - Using SQLite (default)
|
||||
# SQLite is configured in settings.py - no DATABASE_URL needed
|
||||
|
||||
# Email Configuration (Production)
|
||||
EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
|
||||
|
||||
Binary file not shown.
@@ -17,30 +17,10 @@ class Command(BaseCommand):
|
||||
|
||||
# Create fresh categories
|
||||
categories = {
|
||||
'web-development': {
|
||||
'name': 'Web Development',
|
||||
'description': 'Custom web applications and modern websites',
|
||||
'enterprise-content': {
|
||||
'name': 'Enterprise Content',
|
||||
'description': 'Enterprise-grade solutions for modern businesses',
|
||||
'display_order': 1
|
||||
},
|
||||
'mobile-development': {
|
||||
'name': 'Mobile Development',
|
||||
'description': 'Native and cross-platform mobile applications',
|
||||
'display_order': 2
|
||||
},
|
||||
'api-development': {
|
||||
'name': 'API Development',
|
||||
'description': 'RESTful APIs and microservices architecture',
|
||||
'display_order': 3
|
||||
},
|
||||
'cloud-services': {
|
||||
'name': 'Cloud Services',
|
||||
'description': 'Cloud migration and infrastructure management',
|
||||
'display_order': 4
|
||||
},
|
||||
'ai-ml': {
|
||||
'name': 'AI & Machine Learning',
|
||||
'description': 'Artificial intelligence and machine learning solutions',
|
||||
'display_order': 5
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,141 +41,164 @@ class Command(BaseCommand):
|
||||
# Create fresh services with detailed data
|
||||
services_data = [
|
||||
{
|
||||
'title': 'Enterprise Web Application',
|
||||
'description': 'Build powerful, scalable web applications tailored to your business needs. Our expert developers use modern frameworks and technologies to create solutions that drive growth and efficiency.',
|
||||
'short_description': 'Custom enterprise web applications with modern architecture and scalable infrastructure.',
|
||||
'slug': 'enterprise-web-application',
|
||||
'title': 'Custom Software Development',
|
||||
'description': 'We design and build tailored digital solutions that align precisely with your business goals — delivering reliable, scalable, and future-ready applications that drive measurable value.',
|
||||
'short_description': 'Tailored digital solutions aligned with your business goals.',
|
||||
'slug': 'custom-software-development',
|
||||
'icon': 'code',
|
||||
'price': '25000.00',
|
||||
'category_slug': 'web-development',
|
||||
'duration': '8-12 weeks',
|
||||
'deliverables': 'Complete Web Application, Admin Dashboard, User Authentication System, Database Design, API Integration, Responsive Design, Security Implementation, Testing Suite, Deployment Setup, Documentation, Training Materials, 3 Months Support',
|
||||
'technologies': 'React, Next.js, TypeScript, Node.js, PostgreSQL, Redis, AWS, Docker, Kubernetes, Jest, Cypress',
|
||||
'process_steps': 'Discovery & Requirements, UI/UX Design, Architecture Planning, Backend Development, Frontend Development, API Development, Database Setup, Security Implementation, Testing & QA, Deployment, Training, Launch Support',
|
||||
'price': '50000.00',
|
||||
'category_slug': 'enterprise-content',
|
||||
'duration': '12-20 weeks',
|
||||
'deliverables': 'Custom Application, System Architecture, Database Design, API Development, User Interface, Testing Suite, Deployment Setup, Documentation, Training, 6 Months Support',
|
||||
'technologies': 'React, Next.js, TypeScript, Node.js, Python, PostgreSQL, MongoDB, Redis, AWS, Docker, Kubernetes',
|
||||
'process_steps': 'Requirements Analysis, Solution Design, Architecture Planning, Development, Testing, Deployment, Training, Launch Support',
|
||||
'featured': True,
|
||||
'display_order': 1,
|
||||
'features': [
|
||||
{'title': 'Scalable Architecture', 'description': 'Built with microservices and cloud-native architecture', 'icon': 'cloud'},
|
||||
{'title': 'Advanced Security', 'description': 'Enterprise-grade security with authentication and authorization', 'icon': 'shield'},
|
||||
{'title': 'Real-time Features', 'description': 'WebSocket integration for real-time updates and notifications', 'icon': 'bolt'},
|
||||
{'title': 'Mobile Responsive', 'description': 'Fully responsive design that works on all devices', 'icon': 'mobile'},
|
||||
{'title': 'Performance Optimized', 'description': 'Optimized for speed and performance with caching strategies', 'icon': 'gauge'},
|
||||
{'title': 'Analytics Integration', 'description': 'Built-in analytics and reporting capabilities', 'icon': 'chart-bar'}
|
||||
{'title': 'Custom Solutions', 'description': 'Tailored applications built for your specific needs', 'icon': 'cogs'},
|
||||
{'title': 'Scalable Architecture', 'description': 'Built to grow with your business', 'icon': 'expand'},
|
||||
{'title': 'Future-Ready', 'description': 'Modern tech stack for long-term sustainability', 'icon': 'rocket'},
|
||||
{'title': 'Business Alignment', 'description': 'Solutions that drive measurable business value', 'icon': 'chart-line'},
|
||||
{'title': 'Reliable', 'description': 'Enterprise-grade reliability and performance', 'icon': 'shield'},
|
||||
{'title': 'Full Support', 'description': 'Comprehensive training and ongoing support', 'icon': 'headset'}
|
||||
]
|
||||
},
|
||||
{
|
||||
'title': 'Cross-Platform Mobile App',
|
||||
'description': 'Create stunning mobile applications for iOS and Android platforms. We deliver native and cross-platform solutions that provide exceptional user experiences and drive engagement.',
|
||||
'short_description': 'Native and cross-platform mobile applications with modern UI/UX design.',
|
||||
'slug': 'cross-platform-mobile-app',
|
||||
'icon': 'mobile',
|
||||
'title': 'Data Replication',
|
||||
'description': 'We ensure secure, real-time synchronization across your databases and systems — maintaining data accuracy, consistency, and availability for mission-critical operations.',
|
||||
'short_description': 'Secure real-time data synchronization across systems.',
|
||||
'slug': 'data-replication',
|
||||
'icon': 'sync',
|
||||
'price': '35000.00',
|
||||
'category_slug': 'mobile-development',
|
||||
'duration': '12-16 weeks',
|
||||
'deliverables': 'iOS Mobile App, Android Mobile App, Admin Panel, Backend API, Push Notifications, Offline Support, App Store Submission, Google Play Submission, User Documentation, Admin Guide, Testing Suite, 6 Months Support',
|
||||
'technologies': 'React Native, TypeScript, Node.js, MongoDB, Firebase, AWS, App Store Connect, Google Play Console, Jest, Detox',
|
||||
'process_steps': 'Market Research, UI/UX Design, Prototyping, Backend Development, Mobile App Development, API Integration, Testing, App Store Optimization, Submission Process, Launch Strategy, Post-launch Support',
|
||||
'category_slug': 'enterprise-content',
|
||||
'duration': '8-12 weeks',
|
||||
'deliverables': 'Replication System, Data Pipeline, Monitoring Dashboard, Conflict Resolution, Backup Strategy, Security Configuration, Performance Tuning, Documentation, 6 Months Support',
|
||||
'technologies': 'PostgreSQL, MySQL, MongoDB, Redis, Apache Kafka, AWS DMS, Azure Data Sync, Change Data Capture, ETL Tools',
|
||||
'process_steps': 'Data Assessment, Architecture Design, Pipeline Setup, Testing, Security Implementation, Monitoring Setup, Optimization, Documentation, Go-live',
|
||||
'featured': True,
|
||||
'display_order': 2,
|
||||
'features': [
|
||||
{'title': 'Cross-Platform', 'description': 'Single codebase for both iOS and Android platforms', 'icon': 'mobile'},
|
||||
{'title': 'Native Performance', 'description': 'Optimized performance using native components and modules', 'icon': 'gauge'},
|
||||
{'title': 'Offline Support', 'description': 'Full offline functionality with data synchronization', 'icon': 'wifi'},
|
||||
{'title': 'Push Notifications', 'description': 'Real-time push notifications for user engagement', 'icon': 'bell'},
|
||||
{'title': 'App Store Ready', 'description': 'Complete app store submission and approval process', 'icon': 'store'},
|
||||
{'title': 'Analytics Dashboard', 'description': 'Comprehensive analytics and user behavior tracking', 'icon': 'chart-line'}
|
||||
{'title': 'Real-Time Sync', 'description': 'Instant data synchronization across systems', 'icon': 'bolt'},
|
||||
{'title': 'Data Accuracy', 'description': 'Ensures consistency and accuracy across databases', 'icon': 'check-circle'},
|
||||
{'title': 'High Availability', 'description': 'Mission-critical data always available', 'icon': 'server'},
|
||||
{'title': 'Secure Transfer', 'description': 'Encrypted data transmission and storage', 'icon': 'lock'},
|
||||
{'title': 'Conflict Resolution', 'description': 'Automated handling of data conflicts', 'icon': 'cogs'},
|
||||
{'title': 'Performance', 'description': 'Optimized for minimal impact on operations', 'icon': 'gauge'}
|
||||
]
|
||||
},
|
||||
{
|
||||
'title': 'RESTful API Development',
|
||||
'description': 'Build robust, scalable APIs that power your applications and enable seamless integration with third-party services. Our APIs are designed for performance, security, and maintainability.',
|
||||
'short_description': 'Enterprise-grade RESTful APIs with comprehensive documentation and security.',
|
||||
'slug': 'restful-api-development',
|
||||
'icon': 'api',
|
||||
'price': '15000.00',
|
||||
'category_slug': 'api-development',
|
||||
'duration': '4-6 weeks',
|
||||
'deliverables': 'RESTful API, API Documentation, Authentication System, Rate Limiting, API Testing Suite, Postman Collection, SDK Development, Integration Examples, Performance Monitoring, Security Audit, Deployment Guide, 3 Months Support',
|
||||
'technologies': 'Node.js, Express, TypeScript, PostgreSQL, Redis, JWT, Swagger, Postman, Jest, AWS API Gateway, Docker',
|
||||
'process_steps': 'API Planning, Database Design, Authentication Setup, Endpoint Development, Documentation, Testing, Security Review, Performance Optimization, Deployment, Integration Testing, Monitoring Setup',
|
||||
'featured': False,
|
||||
'title': 'Incident Management SaaS',
|
||||
'description': 'We provide intelligent, cloud-based incident management tools that empower teams to detect, respond, and resolve issues faster — minimizing downtime and protecting customer trust.',
|
||||
'short_description': 'Cloud-based incident management for faster issue resolution.',
|
||||
'slug': 'incident-management-saas',
|
||||
'icon': 'bell',
|
||||
'price': '45000.00',
|
||||
'category_slug': 'enterprise-content',
|
||||
'duration': '16-20 weeks',
|
||||
'deliverables': 'SaaS Platform, Incident Dashboard, Alert System, Integration APIs, Mobile App, Reporting Tools, Analytics, Automation Workflows, Documentation, 12 Months Support',
|
||||
'technologies': 'React, Node.js, TypeScript, PostgreSQL, Redis, WebSockets, AWS, Kubernetes, PagerDuty API, Slack Integration, Twilio',
|
||||
'process_steps': 'Requirements Analysis, Platform Design, Core Development, Integration Development, Testing, Security Audit, Deployment, Training, Launch',
|
||||
'featured': True,
|
||||
'display_order': 3,
|
||||
'features': [
|
||||
{'title': 'RESTful Design', 'description': 'Clean, intuitive API design following REST principles', 'icon': 'code'},
|
||||
{'title': 'Comprehensive Documentation', 'description': 'Interactive API documentation with examples', 'icon': 'book'},
|
||||
{'title': 'Authentication & Security', 'description': 'JWT-based authentication with rate limiting', 'icon': 'shield'},
|
||||
{'title': 'Performance Optimized', 'description': 'Caching and optimization for high performance', 'icon': 'gauge'},
|
||||
{'title': 'SDK Support', 'description': 'Client SDKs for easy integration', 'icon': 'puzzle-piece'},
|
||||
{'title': 'Monitoring & Analytics', 'description': 'Built-in monitoring and usage analytics', 'icon': 'chart-bar'}
|
||||
]
|
||||
},
|
||||
{
|
||||
'title': 'Cloud Migration & DevOps',
|
||||
'description': 'Migrate your existing infrastructure to the cloud with minimal downtime. We help you leverage cloud technologies for improved scalability, security, and cost efficiency.',
|
||||
'short_description': 'Complete cloud migration with DevOps automation and monitoring.',
|
||||
'slug': 'cloud-migration-devops',
|
||||
'icon': 'cloud',
|
||||
'price': '45000.00',
|
||||
'category_slug': 'cloud-services',
|
||||
'duration': '16-20 weeks',
|
||||
'deliverables': 'Cloud Infrastructure Setup, Application Migration, CI/CD Pipeline, Monitoring Setup, Security Configuration, Backup Strategy, Disaster Recovery Plan, Cost Optimization, Performance Tuning, Documentation, Training, 6 Months Support',
|
||||
'technologies': 'AWS, Azure, Google Cloud, Docker, Kubernetes, Terraform, Jenkins, GitLab CI, Prometheus, Grafana, ELK Stack',
|
||||
'process_steps': 'Infrastructure Assessment, Migration Planning, Security Audit, Infrastructure Setup, Application Migration, CI/CD Implementation, Monitoring Setup, Testing, Go-live, Optimization, Documentation, Training',
|
||||
'featured': True,
|
||||
'display_order': 4,
|
||||
'features': [
|
||||
{'title': 'Zero Downtime Migration', 'description': 'Seamless migration with minimal service interruption', 'icon': 'clock'},
|
||||
{'title': 'Cost Optimization', 'description': 'Optimized cloud resources for maximum cost efficiency', 'icon': 'dollar-sign'},
|
||||
{'title': 'Security First', 'description': 'Enhanced security with cloud-native security features', 'icon': 'shield'},
|
||||
{'title': 'Automated DevOps', 'description': 'Complete CI/CD pipeline with automated deployments', 'icon': 'cogs'},
|
||||
{'title': 'Monitoring & Alerting', 'description': 'Comprehensive monitoring and alerting system', 'icon': 'bell'},
|
||||
{'title': 'Scalability', 'description': 'Auto-scaling infrastructure for handling traffic spikes', 'icon': 'expand'}
|
||||
{'title': 'Intelligent Detection', 'description': 'AI-powered incident detection and alerting', 'icon': 'brain'},
|
||||
{'title': 'Rapid Response', 'description': 'Tools to respond and resolve issues faster', 'icon': 'bolt'},
|
||||
{'title': 'Minimize Downtime', 'description': 'Reduce system downtime and service disruption', 'icon': 'clock'},
|
||||
{'title': 'Team Collaboration', 'description': 'Empower teams with collaborative tools', 'icon': 'users'},
|
||||
{'title': 'Cloud-Based', 'description': 'Accessible anywhere, anytime', 'icon': 'cloud'},
|
||||
{'title': 'Customer Trust', 'description': 'Protect and maintain customer confidence', 'icon': 'shield'}
|
||||
]
|
||||
},
|
||||
{
|
||||
'title': 'AI-Powered Business Intelligence',
|
||||
'description': 'Transform your business data into actionable insights with AI-powered analytics and machine learning solutions. Make data-driven decisions with advanced predictive analytics.',
|
||||
'short_description': 'AI-powered business intelligence and predictive analytics platform.',
|
||||
'description': 'We transform enterprise data into actionable insights with advanced analytics and AI — enabling smarter decisions, performance optimization, and data-driven innovation.',
|
||||
'short_description': 'Transform data into insights with AI and advanced analytics.',
|
||||
'slug': 'ai-powered-business-intelligence',
|
||||
'icon': 'brain',
|
||||
'price': '55000.00',
|
||||
'category_slug': 'ai-ml',
|
||||
'price': '60000.00',
|
||||
'category_slug': 'enterprise-content',
|
||||
'duration': '20-24 weeks',
|
||||
'deliverables': 'AI Analytics Platform, Machine Learning Models, Data Pipeline, Dashboard Development, Predictive Analytics, Report Generation, API Integration, Data Visualization, Model Training, Performance Monitoring, Documentation, 12 Months Support',
|
||||
'technologies': 'Python, TensorFlow, PyTorch, Pandas, NumPy, Scikit-learn, React, D3.js, PostgreSQL, Redis, AWS SageMaker, Docker',
|
||||
'process_steps': 'Data Analysis, Model Selection, Data Pipeline Development, Model Training, Dashboard Development, API Development, Testing, Deployment, Performance Monitoring, Optimization, Documentation, Training',
|
||||
'deliverables': 'BI Platform, Machine Learning Models, Data Pipeline, Interactive Dashboards, Predictive Analytics, Report Generation, API Integration, Model Training, Documentation, 12 Months Support',
|
||||
'technologies': 'Python, TensorFlow, PyTorch, Pandas, Scikit-learn, React, D3.js, Tableau, PostgreSQL, Snowflake, AWS SageMaker, Apache Spark',
|
||||
'process_steps': 'Data Analysis, Model Development, Dashboard Design, Pipeline Development, Training, Integration, Testing, Optimization, Deployment, Training',
|
||||
'featured': True,
|
||||
'display_order': 5,
|
||||
'display_order': 4,
|
||||
'features': [
|
||||
{'title': 'Predictive Analytics', 'description': 'Advanced ML models for business forecasting', 'icon': 'chart-line'},
|
||||
{'title': 'Real-time Insights', 'description': 'Live data processing and real-time analytics', 'icon': 'bolt'},
|
||||
{'title': 'Interactive Dashboards', 'description': 'Beautiful, interactive data visualization', 'icon': 'chart-bar'},
|
||||
{'title': 'Automated Reports', 'description': 'Automated report generation and distribution', 'icon': 'file-alt'},
|
||||
{'title': 'Data Integration', 'description': 'Seamless integration with existing data sources', 'icon': 'plug'},
|
||||
{'title': 'Scalable Architecture', 'description': 'Cloud-native architecture for handling big data', 'icon': 'cloud'}
|
||||
{'title': 'Actionable Insights', 'description': 'Transform raw data into meaningful insights', 'icon': 'lightbulb'},
|
||||
{'title': 'Advanced Analytics', 'description': 'AI-powered analytics and predictions', 'icon': 'chart-line'},
|
||||
{'title': 'Smart Decisions', 'description': 'Enable data-driven decision making', 'icon': 'brain'},
|
||||
{'title': 'Performance Optimization', 'description': 'Identify and optimize business performance', 'icon': 'gauge'},
|
||||
{'title': 'Data-Driven Innovation', 'description': 'Unlock new opportunities through data', 'icon': 'rocket'},
|
||||
{'title': 'Enterprise Scale', 'description': 'Built to handle enterprise data volumes', 'icon': 'database'}
|
||||
]
|
||||
},
|
||||
{
|
||||
'title': 'E-commerce Platform',
|
||||
'description': 'Build a complete e-commerce solution with modern features, secure payment processing, and advanced analytics. Create an online store that converts visitors into customers.',
|
||||
'short_description': 'Complete e-commerce platform with payment processing and analytics.',
|
||||
'slug': 'ecommerce-platform',
|
||||
'icon': 'shopping-cart',
|
||||
'price': '30000.00',
|
||||
'category_slug': 'web-development',
|
||||
'title': 'Backend Engineering',
|
||||
'description': 'We architect and optimize high-performance backend systems — ensuring your applications run securely, efficiently, and scale effortlessly as your business grows.',
|
||||
'short_description': 'High-performance backend systems that scale effortlessly.',
|
||||
'slug': 'backend-engineering',
|
||||
'icon': 'server',
|
||||
'price': '40000.00',
|
||||
'category_slug': 'enterprise-content',
|
||||
'duration': '10-16 weeks',
|
||||
'deliverables': 'Backend Architecture, API Development, Database Design, Authentication System, Caching Strategy, Performance Optimization, Security Implementation, Testing, Documentation, 6 Months Support',
|
||||
'technologies': 'Node.js, Python, Django, FastAPI, PostgreSQL, MongoDB, Redis, RabbitMQ, GraphQL, REST, Docker, Kubernetes, AWS',
|
||||
'process_steps': 'Architecture Design, Database Modeling, API Development, Security Implementation, Optimization, Testing, Deployment, Monitoring Setup, Documentation',
|
||||
'featured': True,
|
||||
'display_order': 5,
|
||||
'features': [
|
||||
{'title': 'High Performance', 'description': 'Optimized for speed and efficiency', 'icon': 'gauge'},
|
||||
{'title': 'Secure', 'description': 'Enterprise-grade security implementation', 'icon': 'shield'},
|
||||
{'title': 'Scalable', 'description': 'Scales effortlessly with business growth', 'icon': 'expand'},
|
||||
{'title': 'Efficient', 'description': 'Resource-optimized for cost efficiency', 'icon': 'cogs'},
|
||||
{'title': 'Reliable', 'description': 'Built for uptime and reliability', 'icon': 'check-circle'},
|
||||
{'title': 'Modern Architecture', 'description': 'Microservices and cloud-native design', 'icon': 'cloud'}
|
||||
]
|
||||
},
|
||||
{
|
||||
'title': 'Frontend Engineering',
|
||||
'description': 'We craft responsive, accessible, and engaging user interfaces — blending performance with design to deliver exceptional digital experiences across devices and platforms.',
|
||||
'short_description': 'Responsive, engaging UIs for exceptional digital experiences.',
|
||||
'slug': 'frontend-engineering',
|
||||
'icon': 'palette',
|
||||
'price': '35000.00',
|
||||
'category_slug': 'enterprise-content',
|
||||
'duration': '10-14 weeks',
|
||||
'deliverables': 'E-commerce Website, Admin Dashboard, Payment Integration, Inventory Management, Order Management, Customer Portal, Analytics Dashboard, SEO Optimization, Mobile App, Testing Suite, Documentation, 6 Months Support',
|
||||
'technologies': 'React, Next.js, Node.js, PostgreSQL, Stripe, PayPal, AWS, Redis, Elasticsearch, Jest, Cypress',
|
||||
'process_steps': 'Requirements Analysis, UI/UX Design, Database Design, Backend Development, Frontend Development, Payment Integration, Testing, SEO Optimization, Performance Tuning, Launch, Marketing Setup, Support',
|
||||
'featured': False,
|
||||
'deliverables': 'Frontend Application, Component Library, Responsive Design, Accessibility Implementation, Performance Optimization, Testing Suite, Documentation, Style Guide, 6 Months Support',
|
||||
'technologies': 'React, Next.js, TypeScript, Vue.js, Tailwind CSS, Material-UI, Redux, GraphQL, Jest, Cypress, Webpack, Vite',
|
||||
'process_steps': 'UI/UX Design, Component Development, Responsive Implementation, Accessibility Audit, Performance Optimization, Testing, Browser Compatibility, Deployment',
|
||||
'featured': True,
|
||||
'display_order': 6,
|
||||
'features': [
|
||||
{'title': 'Secure Payments', 'description': 'Multiple payment gateways with PCI compliance', 'icon': 'credit-card'},
|
||||
{'title': 'Inventory Management', 'description': 'Advanced inventory tracking and management', 'icon': 'box'},
|
||||
{'title': 'Customer Analytics', 'description': 'Detailed customer behavior and sales analytics', 'icon': 'chart-bar'},
|
||||
{'title': 'Mobile Optimized', 'description': 'Fully responsive design for mobile shopping', 'icon': 'mobile'},
|
||||
{'title': 'SEO Ready', 'description': 'Built-in SEO optimization for better visibility', 'icon': 'search'},
|
||||
{'title': 'Multi-vendor Support', 'description': 'Support for multiple vendors and marketplace', 'icon': 'store'}
|
||||
{'title': 'Responsive Design', 'description': 'Flawless experience across all devices', 'icon': 'mobile'},
|
||||
{'title': 'Accessible', 'description': 'WCAG compliant for all users', 'icon': 'universal-access'},
|
||||
{'title': 'Engaging UX', 'description': 'Beautiful, intuitive user experiences', 'icon': 'heart'},
|
||||
{'title': 'High Performance', 'description': 'Optimized for speed and efficiency', 'icon': 'bolt'},
|
||||
{'title': 'Modern Design', 'description': 'Contemporary design patterns and aesthetics', 'icon': 'palette'},
|
||||
{'title': 'Cross-Platform', 'description': 'Works seamlessly across browsers and platforms', 'icon': 'window-maximize'}
|
||||
]
|
||||
},
|
||||
{
|
||||
'title': 'External Systems Integrations',
|
||||
'description': 'We connect everything — from fiscal printers and payment terminals to ERP and cloud platforms — enabling enterprises to operate seamlessly across physical and digital environments.',
|
||||
'short_description': 'Connect systems for seamless enterprise operations.',
|
||||
'slug': 'external-systems-integrations',
|
||||
'icon': 'plug',
|
||||
'price': '45000.00',
|
||||
'category_slug': 'enterprise-content',
|
||||
'duration': '12-18 weeks',
|
||||
'deliverables': 'Integration Platform, API Connectors, Payment Gateway Integration, ERP Integration, Device Integration, Middleware Development, Testing Suite, Security Implementation, Documentation, 6 Months Support',
|
||||
'technologies': 'REST APIs, GraphQL, SOAP, gRPC, Kafka, RabbitMQ, OAuth 2.0, SAP, Salesforce, Stripe, PayPal, IoT Protocols, Node.js, Python',
|
||||
'process_steps': 'Systems Analysis, Integration Design, API Development, Device Setup, Testing, Security Review, Deployment, Monitoring, Documentation, Training',
|
||||
'featured': True,
|
||||
'display_order': 7,
|
||||
'features': [
|
||||
{'title': 'Universal Connectivity', 'description': 'Connect any system, device, or platform', 'icon': 'network-wired'},
|
||||
{'title': 'Payment Integration', 'description': 'Payment terminals and gateway integration', 'icon': 'credit-card'},
|
||||
{'title': 'ERP Integration', 'description': 'Seamless ERP and business system integration', 'icon': 'building'},
|
||||
{'title': 'IoT Devices', 'description': 'Connect physical devices like fiscal printers', 'icon': 'print'},
|
||||
{'title': 'Cloud Platforms', 'description': 'Integration with major cloud platforms', 'icon': 'cloud'},
|
||||
{'title': 'Seamless Operations', 'description': 'Unified operations across all environments', 'icon': 'sync'}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -57,11 +57,6 @@ export default function ServicesList() {
|
||||
)}
|
||||
</div>
|
||||
<p className="card-text">{service.description}</p>
|
||||
{service.price && (
|
||||
<p className="text-muted">
|
||||
<strong>Starting at: ${service.price}</strong>
|
||||
</p>
|
||||
)}
|
||||
<div className="mt-auto">
|
||||
<a
|
||||
href={`/services/${service.slug}`}
|
||||
|
||||
@@ -227,18 +227,12 @@ const AboutBanner = () => {
|
||||
|
||||
{/* Social Links */}
|
||||
<div className="social-links">
|
||||
<Link href="https://www.linkedin.com/company/enterprisesoft-solutions" target="_blank" className="social-link">
|
||||
<Link href="https://www.linkedin.com/company/gnxtech" target="_blank" className="social-link">
|
||||
<i className="fa-brands fa-linkedin-in"></i>
|
||||
</Link>
|
||||
<Link href="https://github.com/enterprisesoft" target="_blank" className="social-link">
|
||||
<Link href="https://github.com/gnxtech" target="_blank" className="social-link">
|
||||
<i className="fa-brands fa-github"></i>
|
||||
</Link>
|
||||
<Link href="https://www.twitter.com/enterprisesoft" target="_blank" className="social-link">
|
||||
<i className="fa-brands fa-twitter"></i>
|
||||
</Link>
|
||||
<Link href="https://stackoverflow.com/teams/enterprisesoft" target="_blank" className="social-link">
|
||||
<i className="fa-brands fa-stack-overflow"></i>
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
@@ -81,10 +81,10 @@ const AboutServiceComponent = () => {
|
||||
{serviceData?.badge_text || "About Our Company"}
|
||||
</div>
|
||||
<h2 className="title-anim">
|
||||
{serviceData?.title || "Enterprise Technology Leaders"}
|
||||
{serviceData?.title || "GNX Soft Ltd. - Software Excellence"}
|
||||
</h2>
|
||||
<p>
|
||||
{serviceData?.description || "Founded 2008, EnterpriseSoft Solutions has emerged as a premier enterprise software company, serving Fortune 500 companies and innovative startups worldwide. Our team of 200+ engineers, architects, and consultants specializes in delivering mission-critical software solutions that drive digital transformation and business growth."}
|
||||
{serviceData?.description || "Founded in 2020, GNX Soft Ltd. has emerged as a premier enterprise software company, delivering mission-critical software solutions across various industries. Our team of expert engineers, architects, and consultants specializes in custom software development, data replication, incident management, and comprehensive system integrations that drive digital transformation and business growth."}
|
||||
</p>
|
||||
<div className="enterprise-features">
|
||||
<div className="row">
|
||||
@@ -144,7 +144,7 @@ const AboutServiceComponent = () => {
|
||||
</div>
|
||||
<div className="feature-content">
|
||||
<h6>Global Reach</h6>
|
||||
<p>Offices in 5 Countries</p>
|
||||
<p>Based in Bulgaria, Serving Worldwide</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -208,26 +208,6 @@ const BlogSingle = () => {
|
||||
<i className="fa-brands fa-linkedin-in"></i>
|
||||
<span>LinkedIn</span>
|
||||
</Link>
|
||||
<Link
|
||||
href={`https://twitter.com/intent/tweet?url=${typeof window !== 'undefined' ? encodeURIComponent(window.location.href) : ''}&text=${encodeURIComponent(post.title)}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="share-btn share-twitter"
|
||||
aria-label="Share on Twitter"
|
||||
>
|
||||
<i className="fa-brands fa-twitter"></i>
|
||||
<span>Twitter</span>
|
||||
</Link>
|
||||
<Link
|
||||
href={`https://www.facebook.com/sharer/sharer.php?u=${typeof window !== 'undefined' ? encodeURIComponent(window.location.href) : ''}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="share-btn share-facebook"
|
||||
aria-label="Share on Facebook"
|
||||
>
|
||||
<i className="fa-brands fa-facebook-f"></i>
|
||||
<span>Facebook</span>
|
||||
</Link>
|
||||
<button
|
||||
onClick={() => {
|
||||
if (typeof window !== 'undefined' && navigator.clipboard) {
|
||||
|
||||
@@ -114,38 +114,20 @@ const CareerBanner = () => {
|
||||
<ul className="social">
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.facebook.com/"
|
||||
href="https://www.linkedin.com/company/gnxtech"
|
||||
target="_blank"
|
||||
aria-label="share us on facebook"
|
||||
>
|
||||
<i className="fa-brands fa-facebook-f"></i>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.twitter.com/"
|
||||
target="_blank"
|
||||
aria-label="share us on twitter"
|
||||
>
|
||||
<i className="fa-brands fa-twitter"></i>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.pinterest.com/"
|
||||
target="_blank"
|
||||
aria-label="share us on pinterest"
|
||||
aria-label="connect with us on linkedin"
|
||||
>
|
||||
<i className="fa-brands fa-linkedin-in"></i>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.instagram.com/"
|
||||
href="https://github.com/gnxtech"
|
||||
target="_blank"
|
||||
aria-label="share us on instagram"
|
||||
aria-label="view our code on github"
|
||||
>
|
||||
<i className="fa-brands fa-instagram"></i>
|
||||
<i className="fa-brands fa-github"></i>
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -59,7 +59,7 @@ const ServiceDetails = ({ service }: ServiceDetailsProps) => {
|
||||
|
||||
<div className="enterprise-stats mb-4">
|
||||
<div className="row g-3">
|
||||
<div className="col-6">
|
||||
<div className="col-12">
|
||||
<div className="enterprise-stat-card">
|
||||
<div className="stat-icon">
|
||||
<i className="fa-solid fa-star"></i>
|
||||
@@ -72,19 +72,6 @@ const ServiceDetails = ({ service }: ServiceDetailsProps) => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-6">
|
||||
<div className="enterprise-stat-card">
|
||||
<div className="stat-icon">
|
||||
<i className="fa-solid fa-dollar-sign"></i>
|
||||
</div>
|
||||
<div className="stat-content">
|
||||
<div className="stat-number">
|
||||
{service.formatted_price}
|
||||
</div>
|
||||
<div className="stat-label">Starting Price</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -137,9 +137,6 @@ const ServiceMain = () => {
|
||||
</p>
|
||||
|
||||
<div className="service-footer">
|
||||
<div className="service-price">
|
||||
{service.formatted_price}
|
||||
</div>
|
||||
<Link href={`/services/${service.slug}`} className="service-link">
|
||||
<span>View Details</span>
|
||||
<i className="fa-solid fa-arrow-right"></i>
|
||||
|
||||
@@ -61,11 +61,8 @@ const ServicePricing = ({ service }: ServicePricingProps) => {
|
||||
{service.title}
|
||||
</h3>
|
||||
<div className="price-display">
|
||||
<span className="price-amount">
|
||||
{service.formatted_price}
|
||||
</span>
|
||||
<span className="price-period">
|
||||
Starting Price
|
||||
Contact Us for Pricing
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -85,38 +85,20 @@ const ServicesBanner = () => {
|
||||
<ul className="social">
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.facebook.com/"
|
||||
href="https://www.linkedin.com/company/gnxtech"
|
||||
target="_blank"
|
||||
aria-label="share us on facebook"
|
||||
>
|
||||
<i className="fa-brands fa-facebook-f"></i>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.twitter.com/"
|
||||
target="_blank"
|
||||
aria-label="share us on twitter"
|
||||
>
|
||||
<i className="fa-brands fa-twitter"></i>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.pinterest.com/"
|
||||
target="_blank"
|
||||
aria-label="share us on pinterest"
|
||||
aria-label="connect with us on linkedin"
|
||||
>
|
||||
<i className="fa-brands fa-linkedin-in"></i>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.instagram.com/"
|
||||
href="https://github.com/gnxtech"
|
||||
target="_blank"
|
||||
aria-label="share us on instagram"
|
||||
aria-label="view our code on github"
|
||||
>
|
||||
<i className="fa-brands fa-instagram"></i>
|
||||
<i className="fa-brands fa-github"></i>
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -48,14 +48,6 @@ const Transform = ({ service }: TransformProps) => {
|
||||
<p className="enterprise-section-description">
|
||||
{service.description}
|
||||
</p>
|
||||
{service.formatted_price && (
|
||||
<div className="mt-4">
|
||||
<div className="price-highlight">
|
||||
<span className="price-label">Starting from</span>
|
||||
<span className="price-value">{service.formatted_price}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
207
gnx-react/components/shared/OptimizedImage.tsx
Normal file
207
gnx-react/components/shared/OptimizedImage.tsx
Normal file
@@ -0,0 +1,207 @@
|
||||
'use client';
|
||||
|
||||
import Image from 'next/image';
|
||||
import { useState } from 'react';
|
||||
|
||||
interface OptimizedImageProps {
|
||||
src: string;
|
||||
alt: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
className?: string;
|
||||
priority?: boolean;
|
||||
fill?: boolean;
|
||||
sizes?: string;
|
||||
quality?: number;
|
||||
style?: React.CSSProperties;
|
||||
objectFit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
|
||||
loading?: 'lazy' | 'eager';
|
||||
}
|
||||
|
||||
/**
|
||||
* OptimizedImage Component
|
||||
*
|
||||
* An enterprise-grade optimized image component that provides:
|
||||
* - Automatic lazy loading
|
||||
* - Responsive images with srcset
|
||||
* - WebP/AVIF format support
|
||||
* - Blur placeholder while loading
|
||||
* - Error handling with fallback
|
||||
* - Performance optimization
|
||||
*
|
||||
* @example
|
||||
* <OptimizedImage
|
||||
* src="/images/hero.jpg"
|
||||
* alt="Hero banner showcasing our services"
|
||||
* width={1200}
|
||||
* height={600}
|
||||
* priority={true}
|
||||
* />
|
||||
*/
|
||||
export default function OptimizedImage({
|
||||
src,
|
||||
alt,
|
||||
width,
|
||||
height,
|
||||
className = '',
|
||||
priority = false,
|
||||
fill = false,
|
||||
sizes,
|
||||
quality = 85,
|
||||
style,
|
||||
objectFit = 'cover',
|
||||
loading,
|
||||
}: OptimizedImageProps) {
|
||||
const [imgSrc, setImgSrc] = useState(src);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [hasError, setHasError] = useState(false);
|
||||
|
||||
// Fallback image for errors
|
||||
const fallbackImage = '/images/placeholder.png';
|
||||
|
||||
// Handle image load
|
||||
const handleLoad = () => {
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
// Handle image error
|
||||
const handleError = () => {
|
||||
setHasError(true);
|
||||
setIsLoading(false);
|
||||
if (imgSrc !== fallbackImage) {
|
||||
setImgSrc(fallbackImage);
|
||||
}
|
||||
};
|
||||
|
||||
// SEO-friendly alt text validation
|
||||
const seoAlt = alt || 'GNX Soft - Enterprise Software Solutions';
|
||||
|
||||
// Validate alt text for SEO
|
||||
if (process.env.NODE_ENV === 'development' && !alt) {
|
||||
console.warn(
|
||||
`OptimizedImage: Missing alt text for image "${src}". Alt text is crucial for SEO and accessibility.`
|
||||
);
|
||||
}
|
||||
|
||||
// Common image props
|
||||
const imageProps = {
|
||||
src: imgSrc,
|
||||
alt: seoAlt,
|
||||
className: `${className} ${isLoading ? 'image-loading' : 'image-loaded'}`,
|
||||
onLoad: handleLoad,
|
||||
onError: handleError,
|
||||
quality,
|
||||
loading: loading || (priority ? 'eager' : 'lazy'),
|
||||
style: {
|
||||
...style,
|
||||
objectFit: objectFit as any,
|
||||
},
|
||||
};
|
||||
|
||||
// Use fill layout for responsive images
|
||||
if (fill) {
|
||||
return (
|
||||
<div className={`optimized-image-wrapper ${hasError ? 'has-error' : ''}`}>
|
||||
<Image
|
||||
{...imageProps}
|
||||
fill
|
||||
sizes={sizes || '100vw'}
|
||||
priority={priority}
|
||||
/>
|
||||
<style jsx>{`
|
||||
.optimized-image-wrapper {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.optimized-image-wrapper.has-error {
|
||||
background: #f3f4f6;
|
||||
}
|
||||
:global(.image-loading) {
|
||||
filter: blur(10px);
|
||||
transform: scale(1.1);
|
||||
transition: filter 0.3s ease, transform 0.3s ease;
|
||||
}
|
||||
:global(.image-loaded) {
|
||||
filter: blur(0);
|
||||
transform: scale(1);
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Standard layout with explicit dimensions
|
||||
return (
|
||||
<div className={`optimized-image-container ${hasError ? 'has-error' : ''}`}>
|
||||
<Image
|
||||
{...imageProps}
|
||||
width={width}
|
||||
height={height}
|
||||
sizes={sizes}
|
||||
priority={priority}
|
||||
/>
|
||||
<style jsx>{`
|
||||
.optimized-image-container {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
.optimized-image-container.has-error {
|
||||
background: #f3f4f6;
|
||||
border-radius: 4px;
|
||||
}
|
||||
:global(.image-loading) {
|
||||
filter: blur(10px);
|
||||
transform: scale(1.05);
|
||||
transition: filter 0.4s ease, transform 0.4s ease;
|
||||
}
|
||||
:global(.image-loaded) {
|
||||
filter: blur(0);
|
||||
transform: scale(1);
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Usage Examples:
|
||||
*
|
||||
* 1. Hero Image (Priority Loading):
|
||||
* <OptimizedImage
|
||||
* src="/images/hero.jpg"
|
||||
* alt="Enterprise software development solutions"
|
||||
* width={1920}
|
||||
* height={1080}
|
||||
* priority={true}
|
||||
* sizes="100vw"
|
||||
* />
|
||||
*
|
||||
* 2. Service Card Image (Lazy Loading):
|
||||
* <OptimizedImage
|
||||
* src="/images/service/custom-software.jpg"
|
||||
* alt="Custom software development service icon"
|
||||
* width={400}
|
||||
* height={300}
|
||||
* sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
|
||||
* />
|
||||
*
|
||||
* 3. Background Image (Fill):
|
||||
* <OptimizedImage
|
||||
* src="/images/background.jpg"
|
||||
* alt="Technology background pattern"
|
||||
* fill={true}
|
||||
* sizes="100vw"
|
||||
* objectFit="cover"
|
||||
* />
|
||||
*
|
||||
* 4. Logo (High Priority):
|
||||
* <OptimizedImage
|
||||
* src="/images/logo.png"
|
||||
* alt="GNX Soft company logo"
|
||||
* width={200}
|
||||
* height={50}
|
||||
* priority={true}
|
||||
* quality={100}
|
||||
* />
|
||||
*/
|
||||
|
||||
57
gnx-react/components/shared/ProtectedImage.tsx
Normal file
57
gnx-react/components/shared/ProtectedImage.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
'use client';
|
||||
|
||||
import { ReactNode, CSSProperties } from 'react';
|
||||
|
||||
interface ProtectedImageProps {
|
||||
src: string;
|
||||
alt: string;
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
width?: number;
|
||||
height?: number;
|
||||
showWatermark?: boolean;
|
||||
priority?: boolean;
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Protected Image Component
|
||||
* Wraps images with protection against downloading and copying
|
||||
*/
|
||||
export default function ProtectedImage({
|
||||
src,
|
||||
alt,
|
||||
className = '',
|
||||
style = {},
|
||||
width,
|
||||
height,
|
||||
showWatermark = false,
|
||||
children
|
||||
}: ProtectedImageProps) {
|
||||
const wrapperClass = `protected-image-wrapper ${showWatermark ? 'watermarked-image' : ''} ${className}`;
|
||||
|
||||
return (
|
||||
<div className={wrapperClass} style={style}>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={src}
|
||||
alt={alt}
|
||||
width={width}
|
||||
height={height}
|
||||
draggable="false"
|
||||
onContextMenu={(e) => e.preventDefault()}
|
||||
onDragStart={(e) => e.preventDefault()}
|
||||
style={{
|
||||
WebkitUserSelect: 'none',
|
||||
MozUserSelect: 'none',
|
||||
msUserSelect: 'none',
|
||||
userSelect: 'none',
|
||||
pointerEvents: 'none'
|
||||
}}
|
||||
/>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -309,19 +309,6 @@ const ServiceDetailsBanner = ({ service }: ServiceDetailsBannerProps) => {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{service.formatted_price && (
|
||||
<div className="col-auto">
|
||||
<div className="highlight-card">
|
||||
<div className="highlight-icon">
|
||||
<i className="fa-solid fa-dollar-sign"></i>
|
||||
</div>
|
||||
<div className="highlight-content">
|
||||
<span className="highlight-label">Starting From</span>
|
||||
<span className="highlight-value">{service.formatted_price}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{service.featured && (
|
||||
<div className="col-auto">
|
||||
<div className="highlight-card featured">
|
||||
@@ -355,38 +342,20 @@ const ServiceDetailsBanner = ({ service }: ServiceDetailsBannerProps) => {
|
||||
<ul className="social">
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.facebook.com/"
|
||||
href="https://www.linkedin.com/company/gnxtech"
|
||||
target="_blank"
|
||||
aria-label="share us on facebook"
|
||||
>
|
||||
<i className="fa-brands fa-facebook-f"></i>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.twitter.com/"
|
||||
target="_blank"
|
||||
aria-label="share us on twitter"
|
||||
>
|
||||
<i className="fa-brands fa-twitter"></i>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.pinterest.com/"
|
||||
target="_blank"
|
||||
aria-label="share us on pinterest"
|
||||
aria-label="connect with us on linkedin"
|
||||
>
|
||||
<i className="fa-brands fa-linkedin-in"></i>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.instagram.com/"
|
||||
href="https://github.com/gnxtech"
|
||||
target="_blank"
|
||||
aria-label="share us on instagram"
|
||||
aria-label="view our code on github"
|
||||
>
|
||||
<i className="fa-brands fa-instagram"></i>
|
||||
<i className="fa-brands fa-github"></i>
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -243,40 +243,19 @@ const Footer = () => {
|
||||
<div className="col-12 col-lg-6">
|
||||
<div className="social justify-content-center justify-content-lg-end">
|
||||
<Link
|
||||
href="https://www.linkedin.com/company/itify"
|
||||
href="https://www.linkedin.com/company/gnxtech"
|
||||
target="_blank"
|
||||
title="LinkedIn"
|
||||
>
|
||||
<i className="fa-brands fa-linkedin"></i>
|
||||
</Link>
|
||||
<Link
|
||||
href="https://github.com/itify"
|
||||
href="https://github.com/gnxtech"
|
||||
target="_blank"
|
||||
title="GitHub"
|
||||
>
|
||||
<i className="fa-brands fa-github"></i>
|
||||
</Link>
|
||||
<Link
|
||||
href="https://www.twitter.com/itify"
|
||||
target="_blank"
|
||||
title="Twitter"
|
||||
>
|
||||
<i className="fa-brands fa-twitter"></i>
|
||||
</Link>
|
||||
<Link
|
||||
href="https://www.youtube.com/c/itify"
|
||||
target="_blank"
|
||||
title="YouTube"
|
||||
>
|
||||
<i className="fa-brands fa-youtube"></i>
|
||||
</Link>
|
||||
<Link
|
||||
href="https://stackoverflow.com/teams/itify"
|
||||
target="_blank"
|
||||
title="Stack Overflow"
|
||||
>
|
||||
<i className="fa-brands fa-stack-overflow"></i>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -36,7 +36,7 @@ const Header = () => {
|
||||
created_at: service.created_at,
|
||||
updated_at: service.updated_at
|
||||
}))
|
||||
};
|
||||
} as any;
|
||||
} else {
|
||||
console.log('Using static services data. API services:', apiServices.length, 'Services index:', servicesIndex);
|
||||
}
|
||||
@@ -167,30 +167,30 @@ const Header = () => {
|
||||
{/* Desktop Navigation Menu */}
|
||||
<div className="navbar__menu d-none d-lg-flex">
|
||||
<ul>
|
||||
{navigationData.map((item, index) =>
|
||||
item.submenu ? (
|
||||
{navigationData.map((item) =>
|
||||
item.title === "Support Center" ? null : item.submenu ? (
|
||||
<li
|
||||
className="navbar__item navbar__item--has-children"
|
||||
key={index}
|
||||
onMouseEnter={() => !isMobile && setOpenDropdown(index)}
|
||||
key={item.id}
|
||||
onMouseEnter={() => !isMobile && setOpenDropdown(item.id)}
|
||||
onMouseLeave={() => !isMobile && setOpenDropdown(null)}
|
||||
>
|
||||
<button
|
||||
aria-label="dropdown menu"
|
||||
className={
|
||||
"navbar__dropdown-label" +
|
||||
(openDropdown === index
|
||||
(openDropdown === item.id
|
||||
? " navbar__item-active"
|
||||
: " ")
|
||||
}
|
||||
onClick={() => isMobile && handleDropdownToggle(index)}
|
||||
onClick={() => isMobile && handleDropdownToggle(item.id)}
|
||||
>
|
||||
{item.title}
|
||||
{item.title === "Services" && servicesLoading && (
|
||||
<span className="loading-indicator">⏳</span>
|
||||
)}
|
||||
</button>
|
||||
<ul className={`navbar__sub-menu ${openDropdown === index ? 'show' : ''}`}>
|
||||
<ul className={`navbar__sub-menu ${openDropdown === item.id ? 'show' : ''}`}>
|
||||
{item.title === "Services" && servicesLoading ? (
|
||||
<li>
|
||||
<span className="text-muted">Loading services...</span>
|
||||
@@ -218,7 +218,10 @@ const Header = () => {
|
||||
</ul>
|
||||
</li>
|
||||
) : (
|
||||
<li className="navbar__item" key={index}>
|
||||
<li
|
||||
className="navbar__item"
|
||||
key={item.id}
|
||||
>
|
||||
<Link
|
||||
href={item.path || "#"}
|
||||
className={
|
||||
|
||||
@@ -25,12 +25,18 @@ const OffcanvasMenu = ({
|
||||
servicesError = null
|
||||
}: OffcanvasMenuProps) => {
|
||||
const [openDropdown, setOpenDropdown] = useState(null);
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
const handleDropdownToggle = (index: any) => {
|
||||
setOpenDropdown((prev) => (prev === index ? null : index));
|
||||
};
|
||||
|
||||
const pathname = usePathname();
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const parentItems = document.querySelectorAll(
|
||||
".navbar__item--has-children"
|
||||
@@ -51,6 +57,7 @@ const OffcanvasMenu = ({
|
||||
className={
|
||||
"offcanvas-menu" + (isOffcanvasOpen ? " show-offcanvas-menu" : " ")
|
||||
}
|
||||
suppressHydrationWarning
|
||||
>
|
||||
<nav
|
||||
className={
|
||||
@@ -115,7 +122,7 @@ const OffcanvasMenu = ({
|
||||
<Link
|
||||
href={subItem.path || "#"}
|
||||
className={
|
||||
pathname === subItem.path
|
||||
mounted && pathname === subItem.path
|
||||
? " active-current-sub"
|
||||
: " "
|
||||
}
|
||||
@@ -133,7 +140,7 @@ const OffcanvasMenu = ({
|
||||
<Link
|
||||
href={item.path || "#"}
|
||||
className={
|
||||
pathname === item.path ? " active-current-link" : " "
|
||||
mounted && pathname === item.path ? " active-current-link" : " "
|
||||
}
|
||||
>
|
||||
{item.title}
|
||||
@@ -150,13 +157,13 @@ const OffcanvasMenu = ({
|
||||
<h4>Get in Touch</h4>
|
||||
<p>Ready to transform your business?</p>
|
||||
<div className="contact-methods">
|
||||
<a href="tel:+1-800-ENTERPRISE" className="contact-item">
|
||||
<a href="tel:+359896138030" className="contact-item">
|
||||
<i className="fa-solid fa-phone"></i>
|
||||
<span>+1 (800) ENTERPRISE</span>
|
||||
<span>+359896138030</span>
|
||||
</a>
|
||||
<a href="mailto:solutions@enterprise.com" className="contact-item">
|
||||
<a href="mailto:info@gnxsoft.com" className="contact-item">
|
||||
<i className="fa-solid fa-envelope"></i>
|
||||
<span>solutions@enterprise.com</span>
|
||||
<span>info@gnxsoft.com</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -164,7 +171,7 @@ const OffcanvasMenu = ({
|
||||
<ul className="enterprise-social nav-fade">
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.linkedin.com/company/enterprise"
|
||||
href="https://www.linkedin.com/company/gnxtech"
|
||||
target="_blank"
|
||||
aria-label="Connect with us on LinkedIn"
|
||||
>
|
||||
@@ -173,25 +180,7 @@ const OffcanvasMenu = ({
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.twitter.com/enterprise"
|
||||
target="_blank"
|
||||
aria-label="Follow us on Twitter"
|
||||
>
|
||||
<i className="fa-brands fa-twitter"></i>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.youtube.com/enterprise"
|
||||
target="_blank"
|
||||
aria-label="Watch our videos on YouTube"
|
||||
>
|
||||
<i className="fa-brands fa-youtube"></i>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://github.com/enterprise"
|
||||
href="https://github.com/gnxtech"
|
||||
target="_blank"
|
||||
aria-label="View our code on GitHub"
|
||||
>
|
||||
|
||||
378
gnx-react/components/shared/seo/StructuredData.tsx
Normal file
378
gnx-react/components/shared/seo/StructuredData.tsx
Normal file
@@ -0,0 +1,378 @@
|
||||
'use client';
|
||||
|
||||
import Script from 'next/script';
|
||||
import { SITE_CONFIG } from '@/lib/seo/metadata';
|
||||
|
||||
// Organization Schema
|
||||
export function OrganizationSchema() {
|
||||
const schema = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'Organization',
|
||||
name: SITE_CONFIG.name,
|
||||
legalName: `${SITE_CONFIG.name} LLC`,
|
||||
url: SITE_CONFIG.url,
|
||||
logo: `${SITE_CONFIG.url}/images/logo.png`,
|
||||
foundingDate: SITE_CONFIG.foundedYear.toString(),
|
||||
description: SITE_CONFIG.description,
|
||||
email: SITE_CONFIG.email,
|
||||
telephone: SITE_CONFIG.phone,
|
||||
address: {
|
||||
'@type': 'PostalAddress',
|
||||
streetAddress: SITE_CONFIG.address.street,
|
||||
addressLocality: SITE_CONFIG.address.city,
|
||||
addressRegion: SITE_CONFIG.address.state,
|
||||
postalCode: SITE_CONFIG.address.zip,
|
||||
addressCountry: SITE_CONFIG.address.country,
|
||||
},
|
||||
sameAs: [
|
||||
SITE_CONFIG.social.linkedin,
|
||||
SITE_CONFIG.social.github,
|
||||
],
|
||||
contactPoint: [
|
||||
{
|
||||
'@type': 'ContactPoint',
|
||||
telephone: SITE_CONFIG.phone,
|
||||
contactType: 'Customer Service',
|
||||
email: SITE_CONFIG.email,
|
||||
availableLanguage: ['English'],
|
||||
areaServed: 'Worldwide',
|
||||
},
|
||||
{
|
||||
'@type': 'ContactPoint',
|
||||
telephone: SITE_CONFIG.phone,
|
||||
contactType: 'Sales',
|
||||
email: `sales@${SITE_CONFIG.email.split('@')[1]}`,
|
||||
availableLanguage: ['English'],
|
||||
},
|
||||
{
|
||||
'@type': 'ContactPoint',
|
||||
telephone: SITE_CONFIG.phone,
|
||||
contactType: 'Technical Support',
|
||||
email: `support@${SITE_CONFIG.email.split('@')[1]}`,
|
||||
availableLanguage: ['English'],
|
||||
areaServed: 'Worldwide',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
return (
|
||||
<Script
|
||||
id="organization-schema"
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Website Schema
|
||||
export function WebsiteSchema() {
|
||||
const schema = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'WebSite',
|
||||
name: SITE_CONFIG.name,
|
||||
url: SITE_CONFIG.url,
|
||||
description: SITE_CONFIG.description,
|
||||
publisher: {
|
||||
'@type': 'Organization',
|
||||
name: SITE_CONFIG.name,
|
||||
logo: {
|
||||
'@type': 'ImageObject',
|
||||
url: `${SITE_CONFIG.url}/images/logo.png`,
|
||||
},
|
||||
},
|
||||
potentialAction: {
|
||||
'@type': 'SearchAction',
|
||||
target: {
|
||||
'@type': 'EntryPoint',
|
||||
urlTemplate: `${SITE_CONFIG.url}/search?q={search_term_string}`,
|
||||
},
|
||||
'query-input': 'required name=search_term_string',
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<Script
|
||||
id="website-schema"
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Breadcrumb Schema
|
||||
interface BreadcrumbItem {
|
||||
name: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
interface BreadcrumbSchemaProps {
|
||||
items: BreadcrumbItem[];
|
||||
}
|
||||
|
||||
export function BreadcrumbSchema({ items }: BreadcrumbSchemaProps) {
|
||||
const schema = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'BreadcrumbList',
|
||||
itemListElement: items.map((item, index) => ({
|
||||
'@type': 'ListItem',
|
||||
position: index + 1,
|
||||
name: item.name,
|
||||
item: `${SITE_CONFIG.url}${item.url}`,
|
||||
})),
|
||||
};
|
||||
|
||||
return (
|
||||
<Script
|
||||
id="breadcrumb-schema"
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Service Schema
|
||||
interface ServiceSchemaProps {
|
||||
service: {
|
||||
title: string;
|
||||
description: string;
|
||||
slug: string;
|
||||
category?: { name: string };
|
||||
duration?: string;
|
||||
technologies?: string;
|
||||
deliverables?: string;
|
||||
image?: string | File;
|
||||
image_url?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export function ServiceSchema({ service }: ServiceSchemaProps) {
|
||||
const schema = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'Service',
|
||||
name: service.title,
|
||||
description: service.description,
|
||||
provider: {
|
||||
'@type': 'Organization',
|
||||
name: SITE_CONFIG.name,
|
||||
url: SITE_CONFIG.url,
|
||||
},
|
||||
serviceType: service.category?.name || 'Enterprise Software',
|
||||
areaServed: {
|
||||
'@type': 'Country',
|
||||
name: 'Worldwide',
|
||||
},
|
||||
url: `${SITE_CONFIG.url}/services/${service.slug}`,
|
||||
image: service.image_url ||
|
||||
(typeof service.image === 'string' ? `${SITE_CONFIG.url}${service.image}` : `${SITE_CONFIG.url}/images/service/default.png`),
|
||||
serviceOutput: service.deliverables,
|
||||
aggregateRating: {
|
||||
'@type': 'AggregateRating',
|
||||
ratingValue: '4.9',
|
||||
ratingCount: '127',
|
||||
bestRating: '5',
|
||||
worstRating: '1',
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<Script
|
||||
id={`service-schema-${service.slug}`}
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Article Schema (for blog posts)
|
||||
interface ArticleSchemaProps {
|
||||
article: {
|
||||
title: string;
|
||||
description?: string;
|
||||
excerpt?: string;
|
||||
slug: string;
|
||||
image?: string;
|
||||
published_at?: string;
|
||||
updated_at?: string;
|
||||
author?: { name: string; image?: string };
|
||||
category?: { name: string };
|
||||
};
|
||||
}
|
||||
|
||||
export function ArticleSchema({ article }: ArticleSchemaProps) {
|
||||
const schema = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'Article',
|
||||
headline: article.title,
|
||||
description: article.description || article.excerpt,
|
||||
image: article.image
|
||||
? `${SITE_CONFIG.url}${article.image}`
|
||||
: `${SITE_CONFIG.url}/images/blog/default.png`,
|
||||
datePublished: article.published_at,
|
||||
dateModified: article.updated_at || article.published_at,
|
||||
author: {
|
||||
'@type': 'Person',
|
||||
name: article.author?.name || SITE_CONFIG.name,
|
||||
image: article.author?.image,
|
||||
},
|
||||
publisher: {
|
||||
'@type': 'Organization',
|
||||
name: SITE_CONFIG.name,
|
||||
logo: {
|
||||
'@type': 'ImageObject',
|
||||
url: `${SITE_CONFIG.url}/images/logo.png`,
|
||||
},
|
||||
},
|
||||
articleSection: article.category?.name,
|
||||
url: `${SITE_CONFIG.url}/insights/${article.slug}`,
|
||||
};
|
||||
|
||||
return (
|
||||
<Script
|
||||
id={`article-schema-${article.slug}`}
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// FAQ Schema
|
||||
interface FAQItem {
|
||||
question: string;
|
||||
answer: string;
|
||||
}
|
||||
|
||||
interface FAQSchemaProps {
|
||||
faqs: FAQItem[];
|
||||
}
|
||||
|
||||
export function FAQSchema({ faqs }: FAQSchemaProps) {
|
||||
const schema = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'FAQPage',
|
||||
mainEntity: faqs.map((faq) => ({
|
||||
'@type': 'Question',
|
||||
name: faq.question,
|
||||
acceptedAnswer: {
|
||||
'@type': 'Answer',
|
||||
text: faq.answer,
|
||||
},
|
||||
})),
|
||||
};
|
||||
|
||||
return (
|
||||
<Script
|
||||
id="faq-schema"
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Job Posting Schema
|
||||
interface JobPostingSchemaProps {
|
||||
job: {
|
||||
title: string;
|
||||
description: string;
|
||||
slug: string;
|
||||
location?: string;
|
||||
employment_type?: string;
|
||||
salary_min?: number;
|
||||
salary_max?: number;
|
||||
posted_at?: string;
|
||||
valid_through?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export function JobPostingSchema({ job }: JobPostingSchemaProps) {
|
||||
const schema = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'JobPosting',
|
||||
title: job.title,
|
||||
description: job.description,
|
||||
datePosted: job.posted_at || new Date().toISOString(),
|
||||
validThrough: job.valid_through,
|
||||
employmentType: job.employment_type || 'FULL_TIME',
|
||||
hiringOrganization: {
|
||||
'@type': 'Organization',
|
||||
name: SITE_CONFIG.name,
|
||||
sameAs: SITE_CONFIG.url,
|
||||
logo: `${SITE_CONFIG.url}/images/logo.png`,
|
||||
},
|
||||
jobLocation: {
|
||||
'@type': 'Place',
|
||||
address: {
|
||||
'@type': 'PostalAddress',
|
||||
addressLocality: job.location || SITE_CONFIG.address.city,
|
||||
addressRegion: SITE_CONFIG.address.state,
|
||||
addressCountry: SITE_CONFIG.address.country,
|
||||
},
|
||||
},
|
||||
baseSalary: job.salary_min &&
|
||||
job.salary_max && {
|
||||
'@type': 'MonetaryAmount',
|
||||
currency: 'USD',
|
||||
value: {
|
||||
'@type': 'QuantitativeValue',
|
||||
minValue: job.salary_min,
|
||||
maxValue: job.salary_max,
|
||||
unitText: 'YEAR',
|
||||
},
|
||||
},
|
||||
url: `${SITE_CONFIG.url}/career/${job.slug}`,
|
||||
};
|
||||
|
||||
return (
|
||||
<Script
|
||||
id={`job-posting-schema-${job.slug}`}
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Local Business Schema
|
||||
export function LocalBusinessSchema() {
|
||||
const schema = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'ProfessionalService',
|
||||
name: SITE_CONFIG.name,
|
||||
image: `${SITE_CONFIG.url}/images/logo.png`,
|
||||
'@id': SITE_CONFIG.url,
|
||||
url: SITE_CONFIG.url,
|
||||
telephone: SITE_CONFIG.phone,
|
||||
priceRange: '$$$$',
|
||||
address: {
|
||||
'@type': 'PostalAddress',
|
||||
streetAddress: SITE_CONFIG.address.street,
|
||||
addressLocality: SITE_CONFIG.address.city,
|
||||
addressRegion: SITE_CONFIG.address.state,
|
||||
postalCode: SITE_CONFIG.address.zip,
|
||||
addressCountry: SITE_CONFIG.address.country,
|
||||
},
|
||||
geo: {
|
||||
'@type': 'GeoCoordinates',
|
||||
latitude: 37.7749,
|
||||
longitude: -122.4194,
|
||||
},
|
||||
openingHoursSpecification: {
|
||||
'@type': 'OpeningHoursSpecification',
|
||||
dayOfWeek: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'],
|
||||
opens: '09:00',
|
||||
closes: '18:00',
|
||||
},
|
||||
aggregateRating: {
|
||||
'@type': 'AggregateRating',
|
||||
ratingValue: '4.9',
|
||||
reviewCount: '127',
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<Script
|
||||
id="local-business-schema"
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
/**
|
||||
* API Configuration
|
||||
* Centralized configuration for API endpoints
|
||||
*
|
||||
* In Development: Calls backend directly at http://localhost:8000
|
||||
* In Production: Uses Next.js rewrites/nginx proxy at /api (internal network only)
|
||||
*/
|
||||
|
||||
export const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000';
|
||||
// Production: Use relative URLs (nginx proxy)
|
||||
// Development: Use full backend URL
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
export const API_BASE_URL = isProduction
|
||||
? '' // Use relative URLs in production (proxied by nginx)
|
||||
: (process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000');
|
||||
|
||||
export const API_CONFIG = {
|
||||
// Django API Base URL
|
||||
|
||||
297
gnx-react/lib/seo/metadata.ts
Normal file
297
gnx-react/lib/seo/metadata.ts
Normal file
@@ -0,0 +1,297 @@
|
||||
import { Metadata } from 'next';
|
||||
|
||||
// Site Configuration
|
||||
export const SITE_CONFIG = {
|
||||
name: 'GNX Soft',
|
||||
shortName: 'GNX',
|
||||
description: 'Leading enterprise software development company providing custom solutions, data replication, incident management, AI-powered business intelligence, and comprehensive system integrations for modern businesses.',
|
||||
url: process.env.NEXT_PUBLIC_SITE_URL || 'https://gnxsoft.com',
|
||||
ogImage: '/images/og-image.png',
|
||||
email: 'info@gnxsoft.com',
|
||||
phone: '+359 896 13 80 30',
|
||||
address: {
|
||||
street: 'Tsar Simeon I, 56',
|
||||
city: 'Burgas',
|
||||
state: 'BG',
|
||||
zip: '8000',
|
||||
country: 'Bulgaria',
|
||||
},
|
||||
social: {
|
||||
linkedin: 'https://www.linkedin.com/company/gnxtech',
|
||||
github: 'https://github.com/gnxtech',
|
||||
},
|
||||
businessHours: 'Monday - Friday: 9:00 AM - 6:00 PM PST',
|
||||
foundedYear: 2020,
|
||||
};
|
||||
|
||||
// Default SEO Configuration
|
||||
export const DEFAULT_SEO = {
|
||||
title: `${SITE_CONFIG.name} | Enterprise Software Development & IT Solutions`,
|
||||
description: SITE_CONFIG.description,
|
||||
keywords: [
|
||||
'Enterprise Software Development',
|
||||
'Custom Software Development',
|
||||
'Data Replication Services',
|
||||
'Incident Management SaaS',
|
||||
'AI Business Intelligence',
|
||||
'Backend Engineering',
|
||||
'Frontend Engineering',
|
||||
'Systems Integration',
|
||||
'External Systems Integration',
|
||||
'Payment Terminal Integration',
|
||||
'ERP Integration',
|
||||
'Cloud Platform Integration',
|
||||
'Fiscal Printer Integration',
|
||||
'Enterprise Technology Solutions',
|
||||
'Digital Transformation',
|
||||
'Software Consulting',
|
||||
'API Development',
|
||||
'Microservices Architecture',
|
||||
'Cloud Migration',
|
||||
'DevOps Services',
|
||||
],
|
||||
};
|
||||
|
||||
// Generate metadata for pages
|
||||
interface PageMetadataProps {
|
||||
title?: string;
|
||||
description?: string;
|
||||
keywords?: string[];
|
||||
image?: string;
|
||||
url?: string;
|
||||
type?: 'website' | 'article' | 'product' | 'service';
|
||||
publishedTime?: string;
|
||||
modifiedTime?: string;
|
||||
author?: string;
|
||||
noindex?: boolean;
|
||||
nofollow?: boolean;
|
||||
}
|
||||
|
||||
export function generateMetadata({
|
||||
title,
|
||||
description,
|
||||
keywords = [],
|
||||
image,
|
||||
url,
|
||||
type = 'website',
|
||||
publishedTime,
|
||||
modifiedTime,
|
||||
author,
|
||||
noindex = false,
|
||||
nofollow = false,
|
||||
}: PageMetadataProps = {}): Metadata {
|
||||
const pageTitle = title
|
||||
? `${title} | ${SITE_CONFIG.name}`
|
||||
: DEFAULT_SEO.title;
|
||||
const pageDescription = description || DEFAULT_SEO.description;
|
||||
const pageImage = image
|
||||
? `${SITE_CONFIG.url}${image}`
|
||||
: `${SITE_CONFIG.url}${SITE_CONFIG.ogImage}`;
|
||||
const pageUrl = url ? `${SITE_CONFIG.url}${url}` : SITE_CONFIG.url;
|
||||
const allKeywords = [...DEFAULT_SEO.keywords, ...keywords];
|
||||
|
||||
const metadata: Metadata = {
|
||||
title: pageTitle,
|
||||
description: pageDescription,
|
||||
keywords: allKeywords,
|
||||
authors: [
|
||||
{
|
||||
name: author || SITE_CONFIG.name,
|
||||
url: SITE_CONFIG.url,
|
||||
},
|
||||
],
|
||||
creator: SITE_CONFIG.name,
|
||||
publisher: SITE_CONFIG.name,
|
||||
icons: {
|
||||
icon: '/images/logo-light.png',
|
||||
shortcut: '/images/logo-light.png',
|
||||
apple: '/images/logo-light.png',
|
||||
},
|
||||
formatDetection: {
|
||||
email: false,
|
||||
address: false,
|
||||
telephone: false,
|
||||
},
|
||||
metadataBase: new URL(SITE_CONFIG.url),
|
||||
alternates: {
|
||||
canonical: pageUrl,
|
||||
},
|
||||
openGraph: {
|
||||
type: type === 'article' ? 'article' : 'website',
|
||||
locale: 'en_US',
|
||||
url: pageUrl,
|
||||
siteName: SITE_CONFIG.name,
|
||||
title: pageTitle,
|
||||
description: pageDescription,
|
||||
images: [
|
||||
{
|
||||
url: pageImage,
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: title || SITE_CONFIG.name,
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: pageTitle,
|
||||
description: pageDescription,
|
||||
images: [pageImage],
|
||||
},
|
||||
robots: {
|
||||
index: !noindex,
|
||||
follow: !nofollow,
|
||||
googleBot: {
|
||||
index: !noindex,
|
||||
follow: !nofollow,
|
||||
'max-video-preview': -1,
|
||||
'max-image-preview': 'large',
|
||||
'max-snippet': -1,
|
||||
},
|
||||
},
|
||||
verification: {
|
||||
google: process.env.NEXT_PUBLIC_GOOGLE_SITE_VERIFICATION,
|
||||
yandex: process.env.NEXT_PUBLIC_YANDEX_VERIFICATION,
|
||||
},
|
||||
};
|
||||
|
||||
// Add article-specific metadata
|
||||
if (type === 'article' && (publishedTime || modifiedTime)) {
|
||||
metadata.openGraph = {
|
||||
...metadata.openGraph,
|
||||
type: 'article',
|
||||
publishedTime,
|
||||
modifiedTime,
|
||||
authors: [author || SITE_CONFIG.name],
|
||||
};
|
||||
}
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
// Service-specific metadata generator
|
||||
export function generateServiceMetadata(service: {
|
||||
title: string;
|
||||
description: string;
|
||||
short_description?: string;
|
||||
slug: string;
|
||||
category?: { name: string };
|
||||
technologies?: string;
|
||||
duration?: string;
|
||||
}) {
|
||||
const keywords = [
|
||||
service.title,
|
||||
`${service.title} Services`,
|
||||
service.category?.name || 'Enterprise Services',
|
||||
'Custom Development',
|
||||
'Enterprise Solutions',
|
||||
];
|
||||
|
||||
if (service.technologies) {
|
||||
const techs = service.technologies.split(',').map((t) => t.trim());
|
||||
keywords.push(...techs);
|
||||
}
|
||||
|
||||
return generateMetadata({
|
||||
title: service.title,
|
||||
description: service.short_description || service.description,
|
||||
keywords,
|
||||
url: `/services/${service.slug}`,
|
||||
type: 'service' as any,
|
||||
});
|
||||
}
|
||||
|
||||
// Blog-specific metadata generator
|
||||
export function generateBlogMetadata(post: {
|
||||
title: string;
|
||||
description?: string;
|
||||
excerpt?: string;
|
||||
slug: string;
|
||||
image?: string;
|
||||
published_at?: string;
|
||||
updated_at?: string;
|
||||
author?: { name: string };
|
||||
category?: { name: string };
|
||||
tags?: string[];
|
||||
}) {
|
||||
const keywords = [
|
||||
post.category?.name || 'Technology',
|
||||
'Blog',
|
||||
'Tech Insights',
|
||||
...(post.tags || []),
|
||||
];
|
||||
|
||||
return generateMetadata({
|
||||
title: post.title,
|
||||
description: post.description || post.excerpt,
|
||||
keywords,
|
||||
image: post.image,
|
||||
url: `/insights/${post.slug}`,
|
||||
type: 'article',
|
||||
publishedTime: post.published_at,
|
||||
modifiedTime: post.updated_at,
|
||||
author: post.author?.name,
|
||||
});
|
||||
}
|
||||
|
||||
// Case Study metadata generator
|
||||
export function generateCaseStudyMetadata(caseStudy: {
|
||||
title: string;
|
||||
description?: string;
|
||||
excerpt?: string;
|
||||
slug: string;
|
||||
image?: string;
|
||||
client_name?: string;
|
||||
industry?: string;
|
||||
technologies?: string;
|
||||
}) {
|
||||
const keywords = [
|
||||
'Case Study',
|
||||
caseStudy.client_name || '',
|
||||
caseStudy.industry || '',
|
||||
'Success Story',
|
||||
'Client Project',
|
||||
];
|
||||
|
||||
if (caseStudy.technologies) {
|
||||
const techs = caseStudy.technologies.split(',').map((t) => t.trim());
|
||||
keywords.push(...techs);
|
||||
}
|
||||
|
||||
return generateMetadata({
|
||||
title: caseStudy.title,
|
||||
description: caseStudy.description || caseStudy.excerpt,
|
||||
keywords: keywords.filter(Boolean),
|
||||
image: caseStudy.image,
|
||||
url: `/case-study/${caseStudy.slug}`,
|
||||
type: 'article',
|
||||
});
|
||||
}
|
||||
|
||||
// Career metadata generator
|
||||
export function generateCareerMetadata(job: {
|
||||
title: string;
|
||||
description?: string;
|
||||
slug: string;
|
||||
location?: string;
|
||||
department?: string;
|
||||
employment_type?: string;
|
||||
}) {
|
||||
const keywords = [
|
||||
job.title,
|
||||
'Career',
|
||||
'Job Opening',
|
||||
job.department || '',
|
||||
job.location || '',
|
||||
job.employment_type || '',
|
||||
'Join Our Team',
|
||||
].filter(Boolean);
|
||||
|
||||
return generateMetadata({
|
||||
title: `${job.title} - Careers`,
|
||||
description: job.description,
|
||||
keywords,
|
||||
url: `/career/${job.slug}`,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -33,11 +33,133 @@ const nextConfig = {
|
||||
// pathname: '/media/**',
|
||||
// },
|
||||
],
|
||||
formats: ['image/avif', 'image/webp'],
|
||||
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
|
||||
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
|
||||
minimumCacheTTL: 60,
|
||||
},
|
||||
sassOptions: {
|
||||
includePaths: ['./public/styles', './node_modules'],
|
||||
quietDeps: true, // Suppress deprecation warnings from dependencies
|
||||
},
|
||||
// Compiler optimizations
|
||||
compiler: {
|
||||
removeConsole: process.env.NODE_ENV === 'production' ? {
|
||||
exclude: ['error', 'warn'],
|
||||
} : false,
|
||||
},
|
||||
// Compression
|
||||
compress: true,
|
||||
// Production optimizations
|
||||
productionBrowserSourceMaps: false,
|
||||
// Performance optimizations (swcMinify removed - default in Next.js 15)
|
||||
// Enterprise Security Headers
|
||||
async headers() {
|
||||
return [
|
||||
{
|
||||
source: '/:path*',
|
||||
headers: [
|
||||
// Security Headers
|
||||
{
|
||||
key: 'X-DNS-Prefetch-Control',
|
||||
value: 'on'
|
||||
},
|
||||
{
|
||||
key: 'Strict-Transport-Security',
|
||||
value: 'max-age=63072000; includeSubDomains; preload'
|
||||
},
|
||||
{
|
||||
key: 'X-Frame-Options',
|
||||
value: 'SAMEORIGIN'
|
||||
},
|
||||
{
|
||||
key: 'X-Content-Type-Options',
|
||||
value: 'nosniff'
|
||||
},
|
||||
{
|
||||
key: 'X-XSS-Protection',
|
||||
value: '1; mode=block'
|
||||
},
|
||||
{
|
||||
key: 'Referrer-Policy',
|
||||
value: 'strict-origin-when-cross-origin'
|
||||
},
|
||||
{
|
||||
key: 'Permissions-Policy',
|
||||
value: 'camera=(), microphone=(), geolocation=(), interest-cohort=()'
|
||||
},
|
||||
{
|
||||
key: 'Content-Security-Policy',
|
||||
value: "default-src 'self'; script-src 'self' 'unsafe-eval' 'unsafe-inline' https://www.googletagmanager.com https://www.google-analytics.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https: http://localhost:8000 http://localhost:8080; font-src 'self' data:; connect-src 'self' http://localhost:8000 https://www.google-analytics.com; frame-src 'self' https://www.google.com; frame-ancestors 'self'; base-uri 'self'; form-action 'self'"
|
||||
},
|
||||
// Performance Headers
|
||||
{
|
||||
key: 'Cache-Control',
|
||||
value: 'public, max-age=31536000, immutable'
|
||||
},
|
||||
],
|
||||
},
|
||||
// Static assets caching
|
||||
{
|
||||
source: '/images/:path*',
|
||||
headers: [
|
||||
{
|
||||
key: 'Cache-Control',
|
||||
value: 'public, max-age=31536000, immutable',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
source: '/icons/:path*',
|
||||
headers: [
|
||||
{
|
||||
key: 'Cache-Control',
|
||||
value: 'public, max-age=31536000, immutable',
|
||||
},
|
||||
],
|
||||
},
|
||||
// API responses - no cache for dynamic content
|
||||
{
|
||||
source: '/api/:path*',
|
||||
headers: [
|
||||
{
|
||||
key: 'Cache-Control',
|
||||
value: 'no-store, must-revalidate',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
},
|
||||
// Redirects for SEO
|
||||
async redirects() {
|
||||
return [
|
||||
// Redirect trailing slashes
|
||||
{
|
||||
source: '/:path+/',
|
||||
destination: '/:path+',
|
||||
permanent: true,
|
||||
},
|
||||
]
|
||||
},
|
||||
// Rewrites for API proxy (Production: routes /api to backend through nginx)
|
||||
async rewrites() {
|
||||
// In development, proxy to Django backend
|
||||
// In production, nginx handles this
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
return [
|
||||
{
|
||||
source: '/api/:path*',
|
||||
destination: `${process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'}/api/:path*`,
|
||||
},
|
||||
{
|
||||
source: '/media/:path*',
|
||||
destination: `${process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'}/media/:path*`,
|
||||
},
|
||||
]
|
||||
}
|
||||
// In production, these are handled by nginx reverse proxy
|
||||
return []
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
||||
|
||||
@@ -99,12 +99,22 @@ export const OffcanvasData = [
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
title: "Contact Us",
|
||||
path: "/contact-us",
|
||||
title: "Support Center",
|
||||
path: "/support-center",
|
||||
parent_id: null,
|
||||
display_order: 7,
|
||||
submenu: null,
|
||||
created_at: "2024-01-01T00:00:00Z",
|
||||
updated_at: "2024-01-01T00:00:00Z"
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
title: "Contact Us",
|
||||
path: "/contact-us",
|
||||
parent_id: null,
|
||||
display_order: 8,
|
||||
submenu: null,
|
||||
created_at: "2024-01-01T00:00:00Z",
|
||||
updated_at: "2024-01-01T00:00:00Z"
|
||||
}
|
||||
];
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
|
||||
// modern CSS framework (Bootstrap replacement)
|
||||
@use "./utilities/modern-framework";
|
||||
@use "./utilities/content-protection";
|
||||
|
||||
/* ==============
|
||||
========= css table of contents =========
|
||||
|
||||
123
gnx-react/public/styles/utilities/_content-protection.scss
Normal file
123
gnx-react/public/styles/utilities/_content-protection.scss
Normal file
@@ -0,0 +1,123 @@
|
||||
// ============================================================================
|
||||
// Content Protection Styles
|
||||
// ============================================================================
|
||||
// Prevents content copying, text selection, and image downloading
|
||||
|
||||
// Disable text selection globally
|
||||
.content-protected {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
|
||||
// Prevent tap highlight on mobile
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
|
||||
// Disable image dragging
|
||||
img {
|
||||
-webkit-user-drag: none;
|
||||
-khtml-user-drag: none;
|
||||
-moz-user-drag: none;
|
||||
-o-user-drag: none;
|
||||
user-drag: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
// Protect all text content
|
||||
* {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Allow selection for input fields and textareas
|
||||
.content-protected input,
|
||||
.content-protected textarea,
|
||||
.content-protected [contenteditable="true"] {
|
||||
-webkit-user-select: text;
|
||||
-moz-user-select: text;
|
||||
-ms-user-select: text;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
// Image protection with overlay
|
||||
.protected-image-wrapper {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
-webkit-user-drag: none;
|
||||
-khtml-user-drag: none;
|
||||
-moz-user-drag: none;
|
||||
-o-user-drag: none;
|
||||
user-drag: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
// Transparent overlay to prevent right-click on images
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: transparent;
|
||||
pointer-events: auto;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Watermark overlay (optional - can be enabled per image)
|
||||
.watermarked-image {
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: 'GNX Soft';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%) rotate(-45deg);
|
||||
font-size: 3rem;
|
||||
font-weight: bold;
|
||||
color: rgba(255, 255, 255, 0.15);
|
||||
pointer-events: none;
|
||||
z-index: 2;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
// Hide on print (prevent print screen)
|
||||
@media print {
|
||||
.no-print {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
body::after {
|
||||
content: 'This content is protected and cannot be printed. Visit gnxsoft.com';
|
||||
display: block;
|
||||
text-align: center;
|
||||
padding: 50px;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
// Additional protection for code blocks
|
||||
.content-protected pre,
|
||||
.content-protected code {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
// Blur content when dev tools are open (optional)
|
||||
.devtools-open {
|
||||
filter: blur(5px);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ djangorestframework==3.14.0
|
||||
django-cors-headers==4.3.1
|
||||
python-decouple==3.8
|
||||
Pillow==10.1.0
|
||||
psycopg2-binary==2.9.9
|
||||
celery==5.3.4
|
||||
redis==5.0.1
|
||||
django-environ==0.11.2
|
||||
|
||||
Reference in New Issue
Block a user