Deploy LittleShop to Hostinger with Docker and BunkerWeb

- Updated Docker configuration for production deployment
- Added SilverPay integration settings
- Configured for admin.thebankofdebbie.giize.com deployment
- Includes all recent security fixes and improvements

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-09-24 13:00:17 +01:00
parent 622bdcf111
commit caff08cb6f
181 changed files with 88295 additions and 63 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,21 @@
{
"runtimeOptions": {
"tfm": "net9.0",
"frameworks": [
{
"name": "Microsoft.NETCore.App",
"version": "9.0.0"
},
{
"name": "Microsoft.AspNetCore.App",
"version": "9.0.0"
}
],
"configProperties": {
"System.GC.Server": true,
"System.Reflection.Metadata.MetadataUpdater.IsSupported": false,
"System.Reflection.NullabilityInfoContext.IsSupported": true,
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
}
}
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,210 @@
# LittleShop Standalone Deployment Guide
## Overview
This package contains everything needed to deploy LittleShop as a standalone service on the Hostinger VPS with localhost-only binding for security.
## Architecture
- **Binding**: localhost only (127.0.0.1:5000)
- **Service**: systemd managed service
- **User**: www-data (non-root)
- **Database**: SQLite (local file)
- **Logs**: /opt/littleshop/logs/
## Quick Deployment
1. **Transfer files to server**:
```bash
# From your local machine
tar -czf littleshop-deploy.tar.gz -C /mnt/c/Production/Source/LittleShop/LittleShop/publish .
scp -P 2255 littleshop-deploy.tar.gz root@srv1002428.hstgr.cloud:/tmp/
```
2. **On the server**:
```bash
cd /tmp
tar -xzf littleshop-deploy.tar.gz
chmod +x deploy.sh
sudo ./deploy.sh
```
## Configuration
### Essential Settings
Edit `/opt/littleshop/appsettings.Localhost.json`:
1. **JWT Secret** (REQUIRED):
- Generate a secure key (minimum 32 characters)
- Example: `openssl rand -base64 32`
2. **SilverPay Integration**:
- Set your API key and webhook secret
- Ensure SilverPay can reach your webhook endpoint
3. **Database**:
- SQLite database auto-created on first run
- Location: `/opt/littleshop/littleshop-production.db`
## Service Management
### Check Status
```bash
systemctl status littleshop
```
### View Logs
```bash
# System logs
journalctl -u littleshop -f
# Application logs
tail -f /opt/littleshop/logs/littleshop-*.log
```
### Restart Service
```bash
systemctl restart littleshop
```
### Stop/Start Service
```bash
systemctl stop littleshop
systemctl start littleshop
```
## Nginx Reverse Proxy (Optional)
To expose the service externally through nginx:
1. Copy nginx config:
```bash
cp nginx-littleshop.conf /etc/nginx/sites-available/littleshop
ln -s /etc/nginx/sites-available/littleshop /etc/nginx/sites-enabled/
```
2. Test and reload nginx:
```bash
nginx -t
systemctl reload nginx
```
3. Set up SSL (recommended):
```bash
certbot --nginx -d srv1002428.hstgr.cloud
```
## Security Notes
1. **Localhost Binding**: Service only listens on 127.0.0.1:5000
2. **Non-root User**: Runs as www-data user
3. **Systemd Hardening**:
- PrivateTmp=true
- NoNewPrivileges=true
- ProtectSystem=strict
- ProtectHome=true
4. **File Permissions**: Restrictive permissions on all files
## Testing
### Local Health Check
```bash
curl http://127.0.0.1:5000/api/health
```
### API Documentation
Access Swagger UI locally:
```bash
ssh -L 5000:127.0.0.1:5000 -p 2255 root@srv1002428.hstgr.cloud
# Then open browser to: http://localhost:5000/swagger
```
## Troubleshooting
### Service Won't Start
```bash
# Check logs
journalctl -u littleshop -n 100
# Check .NET runtime
dotnet --info
# Check permissions
ls -la /opt/littleshop/
```
### Database Issues
```bash
# Check database file
ls -la /opt/littleshop/*.db
# Reset database (WARNING: deletes all data)
systemctl stop littleshop
rm /opt/littleshop/littleshop-production.db*
systemctl start littleshop
```
### Port Already in Use
```bash
# Check what's using port 5000
ss -tulpn | grep :5000
# Change port in appsettings.Localhost.json if needed
```
## Updates
To update the application:
1. Build new version locally
2. Transfer to server
3. Stop service: `systemctl stop littleshop`
4. Backup database: `cp /opt/littleshop/*.db /backup/`
5. Copy new files to `/opt/littleshop/`
6. Start service: `systemctl start littleshop`
## Integration Points
### SilverPay Webhook
- Endpoint: `http://127.0.0.1:5000/api/orders/payments/webhook`
- Configure in SilverPay to point to your public URL
### TeleBot Integration
- Configure TeleBot API URL and key in appsettings
- Ensure TeleBot can reach the API endpoints
## Monitoring
### Health Check
```bash
# Add to crontab for monitoring
*/5 * * * * curl -f http://127.0.0.1:5000/api/health || systemctl restart littleshop
```
### Disk Usage
```bash
# Check database size
du -h /opt/littleshop/*.db
# Check log size
du -sh /opt/littleshop/logs/
```
## Backup
### Database Backup
```bash
# Create backup
sqlite3 /opt/littleshop/littleshop-production.db ".backup /backup/littleshop-$(date +%Y%m%d).db"
# Restore backup
systemctl stop littleshop
cp /backup/littleshop-20250123.db /opt/littleshop/littleshop-production.db
chown www-data:www-data /opt/littleshop/littleshop-production.db
systemctl start littleshop
```
## Support
For issues or questions:
- Check application logs first
- Review this documentation
- Check service status and system logs

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,31 @@
{
"ProjectPath": "C:\\Production\\Source\\LittleShop\\LittleShop",
"ProjectType": "Project (ASP.NET Core)",
"TotalEndpoints": 115,
"AuthenticatedEndpoints": 78,
"TestableStates": 3,
"IdentifiedGaps": 224,
"SuggestedTests": 190,
"DeadLinks": 0,
"HttpErrors": 97,
"VisualIssues": 0,
"SecurityInsights": 1,
"PerformanceInsights": 1,
"OverallTestCoverage": 16.956521739130434,
"VisualConsistencyScore": 0,
"CriticalRecommendations": [
"CRITICAL: Test coverage is only 17.0% - implement comprehensive test suite",
"HIGH: Address 97 HTTP errors in the application",
"MEDIUM: Improve visual consistency - current score 0.0%",
"HIGH: Address 224 testing gaps for comprehensive coverage"
],
"GeneratedFiles": [
"C:\\Production\\Source\\LittleShop\\LittleShop\\TestAgent_Results\\project_structure.json",
"C:\\Production\\Source\\LittleShop\\LittleShop\\TestAgent_Results\\authentication_analysis.json",
"C:\\Production\\Source\\LittleShop\\LittleShop\\TestAgent_Results\\endpoint_discovery.json",
"C:\\Production\\Source\\LittleShop\\LittleShop\\TestAgent_Results\\coverage_analysis.json",
"C:\\Production\\Source\\LittleShop\\LittleShop\\TestAgent_Results\\error_detection.json",
"C:\\Production\\Source\\LittleShop\\LittleShop\\TestAgent_Results\\visual_testing.json",
"C:\\Production\\Source\\LittleShop\\LittleShop\\TestAgent_Results\\intelligent_analysis.json"
]
}

View File

@@ -0,0 +1,79 @@
{
"BusinessLogicInsights": [
{
"Component": "Claude CLI Integration",
"Insight": "Error analyzing business logic: Failed to execute Claude CLI: An error occurred trying to start process \u0027claude\u0027 with working directory \u0027C:\\Production\\Source\\TestAgent\u0027. The system cannot find the file specified.",
"Complexity": "Unknown",
"PotentialIssues": [],
"TestingRecommendations": [],
"Priority": "Medium"
}
],
"TestScenarioSuggestions": [
{
"ScenarioName": "Claude CLI Integration Error",
"Description": "Error generating test scenarios: Failed to execute Claude CLI: An error occurred trying to start process \u0027claude\u0027 with working directory \u0027C:\\Production\\Source\\TestAgent\u0027. The system cannot find the file specified.",
"TestType": "",
"Steps": [],
"ExpectedOutcomes": [],
"Priority": "Medium",
"RequiredData": [],
"Dependencies": []
}
],
"SecurityInsights": [
{
"VulnerabilityType": "Analysis Error",
"Location": "",
"Description": "Error analyzing security: Failed to execute Claude CLI: An error occurred trying to start process \u0027claude\u0027 with working directory \u0027C:\\Production\\Source\\TestAgent\u0027. The system cannot find the file specified.",
"Severity": "Medium",
"Recommendations": [],
"TestingApproaches": []
}
],
"PerformanceInsights": [
{
"Component": "Analysis Error",
"PotentialBottleneck": "Error analyzing performance: Failed to execute Claude CLI: An error occurred trying to start process \u0027claude\u0027 with working directory \u0027C:\\Production\\Source\\TestAgent\u0027. The system cannot find the file specified.",
"Impact": "Unknown",
"OptimizationSuggestions": [],
"TestingStrategies": []
}
],
"ArchitecturalRecommendations": [
{
"Category": "Analysis Error",
"Recommendation": "Error generating architectural recommendations: Failed to execute Claude CLI: An error occurred trying to start process \u0027claude\u0027 with working directory \u0027C:\\Production\\Source\\TestAgent\u0027. The system cannot find the file specified.",
"Rationale": "",
"Impact": "Unknown",
"ImplementationSteps": []
}
],
"GeneratedTestCases": [
{
"TestName": "Claude CLI Integration Error",
"TestCategory": "Error",
"Description": "Error generating test cases: Failed to execute Claude CLI: An error occurred trying to start process \u0027claude\u0027 with working directory \u0027C:\\Production\\Source\\TestAgent\u0027. The system cannot find the file specified.",
"TestCode": "",
"TestData": [],
"ExpectedOutcome": "",
"Reasoning": ""
}
],
"Summary": {
"TotalInsights": 4,
"HighPriorityItems": 0,
"GeneratedTestCases": 1,
"SecurityIssuesFound": 1,
"PerformanceOptimizations": 1,
"KeyFindings": [
"Performance optimization opportunities identified"
],
"NextSteps": [
"Review and prioritize security recommendations",
"Implement generated test cases",
"Address high-priority business logic testing gaps",
"Consider architectural improvements for better testability"
]
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,17 @@
{
"ConsistencyTests": [],
"AuthStateComparisons": [],
"ResponsiveTests": [],
"ComponentTests": [],
"Regressions": [],
"Summary": {
"TotalTests": 0,
"PassedTests": 0,
"FailedTests": 0,
"ConsistencyViolations": 0,
"ResponsiveIssues": 0,
"VisualRegressions": 0,
"OverallScore": 0,
"Recommendations": []
}
}

Binary file not shown.

View File

@@ -0,0 +1,43 @@
{
"ConnectionStrings": {
"DefaultConnection": "Data Source=littleshop-dev.db"
},
"Jwt": {
"Key": "DEV_8aiNFkRrOao7/vleviWM8EP5800dMOh2hlaKGJoQOQvaxxOVHM3eLAb3+5KN8EcjKZKREHttGKUfvtQrV3ZM4A==",
"Issuer": "LittleShop-Dev",
"Audience": "LittleShop-Dev",
"ExpiryInHours": 2
},
"SilverPay": {
"BaseUrl": "http://localhost:8001",
"ApiKey": "sp_test_key_development",
"WebhookSecret": "webhook_secret_dev",
"DefaultWebhookUrl": "http://localhost:5000/api/orders/payments/webhook",
"AllowUnsignedWebhooks": true
},
"Logging": {
"LogLevel": {
"Default": "Debug",
"Microsoft.AspNetCore": "Information",
"LittleShop": "Debug"
}
},
"Security": {
"AllowInsecureSSL": true,
"EnableDetailedErrors": true
},
"CORS": {
"AllowedOrigins": [
"http://localhost:3000",
"http://localhost:5173",
"http://localhost:5000",
"http://localhost:5001",
"https://localhost:5001",
"http://localhost:8080"
]
},
"TeleBot": {
"ApiUrl": "http://localhost:8080",
"ApiKey": "development-key-replace-in-production"
}
}

View File

@@ -0,0 +1,46 @@
{
"ConnectionStrings": {
"DefaultConnection": "Data Source=/app/data/littleshop.db"
},
"Jwt": {
"Key": "YourSuperSecretKeyThatIsAtLeast32CharactersLong!",
"Issuer": "LittleShop",
"Audience": "LittleShop",
"ExpiryInHours": 24
},
"BTCPayServer": {
"BaseUrl": "https://thebankofdebbie.giize.com",
"ApiKey": "db920209c0101efdbd1c6b6d1c99a48e3ba9d0de",
"StoreId": "CvdvHoncGLM7TdMYRAG6Z15YuxQfxeMWRYwi9gvPhh5R",
"WebhookSecret": "your-webhook-secret-here"
},
"RoyalMail": {
"ClientId": "",
"ClientSecret": "",
"BaseUrl": "https://api.royalmail.net/",
"SenderAddress1": "SilverLabs Ltd, 123 Business Street",
"SenderCity": "London",
"SenderPostCode": "SW1A 1AA",
"SenderCountry": "United Kingdom"
},
"WebPush": {
"VapidPublicKey": "BMc6fFJZ8oIQKQzcl3kMnP9tTsjrm3oI_VxLt3lAGYUMWGInzDKn7jqclEoZzjvXy1QXGFb3dIun8mVBwh-QuS4",
"VapidPrivateKey": "dYuuagbz2CzCnPDFUpO_qkGLBgnN3MEFZQnjXNkc1MY",
"Subject": "mailto:admin@littleshop.local"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"BTCPayServer": "Debug"
}
},
"AllowedHosts": "*",
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://0.0.0.0:8080"
}
}
}
}

View File

@@ -0,0 +1,71 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.EntityFrameworkCore": "Warning"
}
},
"ConnectionStrings": {
"DefaultConnection": "Data Source=/opt/littleshop/littleshop-production.db"
},
"Jwt": {
"Key": "your-secure-jwt-secret-key-here-minimum-32-chars",
"Issuer": "LittleShop-Production",
"Audience": "LittleShop-Production",
"ExpiryInHours": 24
},
"SilverPay": {
"BaseUrl": "http://31.97.57.205:8001",
"ApiKey": "YOUR_SILVERPAY_API_KEY",
"WebhookSecret": "YOUR_WEBHOOK_SECRET",
"DefaultWebhookUrl": "http://localhost:5000/api/orders/payments/webhook",
"AllowUnsignedWebhooks": false
},
"RoyalMail": {
"ClientId": "YOUR_ROYAL_MAIL_CLIENT_ID",
"ClientSecret": "YOUR_ROYAL_MAIL_CLIENT_SECRET",
"BaseUrl": "https://api.royalmail.net/",
"SenderAddress1": "Your Address",
"SenderCity": "Your City",
"SenderPostCode": "Your Postcode",
"SenderCountry": "United Kingdom"
},
"WebPush": {
"VapidPublicKey": "YOUR_VAPID_PUBLIC_KEY",
"VapidPrivateKey": "YOUR_VAPID_PRIVATE_KEY",
"Subject": "mailto:admin@yourdomain.com"
},
"AllowedHosts": "localhost;127.0.0.1",
"Urls": "http://127.0.0.1:5000",
"ForwardedHeaders": {
"ForwardedProtoHeaderName": "X-Forwarded-Proto",
"ForwardedForHeaderName": "X-Forwarded-For",
"ForwardedHostHeaderName": "X-Forwarded-Host"
},
"TeleBot": {
"ApiUrl": "YOUR_TELEBOT_API_URL",
"ApiKey": "YOUR_TELEBOT_API_KEY"
},
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
"MinimumLevel": "Information",
"WriteTo": [
{
"Name": "Console",
"Args": {
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj} {Properties:j}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "/opt/littleshop/logs/littleshop-.log",
"rollingInterval": "Day",
"retainedFileCountLimit": 7,
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj} {Properties:j}{NewLine}{Exception}"
}
}
]
}
}

View File

@@ -0,0 +1,71 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.EntityFrameworkCore": "Warning"
}
},
"ConnectionStrings": {
"DefaultConnection": "Data Source=littleshop-production.db"
},
"Jwt": {
"Key": "${JWT_SECRET_KEY}",
"Issuer": "LittleShop-Production",
"Audience": "LittleShop-Production",
"ExpiryInHours": 24
},
"SilverPay": {
"BaseUrl": "${SILVERPAY_BASE_URL}",
"ApiKey": "${SILVERPAY_API_KEY}",
"WebhookSecret": "${SILVERPAY_WEBHOOK_SECRET}",
"DefaultWebhookUrl": "${SILVERPAY_WEBHOOK_URL}",
"AllowUnsignedWebhooks": false
},
"RoyalMail": {
"ClientId": "${ROYALMAIL_CLIENT_ID}",
"ClientSecret": "${ROYALMAIL_CLIENT_SECRET}",
"BaseUrl": "https://api.royalmail.net/",
"SenderAddress1": "${ROYALMAIL_SENDER_ADDRESS}",
"SenderCity": "${ROYALMAIL_SENDER_CITY}",
"SenderPostCode": "${ROYALMAIL_SENDER_POSTCODE}",
"SenderCountry": "United Kingdom"
},
"WebPush": {
"VapidPublicKey": "${WEBPUSH_VAPID_PUBLIC_KEY}",
"VapidPrivateKey": "${WEBPUSH_VAPID_PRIVATE_KEY}",
"Subject": "${WEBPUSH_SUBJECT}"
},
"AllowedHosts": "*",
"Urls": "http://+:8080",
"ForwardedHeaders": {
"ForwardedProtoHeaderName": "X-Forwarded-Proto",
"ForwardedForHeaderName": "X-Forwarded-For",
"ForwardedHostHeaderName": "X-Forwarded-Host"
},
"TeleBot": {
"ApiUrl": "${TELEBOT_API_URL}",
"ApiKey": "${TELEBOT_API_KEY}"
},
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
"MinimumLevel": "Information",
"WriteTo": [
{
"Name": "Console",
"Args": {
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj} {Properties:j}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "/app/logs/littleshop-.log",
"rollingInterval": "Day",
"retainedFileCountLimit": 7,
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj} {Properties:j}{NewLine}{Exception}"
}
}
]
}
}

View File

@@ -0,0 +1,32 @@
{
"ConnectionStrings": {
"DefaultConnection": "Data Source=littleshop.db"
},
"Jwt": {
"Key": "8aiNFkRrOao7/vleviWM8EP5800dMOh2hlaKGJoQOQvaxxOVHM3eLAb3+5KN8EcjKZKREHttGKUfvtQrV3ZM4A==",
"Issuer": "LittleShop",
"Audience": "LittleShop",
"ExpiryInHours": 24
},
"RoyalMail": {
"ClientId": "",
"ClientSecret": "",
"BaseUrl": "https://api.royalmail.net/",
"SenderAddress1": "SilverLabs Ltd, 123 Business Street",
"SenderCity": "London",
"SenderPostCode": "SW1A 1AA",
"SenderCountry": "United Kingdom"
},
"WebPush": {
"VapidPublicKey": "BMc6fFJZ8oIQKQzcl3kMnP9tTsjrm3oI_VxLt3lAGYUMWGInzDKn7jqclEoZzjvXy1QXGFb3dIun8mVBwh-QuS4",
"VapidPrivateKey": "dYuuagbz2CzCnPDFUpO_qkGLBgnN3MEFZQnjXNkc1MY",
"Subject": "mailto:admin@littleshop.local"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@@ -0,0 +1,133 @@
#!/bin/bash
# LittleShop Deployment Script for Hostinger VPS
# This script installs LittleShop as a systemd service with localhost-only binding
set -e
echo "=== LittleShop Deployment Script ==="
echo
# Check if running as root
if [ "$EUID" -ne 0 ]; then
echo "Please run as root (use sudo)"
exit 1
fi
# Variables
APP_DIR="/opt/littleshop"
SERVICE_FILE="/etc/systemd/system/littleshop.service"
RUNTIME_VERSION="9.0"
echo "1. Checking .NET Runtime..."
if ! command -v dotnet &> /dev/null; then
echo " .NET Runtime not found. Installing..."
# Add Microsoft package repository
wget https://packages.microsoft.com/config/debian/13/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
dpkg -i packages-microsoft-prod.deb
rm packages-microsoft-prod.deb
# Install .NET Runtime
apt-get update
apt-get install -y aspnetcore-runtime-$RUNTIME_VERSION
echo " .NET Runtime installed successfully"
else
echo " .NET Runtime already installed: $(dotnet --version)"
fi
echo
echo "2. Creating application directory..."
mkdir -p $APP_DIR
mkdir -p $APP_DIR/logs
mkdir -p $APP_DIR/uploads
echo
echo "3. Stopping existing service (if any)..."
if systemctl is-active --quiet littleshop; then
systemctl stop littleshop
echo " Service stopped"
else
echo " No existing service found"
fi
echo
echo "4. Copying application files..."
cp -r ./* $APP_DIR/
chmod +x $APP_DIR/LittleShop
echo
echo "5. Setting up database..."
if [ ! -f "$APP_DIR/littleshop-production.db" ]; then
echo " Database will be created on first run"
else
echo " Existing database found, will be preserved"
fi
echo
echo "6. Setting permissions..."
chown -R www-data:www-data $APP_DIR
chmod 750 $APP_DIR
chmod -R 640 $APP_DIR/*
chmod 750 $APP_DIR/LittleShop
chmod -R 770 $APP_DIR/logs
chmod -R 770 $APP_DIR/uploads
echo
echo "7. Installing systemd service..."
cp $APP_DIR/littleshop.service $SERVICE_FILE
systemctl daemon-reload
systemctl enable littleshop
echo
echo "8. Starting service..."
systemctl start littleshop
echo
echo "9. Checking service status..."
sleep 3
if systemctl is-active --quiet littleshop; then
echo " ✓ Service is running"
echo
echo "=== Deployment Complete ==="
echo
echo "Service Status:"
systemctl status littleshop --no-pager | head -n 10
echo
echo "Application is running on: http://127.0.0.1:5000"
echo "Logs location: $APP_DIR/logs/"
echo
echo "Useful commands:"
echo " - Check status: systemctl status littleshop"
echo " - View logs: journalctl -u littleshop -f"
echo " - Restart: systemctl restart littleshop"
echo " - Stop: systemctl stop littleshop"
echo
echo "To test locally on the server:"
echo " curl http://127.0.0.1:5000/api/health"
else
echo " ✗ Service failed to start"
echo " Check logs with: journalctl -u littleshop -n 50"
exit 1
fi
echo
echo "10. Testing health endpoint..."
sleep 2
if curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:5000/api/health | grep -q "200"; then
echo " ✓ Health check passed"
else
echo " ⚠ Health check failed or service still starting"
echo " Try again with: curl http://127.0.0.1:5000/api/health"
fi
echo
echo "=== Setup Complete ==="
echo
echo "IMPORTANT: Edit /opt/littleshop/appsettings.Localhost.json to configure:"
echo " - JWT secret key"
echo " - SilverPay API credentials"
echo " - Royal Mail API credentials (if using)"
echo " - WebPush VAPID keys (if using)"
echo
echo "After editing configuration, restart the service:"
echo " systemctl restart littleshop"

Binary file not shown.

View File

@@ -0,0 +1,26 @@
[Unit]
Description=LittleShop ASP.NET Core Web Application
After=network.target
[Service]
Type=notify
WorkingDirectory=/opt/littleshop
ExecStart=/usr/bin/dotnet /opt/littleshop/LittleShop.dll --environment=Localhost
Restart=always
RestartSec=10
KillSignal=SIGINT
SyslogIdentifier=littleshop
User=www-data
Environment=ASPNETCORE_ENVIRONMENT=Localhost
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
Environment=DOTNET_CLI_TELEMETRY_OPTOUT=1
# Security hardening
PrivateTmp=true
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/littleshop
ReadWritePaths=/opt/littleshop/logs
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,65 @@
# Nginx configuration for LittleShop (optional)
# Place this file in /etc/nginx/sites-available/ and create a symlink to sites-enabled/
# This configuration proxies requests from external interface to localhost-only service
server {
listen 80;
server_name srv1002428.hstgr.cloud;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Increase max body size for file uploads
client_max_body_size 50M;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
# Timeouts for long-running requests
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
send_timeout 600;
}
# Static files (if needed)
location /uploads {
alias /opt/littleshop/uploads;
expires 30d;
add_header Cache-Control "public, immutable";
}
location /wwwroot {
alias /opt/littleshop/wwwroot;
expires 30d;
add_header Cache-Control "public, immutable";
}
# Health check endpoint (direct access for monitoring)
location /health {
proxy_pass http://127.0.0.1:5000/api/health;
access_log off;
}
}
# SSL configuration (to be added after Let's Encrypt setup)
# server {
# listen 443 ssl http2;
# server_name srv1002428.hstgr.cloud;
#
# ssl_certificate /etc/letsencrypt/live/srv1002428.hstgr.cloud/fullchain.pem;
# ssl_certificate_key /etc/letsencrypt/live/srv1002428.hstgr.cloud/privkey.pem;
#
# # ... rest of configuration same as above ...
# }

View File

@@ -0,0 +1,415 @@
/*
* 🏢 CORPORATE STEEL THEME 🏢
* Professional dark theme with subtle metallic accents
* Clean, corporate aesthetic with steel/metal textures
*/
:root {
/* Corporate Color Palette */
--corp-primary: #6A4C93; /* Muted Purple */
--corp-secondary: #8B5A8C; /* Soft Purple */
--corp-accent: #9B69B0; /* Light Purple */
/* Steel/Metal Colors */
--steel-dark: #1C1C1E; /* Dark Steel */
--steel-medium: #2C2C2E; /* Medium Steel */
--steel-light: #3A3A3C; /* Light Steel */
--steel-accent: #48484A; /* Steel Accent */
/* Text Colors */
--text-primary: #FFFFFF; /* White */
--text-secondary: #E5E5E7; /* Light Grey */
--text-muted: #AEAEB2; /* Muted Grey */
/* Subtle Gradients */
--steel-gradient: linear-gradient(135deg,
var(--steel-dark) 0%,
var(--steel-medium) 50%,
var(--steel-light) 100%);
--purple-gradient: linear-gradient(135deg,
var(--corp-primary) 0%,
var(--corp-secondary) 100%);
/* Shadows */
--shadow-subtle: 0 2px 8px rgba(0, 0, 0, 0.3);
--shadow-medium: 0 4px 16px rgba(0, 0, 0, 0.4);
--shadow-strong: 0 8px 32px rgba(0, 0, 0, 0.5);
/* Transitions */
--transition-smooth: 0.2s ease;
}
/* Global Corporate Base */
body {
background: var(--steel-dark) !important;
background-image:
linear-gradient(45deg, transparent 49%, rgba(106, 76, 147, 0.03) 50%, transparent 51%),
linear-gradient(-45deg, transparent 49%, rgba(139, 90, 140, 0.02) 50%, transparent 51%);
background-size: 20px 20px, 24px 24px;
color: var(--text-primary) !important;
font-family: 'Segoe UI', 'Roboto', 'Arial', sans-serif !important;
font-weight: 400;
line-height: 1.6;
}
/* Card Styling */
.card,
.rz-card {
background: var(--steel-gradient) !important;
border: 1px solid var(--steel-accent) !important;
border-radius: 8px !important;
box-shadow: var(--shadow-medium) !important;
color: var(--text-primary) !important;
transition: all var(--transition-smooth) !important;
}
.card:hover,
.rz-card:hover {
transform: translateY(-2px) !important;
box-shadow: var(--shadow-strong) !important;
border-color: var(--corp-accent) !important;
}
.card-header,
.rz-card-header {
background: linear-gradient(90deg,
var(--steel-medium) 0%,
var(--steel-light) 100%) !important;
border-bottom: 1px solid var(--steel-accent) !important;
color: var(--text-primary) !important;
font-weight: 600 !important;
}
.card-body,
.rz-card-body {
background: var(--steel-medium) !important;
color: var(--text-primary) !important;
}
/* Button Styling */
.btn,
.rz-button {
background: var(--purple-gradient) !important;
border: 1px solid var(--corp-accent) !important;
border-radius: 6px !important;
color: var(--text-primary) !important;
font-weight: 500 !important;
transition: all var(--transition-smooth) !important;
position: relative !important;
}
.btn:hover,
.rz-button:hover {
background: linear-gradient(135deg,
var(--corp-secondary) 0%,
var(--corp-accent) 100%) !important;
border-color: var(--corp-accent) !important;
transform: translateY(-1px) !important;
color: var(--text-primary) !important;
}
.btn:active,
.rz-button:active {
transform: scale(0.98) !important;
}
/* Navigation */
.navbar-dark {
background: var(--steel-gradient) !important;
border-bottom: 1px solid var(--steel-accent) !important;
}
.navbar-brand {
color: var(--text-primary) !important;
font-weight: 600 !important;
}
.nav-link {
color: var(--text-secondary) !important;
transition: all var(--transition-smooth) !important;
}
.nav-link:hover {
color: var(--corp-accent) !important;
}
/* Form Controls */
.form-control,
.form-select,
.rz-textbox,
.rz-dropdown {
background: var(--steel-medium) !important;
border: 2px solid var(--steel-accent) !important;
border-radius: 6px !important;
color: var(--text-primary) !important;
transition: all var(--transition-smooth) !important;
}
.form-control:focus,
.form-select:focus,
.rz-textbox:focus,
.rz-dropdown:focus {
background: var(--steel-light) !important;
border-color: var(--corp-primary) !important;
box-shadow: 0 0 0 0.25rem rgba(106, 76, 147, 0.25) !important;
outline: none !important;
}
.form-label {
color: var(--text-secondary) !important;
font-weight: 500 !important;
}
/* Tables */
.table {
background: var(--steel-medium) !important;
color: var(--text-primary) !important;
border-radius: 8px !important;
overflow: hidden !important;
}
.table th {
background: var(--steel-light) !important;
color: var(--text-primary) !important;
border-bottom: 2px solid var(--steel-accent) !important;
font-weight: 600 !important;
}
.table td {
border-bottom: 1px solid var(--steel-accent) !important;
color: var(--text-primary) !important;
}
.table tbody tr:hover {
background: rgba(106, 76, 147, 0.1) !important;
}
/* Mobile Navigation */
.mobile-header {
background: var(--steel-gradient) !important;
border-bottom: 1px solid var(--steel-accent) !important;
}
.mobile-bottom-nav {
background: var(--steel-medium) !important;
border-top: 1px solid var(--steel-accent) !important;
}
.mobile-nav-item {
color: var(--text-secondary) !important;
transition: all var(--transition-smooth) !important;
}
.mobile-nav-item:hover {
color: var(--corp-accent) !important;
background: rgba(106, 76, 147, 0.1) !important;
}
.mobile-nav-item.active {
color: var(--text-primary) !important;
background: var(--purple-gradient) !important;
}
.mobile-sidebar {
background: var(--steel-gradient) !important;
border-right: 1px solid var(--steel-accent) !important;
}
.mobile-sidebar-header {
background: var(--purple-gradient) !important;
}
.mobile-sidebar-link {
color: var(--text-secondary) !important;
transition: all var(--transition-smooth) !important;
}
.mobile-sidebar-link:hover {
background: rgba(106, 76, 147, 0.1) !important;
color: var(--corp-accent) !important;
}
.mobile-sidebar-link.active {
background: var(--purple-gradient) !important;
color: var(--text-primary) !important;
}
/* Alerts and Notifications */
.alert {
background: var(--steel-medium) !important;
border: 1px solid var(--steel-accent) !important;
color: var(--text-primary) !important;
}
.alert-success {
border-color: #28a745 !important;
background: linear-gradient(135deg, var(--steel-medium), rgba(40, 167, 69, 0.1)) !important;
}
.alert-danger {
border-color: #dc3545 !important;
background: linear-gradient(135deg, var(--steel-medium), rgba(220, 53, 69, 0.1)) !important;
}
.alert-warning {
border-color: #ffc107 !important;
background: linear-gradient(135deg, var(--steel-medium), rgba(255, 193, 7, 0.1)) !important;
}
/* Mobile Cards */
.mobile-card,
.order-card {
background: var(--steel-gradient) !important;
border: 1px solid var(--steel-accent) !important;
color: var(--text-primary) !important;
}
.mobile-card-header,
.order-header {
background: var(--steel-light) !important;
color: var(--text-primary) !important;
border-bottom: 1px solid var(--steel-accent) !important;
}
.mobile-card-body,
.order-body {
background: var(--steel-medium) !important;
color: var(--text-primary) !important;
}
.mobile-card-footer {
background: var(--steel-light) !important;
color: var(--text-primary) !important;
border-top: 1px solid var(--steel-accent) !important;
}
/* Status Badges */
.status-badge {
background: var(--steel-light) !important;
color: var(--text-primary) !important;
border: 1px solid var(--steel-accent) !important;
}
.status-badge.pending {
background: linear-gradient(135deg, var(--steel-light), rgba(255, 193, 7, 0.2)) !important;
color: #ffc107 !important;
}
.status-badge.processing {
background: linear-gradient(135deg, var(--steel-light), rgba(23, 162, 184, 0.2)) !important;
color: #17a2b8 !important;
}
.status-badge.shipped {
background: linear-gradient(135deg, var(--steel-light), rgba(40, 167, 69, 0.2)) !important;
color: #28a745 !important;
}
/* Breadcrumbs and Links */
a {
color: var(--corp-accent) !important;
transition: color var(--transition-smooth) !important;
}
a:hover {
color: var(--text-primary) !important;
}
/* Dropdowns */
.dropdown-menu {
background: var(--steel-medium) !important;
border: 1px solid var(--steel-accent) !important;
box-shadow: var(--shadow-medium) !important;
}
.dropdown-item {
color: var(--text-secondary) !important;
transition: all var(--transition-smooth) !important;
}
.dropdown-item:hover {
background: rgba(106, 76, 147, 0.2) !important;
color: var(--text-primary) !important;
}
/* List Groups */
.list-group-item {
background: var(--steel-medium) !important;
border: 1px solid var(--steel-accent) !important;
color: var(--text-primary) !important;
}
.list-group-item:hover {
background: var(--steel-light) !important;
}
/* Override any remaining Bootstrap defaults */
.container,
.container-fluid {
color: var(--text-primary) !important;
}
h1, h2, h3, h4, h5, h6 {
color: var(--text-primary) !important;
font-weight: 600 !important;
}
p {
color: var(--text-secondary) !important;
}
/* Subtle Steel Texture */
.steel-texture {
background-image:
linear-gradient(45deg, rgba(255,255,255,0.02) 25%, transparent 25%),
linear-gradient(-45deg, rgba(255,255,255,0.02) 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, rgba(255,255,255,0.02) 75%),
linear-gradient(-45deg, transparent 75%, rgba(255,255,255,0.02) 75%);
background-size: 4px 4px;
background-position: 0 0, 0 2px, 2px -2px, -2px 0px;
}
/* Corporate Professional Styling */
.corporate-card {
background: var(--steel-gradient) !important;
border: 1px solid var(--steel-accent) !important;
border-radius: 8px !important;
box-shadow: var(--shadow-subtle) !important;
}
.corporate-card:hover {
box-shadow: var(--shadow-medium) !important;
transform: translateY(-1px) !important;
}
/* Remove any epileptic-inducing effects */
* {
animation: none !important;
}
/* Clean scrollbars */
::-webkit-scrollbar {
width: 8px;
background: var(--steel-dark);
}
::-webkit-scrollbar-track {
background: var(--steel-medium);
}
::-webkit-scrollbar-thumb {
background: var(--steel-light);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--corp-primary);
}
/* Professional Focus States */
.form-control:focus,
.form-select:focus {
border-color: var(--corp-primary) !important;
box-shadow: 0 0 0 0.2rem rgba(106, 76, 147, 0.25) !important;
}

View File

@@ -0,0 +1,444 @@
/*
* Modern Clean Admin Theme
* Mobile-first, professional, and user-friendly
*/
:root {
/* Modern Color Palette */
--primary-blue: #2563eb;
--primary-purple: #7c3aed;
--success-green: #059669;
--warning-orange: #d97706;
--danger-red: #dc2626;
/* Neutral Greys */
--grey-50: #f9fafb;
--grey-100: #f3f4f6;
--grey-200: #e5e7eb;
--grey-300: #d1d5db;
--grey-400: #9ca3af;
--grey-500: #6b7280;
--grey-600: #4b5563;
--grey-700: #374151;
--grey-800: #1f2937;
--grey-900: #111827;
/* Spacing */
--spacing-xs: 0.25rem;
--spacing-sm: 0.5rem;
--spacing-md: 1rem;
--spacing-lg: 1.5rem;
--spacing-xl: 2rem;
/* Border Radius */
--radius-sm: 0.375rem;
--radius-md: 0.5rem;
--radius-lg: 0.75rem;
--radius-xl: 1rem;
/* Shadows */
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
/* Typography */
--font-sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
--font-mono: ui-monospace, SFMono-Regular, "SF Mono", Consolas, "Liberation Mono", Menlo, monospace;
}
/* Global Styles */
body {
font-family: var(--font-sans);
background-color: var(--grey-50);
color: var(--grey-900);
line-height: 1.6;
}
/* Modern Card Styling */
.card {
border: 1px solid var(--grey-200);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-sm);
background: white;
transition: all 0.2s ease;
}
.card:hover {
box-shadow: var(--shadow-md);
transform: translateY(-1px);
}
.card-header {
background: var(--grey-50);
border-bottom: 1px solid var(--grey-200);
padding: var(--spacing-lg);
font-weight: 600;
color: var(--grey-700);
}
.card-body {
padding: var(--spacing-lg);
}
/* Modern Button Styling */
.btn {
border-radius: var(--radius-md);
font-weight: 500;
padding: 0.625rem 1.25rem;
transition: all 0.2s ease;
border: none;
}
.btn-primary {
background: linear-gradient(135deg, var(--primary-blue) 0%, var(--primary-purple) 100%);
color: white;
}
.btn-primary:hover {
background: linear-gradient(135deg, #1d4ed8 0%, #6d28d9 100%);
transform: translateY(-1px);
box-shadow: var(--shadow-md);
}
.btn-success {
background: var(--success-green);
color: white;
}
.btn-success:hover {
background: #047857;
transform: translateY(-1px);
}
.btn-outline-primary {
border: 2px solid var(--primary-blue);
color: var(--primary-blue);
background: white;
}
.btn-outline-primary:hover {
background: var(--primary-blue);
color: white;
transform: translateY(-1px);
}
.btn-sm {
padding: 0.5rem 1rem;
font-size: 0.875rem;
}
/* Modern Navigation */
.navbar {
background: white !important;
border-bottom: 1px solid var(--grey-200);
box-shadow: var(--shadow-sm);
padding: var(--spacing-md) 0;
}
.navbar-brand {
color: var(--grey-900) !important;
font-weight: 700;
font-size: 1.25rem;
}
.navbar-nav .nav-link {
color: var(--grey-600) !important;
font-weight: 500;
padding: 0.75rem 1rem;
border-radius: var(--radius-md);
margin: 0 0.25rem;
transition: all 0.2s ease;
}
.navbar-nav .nav-link:hover {
color: var(--primary-blue) !important;
background: var(--grey-50);
}
/* Form Controls */
.form-control,
.form-select {
border: 2px solid var(--grey-200);
border-radius: var(--radius-md);
padding: 0.75rem 1rem;
transition: all 0.2s ease;
background: white;
}
.form-control:focus,
.form-select:focus {
border-color: var(--primary-blue);
box-shadow: 0 0 0 0.25rem rgba(37, 99, 235, 0.1);
outline: none;
}
.form-label {
font-weight: 600;
color: var(--grey-700);
margin-bottom: var(--spacing-sm);
}
/* Table Styling */
.table {
background: white;
border-radius: var(--radius-lg);
overflow: hidden;
box-shadow: var(--shadow-sm);
}
.table thead th {
background: var(--grey-50);
border-bottom: 2px solid var(--grey-200);
font-weight: 600;
color: var(--grey-700);
padding: 1rem;
}
.table tbody td {
padding: 1rem;
border-bottom: 1px solid var(--grey-100);
vertical-align: middle;
}
.table tbody tr:hover {
background: var(--grey-50);
}
/* Status Badges */
.badge {
font-weight: 500;
padding: 0.5rem 0.75rem;
border-radius: var(--radius-sm);
}
.bg-success {
background-color: var(--success-green) !important;
}
.bg-warning {
background-color: var(--warning-orange) !important;
}
.bg-danger {
background-color: var(--danger-red) !important;
}
.bg-info {
background-color: var(--primary-blue) !important;
}
/* Page Headers */
h1, h2, h3, h4, h5, h6 {
color: var(--grey-900);
font-weight: 700;
line-height: 1.25;
}
h1 {
font-size: 2rem;
margin-bottom: var(--spacing-lg);
}
/* Mobile Responsive Design */
@media (max-width: 768px) {
.container-fluid {
padding: var(--spacing-md);
}
.card {
margin-bottom: var(--spacing-lg);
border-radius: var(--radius-md);
}
.card-header {
padding: var(--spacing-md);
}
.card-body {
padding: var(--spacing-md);
}
.btn {
width: 100%;
margin-bottom: var(--spacing-sm);
}
.btn:last-child {
margin-bottom: 0;
}
/* Mobile Table - Stack on Small Screens */
@media (max-width: 768px) {
.table-responsive {
border: none;
}
.table {
font-size: 0.875rem;
}
.table thead {
display: none;
}
.table tbody,
.table tbody tr,
.table tbody td {
display: block;
width: 100%;
}
.table tbody tr {
background: white;
border: 1px solid var(--grey-200);
border-radius: var(--radius-md);
margin-bottom: var(--spacing-md);
padding: var(--spacing-md);
box-shadow: var(--shadow-sm);
}
.table tbody td {
border: none;
padding: var(--spacing-sm) 0;
position: relative;
padding-left: 40%;
}
.table tbody td:before {
content: attr(data-label) ": ";
position: absolute;
left: 0;
width: 35%;
font-weight: 600;
color: var(--grey-600);
}
}
/* Mobile Navigation */
.navbar-toggler {
border: none;
padding: 0.25rem 0.5rem;
}
.navbar-collapse {
margin-top: var(--spacing-md);
}
.navbar-nav .nav-link {
padding: var(--spacing-md);
margin: var(--spacing-xs) 0;
background: var(--grey-50);
border-radius: var(--radius-md);
}
}
/* Modern Form Layout */
.row .col-md-6 .form-group,
.row .col-md-6 .mb-3 {
margin-bottom: var(--spacing-lg);
}
/* Action Buttons */
.d-flex.justify-content-between .btn {
min-width: 120px;
}
/* Loading States */
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
/* Focus States for Accessibility */
.btn:focus,
.form-control:focus,
.form-select:focus {
outline: 2px solid var(--primary-blue);
outline-offset: 2px;
}
/* Toast/Alert Improvements */
.alert {
border: none;
border-radius: var(--radius-md);
border-left: 4px solid;
}
.alert-success {
background: #ecfdf5;
color: #065f46;
border-left-color: var(--success-green);
}
.alert-danger {
background: #fef2f2;
color: #991b1b;
border-left-color: var(--danger-red);
}
.alert-warning {
background: #fffbeb;
color: #92400e;
border-left-color: var(--warning-orange);
}
/* Clean List Styling */
.list-group-item {
border: 1px solid var(--grey-200);
background: white;
transition: all 0.2s ease;
}
.list-group-item:hover {
background: var(--grey-50);
transform: translateX(4px);
}
.list-group-item-action {
color: var(--grey-700);
}
.list-group-item-action:hover {
color: var(--primary-blue);
}
/* Typography Improvements */
.text-muted {
color: var(--grey-500) !important;
}
code {
background: var(--grey-100);
color: var(--grey-800);
padding: 0.125rem 0.375rem;
border-radius: var(--radius-sm);
font-family: var(--font-mono);
font-size: 0.875em;
}
/* Utility Classes */
.rounded-modern {
border-radius: var(--radius-lg) !important;
}
.shadow-modern {
box-shadow: var(--shadow-md) !important;
}
.bg-gradient-primary {
background: linear-gradient(135deg, var(--primary-blue) 0%, var(--primary-purple) 100%) !important;
}
/* Print Styles */
@media print {
.navbar,
.btn,
.card-header {
display: none !important;
}
.card {
border: 1px solid #ccc !important;
box-shadow: none !important;
}
}

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,534 @@
/*
* 🤖 RADZEN TECH HOLOGRAPHIC THEME 🤖
* Dark Purple Metallic with Holographic Effects
* Pixel Perfect Robot/Tech Aesthetic
*/
:root {
/* Core Theme Colors */
--tech-primary: #8A2BE2; /* Blue Violet */
--tech-secondary: #9932CC; /* Dark Orchid */
--tech-accent: #DA70D6; /* Orchid */
--tech-highlight: #FF00FF; /* Magenta */
--tech-neon: #00FFFF; /* Cyan */
/* Dark Base Colors */
--tech-dark-base: #0D0014; /* Ultra Dark Purple */
--tech-dark-surface: #1A0A2E; /* Dark Purple */
--tech-dark-card: #16213E; /* Dark Blue Purple */
--tech-dark-accent: #0F3460; /* Deep Blue */
/* Metallic Colors */
--tech-metallic-silver: #C0C0C0;
--tech-metallic-gold: #FFD700;
--tech-metallic-copper: #B87333;
--tech-metallic-chrome: #E5E5E5;
/* Holographic Gradient */
--holographic-gradient: linear-gradient(45deg,
#FF0080 0%,
#7928CA 25%,
#0070F3 50%,
#00DFD8 75%,
#FF0080 100%);
/* Glow Effects */
--tech-glow-primary: 0 0 20px rgba(138, 43, 226, 0.8);
--tech-glow-secondary: 0 0 30px rgba(153, 50, 204, 0.6);
--tech-glow-accent: 0 0 15px rgba(218, 112, 214, 0.9);
--tech-glow-neon: 0 0 25px rgba(0, 255, 255, 0.7);
/* Animation Timings */
--tech-transition-fast: 0.15s cubic-bezier(0.4, 0, 0.2, 1);
--tech-transition-smooth: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
--tech-transition-glow: 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Holographic Animation Keyframes */
@keyframes holographic-shift {
0% { background-position: 0% 50%; }
25% { background-position: 100% 50%; }
50% { background-position: 100% 0%; }
75% { background-position: 0% 0%; }
100% { background-position: 0% 50%; }
}
@keyframes tech-pulse {
0%, 100% {
transform: scale(1);
box-shadow: var(--tech-glow-primary);
}
50% {
transform: scale(1.05);
box-shadow: var(--tech-glow-secondary);
}
}
@keyframes neon-flicker {
0%, 100% { opacity: 1; }
2% { opacity: 0.8; }
4% { opacity: 1; }
8% { opacity: 0.9; }
10% { opacity: 1; }
}
@keyframes data-stream {
0% { transform: translateY(-100%); opacity: 0; }
10% { opacity: 1; }
90% { opacity: 1; }
100% { transform: translateY(100vh); opacity: 0; }
}
/* Global Dark Theme Base */
body {
background: var(--tech-dark-base) !important;
background-image:
radial-gradient(circle at 25% 25%, rgba(138, 43, 226, 0.1) 0%, transparent 50%),
radial-gradient(circle at 75% 75%, rgba(153, 50, 204, 0.1) 0%, transparent 50%),
linear-gradient(45deg, transparent 45%, rgba(0, 255, 255, 0.03) 50%, transparent 55%);
color: var(--tech-metallic-chrome) !important;
font-family: 'Segoe UI', 'Roboto Mono', 'Courier New', monospace !important;
font-weight: 400;
line-height: 1.5;
min-height: 100vh;
}
/* Radzen Component Overrides */
.rz-card {
background: linear-gradient(135deg,
var(--tech-dark-surface) 0%,
var(--tech-dark-card) 100%) !important;
border: 1px solid rgba(138, 43, 226, 0.3) !important;
border-radius: 12px !important;
box-shadow:
0 8px 32px rgba(13, 0, 20, 0.8),
inset 0 1px 0 rgba(218, 112, 214, 0.2),
0 0 0 1px rgba(138, 43, 226, 0.1) !important;
color: var(--tech-metallic-chrome) !important;
transition: all var(--tech-transition-smooth) !important;
backdrop-filter: blur(20px) !important;
}
.rz-card:hover {
transform: translateY(-2px) !important;
box-shadow:
0 16px 64px rgba(13, 0, 20, 0.9),
inset 0 1px 0 rgba(218, 112, 214, 0.4),
var(--tech-glow-primary) !important;
border-color: var(--tech-primary) !important;
}
.rz-card-header {
background: linear-gradient(90deg,
rgba(138, 43, 226, 0.2) 0%,
rgba(153, 50, 204, 0.1) 100%) !important;
border-bottom: 1px solid rgba(218, 112, 214, 0.3) !important;
color: var(--tech-metallic-chrome) !important;
font-weight: 600 !important;
padding: 1rem 1.5rem !important;
position: relative !important;
}
.rz-card-header::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: var(--holographic-gradient);
background-size: 200% 100%;
animation: holographic-shift 3s ease-in-out infinite;
}
.rz-card-body {
padding: 1.5rem !important;
background: rgba(22, 33, 62, 0.3) !important;
}
/* Radzen Buttons */
.rz-button {
background: linear-gradient(135deg,
var(--tech-primary) 0%,
var(--tech-secondary) 100%) !important;
border: 1px solid var(--tech-accent) !important;
border-radius: 8px !important;
color: white !important;
font-weight: 600 !important;
text-transform: uppercase !important;
letter-spacing: 0.5px !important;
transition: all var(--tech-transition-smooth) !important;
position: relative !important;
overflow: hidden !important;
}
.rz-button::before {
content: '';
position: absolute;
top: -2px;
left: -2px;
right: -2px;
bottom: -2px;
background: var(--holographic-gradient);
background-size: 200% 200%;
border-radius: inherit;
z-index: -1;
opacity: 0;
transition: opacity var(--tech-transition-smooth);
animation: holographic-shift 2s ease-in-out infinite;
}
.rz-button:hover::before {
opacity: 1;
}
.rz-button:hover {
transform: translateY(-1px) !important;
/* box-shadow: var(--tech-glow-primary) !important; */
/* text-shadow: 0 0 10px rgba(255, 255, 255, 0.8) !important; */
}
.rz-button:active {
transform: scale(0.98) !important;
}
.rz-button-primary {
background: linear-gradient(135deg,
var(--tech-primary) 0%,
var(--tech-highlight) 100%) !important;
}
.rz-button-secondary {
background: linear-gradient(135deg,
var(--tech-dark-accent) 0%,
var(--tech-secondary) 100%) !important;
}
/* Radzen Data Grid */
.rz-datatable,
.rz-grid {
background: var(--tech-dark-surface) !important;
border: 1px solid rgba(138, 43, 226, 0.3) !important;
border-radius: 12px !important;
overflow: hidden !important;
}
.rz-datatable-header,
.rz-grid-header {
background: linear-gradient(90deg,
var(--tech-dark-card) 0%,
var(--tech-dark-accent) 100%) !important;
color: var(--tech-metallic-chrome) !important;
font-weight: 600 !important;
border-bottom: 2px solid var(--tech-primary) !important;
}
.rz-datatable-header th,
.rz-grid-header th {
border-right: 1px solid rgba(138, 43, 226, 0.2) !important;
padding: 1rem !important;
text-transform: uppercase !important;
letter-spacing: 0.5px !important;
font-size: 0.85rem !important;
}
.rz-datatable-data tr,
.rz-grid-table tr {
background: rgba(26, 10, 46, 0.5) !important;
border-bottom: 1px solid rgba(218, 112, 214, 0.1) !important;
transition: all var(--tech-transition-fast) !important;
}
.rz-datatable-data tr:hover,
.rz-grid-table tr:hover {
background: rgba(138, 43, 226, 0.1) !important;
box-shadow: inset 0 0 20px rgba(218, 112, 214, 0.1) !important;
}
.rz-datatable-data td,
.rz-grid-table td {
border-right: 1px solid rgba(138, 43, 226, 0.1) !important;
padding: 1rem !important;
color: var(--tech-metallic-chrome) !important;
}
/* Radzen Form Controls */
.rz-textbox,
.rz-dropdown,
.rz-multiselect,
.rz-textarea {
background: rgba(22, 33, 62, 0.8) !important;
border: 2px solid rgba(138, 43, 226, 0.3) !important;
border-radius: 8px !important;
color: var(--tech-metallic-chrome) !important;
padding: 0.75rem 1rem !important;
transition: all var(--tech-transition-smooth) !important;
backdrop-filter: blur(10px) !important;
}
.rz-textbox:focus,
.rz-dropdown:focus,
.rz-multiselect:focus,
.rz-textarea:focus {
border-color: var(--tech-primary) !important;
box-shadow:
var(--tech-glow-primary),
inset 0 0 20px rgba(138, 43, 226, 0.1) !important;
outline: none !important;
background: rgba(22, 33, 62, 1) !important;
}
.rz-textbox::placeholder,
.rz-dropdown::placeholder,
.rz-textarea::placeholder {
color: rgba(192, 192, 192, 0.6) !important;
font-style: italic !important;
}
/* Radzen Navigation */
.rz-navigation {
background: linear-gradient(180deg,
var(--tech-dark-surface) 0%,
var(--tech-dark-base) 100%) !important;
border-right: 1px solid rgba(138, 43, 226, 0.3) !important;
backdrop-filter: blur(20px) !important;
}
.rz-navigation-item {
color: var(--tech-metallic-chrome) !important;
padding: 0.75rem 1.5rem !important;
transition: all var(--tech-transition-fast) !important;
border-radius: 0 25px 25px 0 !important;
margin: 0.25rem 0 !important;
position: relative !important;
}
.rz-navigation-item:hover {
background: linear-gradient(90deg,
rgba(138, 43, 226, 0.2) 0%,
rgba(153, 50, 204, 0.1) 100%) !important;
color: var(--tech-accent) !important;
transform: translateX(5px) !important;
}
.rz-navigation-item.rz-state-active {
background: linear-gradient(90deg,
var(--tech-primary) 0%,
var(--tech-secondary) 100%) !important;
color: white !important;
box-shadow: var(--tech-glow-primary) !important;
}
.rz-navigation-item.rz-state-active::after {
content: '';
position: absolute;
right: -1px;
top: 0;
bottom: 0;
width: 3px;
background: var(--tech-neon);
box-shadow: var(--tech-glow-neon);
animation: neon-flicker 2s infinite;
}
/* Radzen Panels */
.rz-panel {
background: var(--tech-dark-surface) !important;
border: 1px solid rgba(138, 43, 226, 0.3) !important;
border-radius: 12px !important;
box-shadow: 0 8px 32px rgba(13, 0, 20, 0.6) !important;
backdrop-filter: blur(20px) !important;
}
.rz-panel-header {
background: linear-gradient(90deg,
var(--tech-dark-card) 0%,
var(--tech-dark-accent) 100%) !important;
border-bottom: 1px solid rgba(218, 112, 214, 0.3) !important;
color: var(--tech-metallic-chrome) !important;
font-weight: 600 !important;
padding: 1rem 1.5rem !important;
position: relative !important;
}
/* Radzen Dialogs */
.rz-dialog {
background: linear-gradient(135deg,
var(--tech-dark-surface) 0%,
var(--tech-dark-card) 100%) !important;
border: 2px solid var(--tech-primary) !important;
border-radius: 16px !important;
box-shadow:
0 20px 60px rgba(13, 0, 20, 0.9),
var(--tech-glow-primary) !important;
backdrop-filter: blur(30px) !important;
}
.rz-dialog-header {
background: linear-gradient(90deg,
rgba(138, 43, 226, 0.3) 0%,
rgba(153, 50, 204, 0.2) 100%) !important;
border-bottom: 1px solid rgba(218, 112, 214, 0.4) !important;
color: var(--tech-metallic-chrome) !important;
font-weight: 700 !important;
text-transform: uppercase !important;
letter-spacing: 1px !important;
}
/* Radzen Notifications */
.rz-notification {
background: linear-gradient(135deg,
var(--tech-dark-surface) 0%,
var(--tech-dark-card) 100%) !important;
border: 1px solid var(--tech-primary) !important;
border-radius: 12px !important;
color: var(--tech-metallic-chrome) !important;
box-shadow:
0 8px 32px rgba(13, 0, 20, 0.8),
var(--tech-glow-primary) !important;
backdrop-filter: blur(20px) !important;
}
.rz-notification-success {
border-color: var(--tech-neon) !important;
box-shadow: var(--tech-glow-neon) !important;
}
.rz-notification-error {
border-color: var(--tech-highlight) !important;
box-shadow: 0 0 25px rgba(255, 0, 255, 0.7) !important;
}
/* Holographic Border Effect */
.tech-holographic-border {
position: relative;
border: 2px solid transparent !important;
background: linear-gradient(var(--tech-dark-surface), var(--tech-dark-surface)) padding-box,
var(--holographic-gradient) border-box !important;
background-size: 200% 200%;
animation: holographic-shift 3s ease-in-out infinite;
}
/* Tech Grid Pattern Overlay */
.tech-grid-overlay {
background-image:
linear-gradient(rgba(138, 43, 226, 0.1) 1px, transparent 1px),
linear-gradient(90deg, rgba(138, 43, 226, 0.1) 1px, transparent 1px);
background-size: 20px 20px;
}
/* Scrollbar Styling */
::-webkit-scrollbar {
width: 12px;
background: var(--tech-dark-base);
}
::-webkit-scrollbar-track {
background: rgba(26, 10, 46, 0.5);
border-radius: 6px;
}
::-webkit-scrollbar-thumb {
background: linear-gradient(180deg,
var(--tech-primary) 0%,
var(--tech-secondary) 100%);
border-radius: 6px;
border: 1px solid rgba(218, 112, 214, 0.3);
}
::-webkit-scrollbar-thumb:hover {
background: linear-gradient(180deg,
var(--tech-accent) 0%,
var(--tech-highlight) 100%);
box-shadow: var(--tech-glow-accent);
}
/* Loading Spinner Override */
.rz-spinner {
border: 3px solid rgba(138, 43, 226, 0.3);
border-top: 3px solid var(--tech-primary);
box-shadow: var(--tech-glow-primary);
}
/* Mobile Responsive Adjustments */
@media (max-width: 768px) {
.rz-card {
margin: 0.5rem !important;
border-radius: 8px !important;
}
.rz-button {
padding: 0.75rem 1.5rem !important;
font-size: 0.9rem !important;
}
.rz-navigation-item {
padding: 1rem !important;
border-radius: 0 20px 20px 0 !important;
}
}
/* Print Styles */
@media print {
body {
background: white !important;
color: black !important;
}
.rz-card {
background: white !important;
border: 1px solid #ccc !important;
box-shadow: none !important;
}
}
/* High Contrast Mode */
@media (prefers-contrast: high) {
:root {
--tech-primary: #FF00FF;
--tech-secondary: #00FFFF;
--tech-accent: #FFFF00;
}
}
/* Reduced Motion */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* Dark Theme Class for Radzen */
.dark-theme {
--rz-primary: var(--tech-primary);
--rz-secondary: var(--tech-secondary);
--rz-success: var(--tech-neon);
--rz-info: var(--tech-accent);
--rz-warning: var(--tech-metallic-gold);
--rz-danger: var(--tech-highlight);
--rz-dark: var(--tech-dark-base);
--rz-light: var(--tech-metallic-chrome);
}
/* Tech Enhancement Classes */
.tech-glow {
box-shadow: var(--tech-glow-primary) !important;
animation: tech-pulse 2s infinite !important;
}
.tech-holographic {
background: var(--holographic-gradient) !important;
background-size: 200% 200% !important;
animation: holographic-shift 3s ease-in-out infinite !important;
-webkit-background-clip: text !important;
background-clip: text !important;
color: transparent !important;
}
.tech-neon {
color: var(--tech-neon) !important;
text-shadow: var(--tech-glow-neon) !important;
animation: neon-flicker 2s infinite !important;
}

View File

@@ -0,0 +1 @@
AAABAAEAEBAQAAEABAAoAQAAFgAAACgAAAAQAAAAIAAAAAEABAAAAAAAgAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Some files were not shown because too many files have changed in this diff Show More