Initial commit: Telegram Management System
Some checks failed
Deploy / deploy (push) Has been cancelled
Some checks failed
Deploy / deploy (push) Has been cancelled
Full-stack web application for Telegram management - Frontend: Vue 3 + Vben Admin - Backend: NestJS - Features: User management, group broadcast, statistics 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
51
marketing-agent/frontend/public/clear-and-test.html
Normal file
51
marketing-agent/frontend/public/clear-and-test.html
Normal file
@@ -0,0 +1,51 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Clear and Test</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Clear Storage and Test Login</h1>
|
||||
|
||||
<button onclick="clearStorage()">Clear All Storage</button>
|
||||
<button onclick="testAuth()">Test Current Auth</button>
|
||||
<button onclick="goToLogin()">Go to Login Page</button>
|
||||
<button onclick="goToHome()">Go to Home (Protected)</button>
|
||||
|
||||
<div id="result" style="margin-top: 20px; padding: 10px; border: 1px solid #ddd;"></div>
|
||||
|
||||
<script>
|
||||
function clearStorage() {
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
document.getElementById('result').innerHTML = 'All storage cleared!';
|
||||
}
|
||||
|
||||
function testAuth() {
|
||||
const token = localStorage.getItem('token');
|
||||
const result = document.getElementById('result');
|
||||
|
||||
if (token) {
|
||||
result.innerHTML = `
|
||||
<p style="color: green;">Authenticated!</p>
|
||||
<p>Token: ${token.substring(0, 50)}...</p>
|
||||
`;
|
||||
} else {
|
||||
result.innerHTML = '<p style="color: red;">Not authenticated</p>';
|
||||
}
|
||||
}
|
||||
|
||||
function goToLogin() {
|
||||
window.location.href = '/login';
|
||||
}
|
||||
|
||||
function goToHome() {
|
||||
window.location.href = '/';
|
||||
}
|
||||
|
||||
// Show current status on load
|
||||
window.onload = testAuth;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
97
marketing-agent/frontend/public/login-test.html
Normal file
97
marketing-agent/frontend/public/login-test.html
Normal file
@@ -0,0 +1,97 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login Test</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; padding: 20px; }
|
||||
input { margin: 5px 0; padding: 5px; width: 200px; }
|
||||
button { margin: 10px 0; padding: 10px 20px; background: #4CAF50; color: white; border: none; cursor: pointer; }
|
||||
button:hover { background: #45a049; }
|
||||
.result { margin-top: 20px; padding: 10px; border: 1px solid #ddd; }
|
||||
.error { color: red; }
|
||||
.success { color: green; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Login Test</h1>
|
||||
|
||||
<div>
|
||||
<input type="text" id="username" value="admin" placeholder="Username"><br>
|
||||
<input type="password" id="password" value="admin123456" placeholder="Password"><br>
|
||||
<button onclick="login()">Login</button>
|
||||
</div>
|
||||
|
||||
<div id="result" class="result"></div>
|
||||
|
||||
<script>
|
||||
async function login() {
|
||||
const username = document.getElementById('username').value;
|
||||
const password = document.getElementById('password').value;
|
||||
const resultDiv = document.getElementById('result');
|
||||
|
||||
try {
|
||||
// Login
|
||||
const response = await axios.post('/api/v1/auth/login', {
|
||||
username: username,
|
||||
password: password
|
||||
});
|
||||
|
||||
console.log('Login response:', response.data);
|
||||
|
||||
if (response.data.success) {
|
||||
const token = response.data.data.accessToken;
|
||||
const user = response.data.data.user;
|
||||
|
||||
resultDiv.innerHTML = `
|
||||
<div class="success">
|
||||
<h3>Login Successful!</h3>
|
||||
<p>User: ${user.username} (${user.role})</p>
|
||||
<p>Token stored in localStorage</p>
|
||||
<button onclick="testDashboard()">Test Dashboard Access</button>
|
||||
<button onclick="goToApp()">Go to Application</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Store token
|
||||
localStorage.setItem('token', token);
|
||||
localStorage.setItem('refreshToken', response.data.data.refreshToken);
|
||||
} else {
|
||||
resultDiv.innerHTML = `<div class="error">Login failed: ${response.data.error}</div>`;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Login error:', error);
|
||||
resultDiv.innerHTML = `<div class="error">Error: ${error.message}</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
async function testDashboard() {
|
||||
const token = localStorage.getItem('token');
|
||||
if (!token) {
|
||||
alert('No token found!');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.get('/api/v1/analytics/dashboard', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Dashboard data:', response.data);
|
||||
alert('Dashboard access successful! Check console for data.');
|
||||
} catch (error) {
|
||||
console.error('Dashboard error:', error);
|
||||
alert('Dashboard error: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
function goToApp() {
|
||||
window.location.href = '/';
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
246
marketing-agent/frontend/public/service-worker.js
Normal file
246
marketing-agent/frontend/public/service-worker.js
Normal file
@@ -0,0 +1,246 @@
|
||||
// Service Worker for offline support and performance optimization
|
||||
|
||||
const CACHE_NAME = 'marketing-agent-v1'
|
||||
const STATIC_CACHE_NAME = 'marketing-agent-static-v1'
|
||||
const DYNAMIC_CACHE_NAME = 'marketing-agent-dynamic-v1'
|
||||
const API_CACHE_NAME = 'marketing-agent-api-v1'
|
||||
|
||||
// Files to cache immediately
|
||||
const STATIC_ASSETS = [
|
||||
'/',
|
||||
'/index.html',
|
||||
'/manifest.json',
|
||||
'/images/logo.png',
|
||||
'/images/placeholder.png'
|
||||
]
|
||||
|
||||
// API endpoints to cache
|
||||
const CACHEABLE_API_PATTERNS = [
|
||||
/\/api\/v1\/users\/profile$/,
|
||||
/\/api\/v1\/campaigns\/templates$/,
|
||||
/\/api\/v1\/settings$/
|
||||
]
|
||||
|
||||
// Install event - cache static assets
|
||||
self.addEventListener('install', (event) => {
|
||||
event.waitUntil(
|
||||
caches.open(STATIC_CACHE_NAME).then((cache) => {
|
||||
return cache.addAll(STATIC_ASSETS)
|
||||
})
|
||||
)
|
||||
self.skipWaiting()
|
||||
})
|
||||
|
||||
// Activate event - clean up old caches
|
||||
self.addEventListener('activate', (event) => {
|
||||
event.waitUntil(
|
||||
caches.keys().then((cacheNames) => {
|
||||
return Promise.all(
|
||||
cacheNames
|
||||
.filter((cacheName) => {
|
||||
return cacheName.startsWith('marketing-agent-') &&
|
||||
cacheName !== CACHE_NAME &&
|
||||
cacheName !== STATIC_CACHE_NAME &&
|
||||
cacheName !== DYNAMIC_CACHE_NAME &&
|
||||
cacheName !== API_CACHE_NAME
|
||||
})
|
||||
.map((cacheName) => caches.delete(cacheName))
|
||||
)
|
||||
})
|
||||
)
|
||||
self.clients.claim()
|
||||
})
|
||||
|
||||
// Fetch event - implement caching strategies
|
||||
self.addEventListener('fetch', (event) => {
|
||||
const { request } = event
|
||||
const url = new URL(request.url)
|
||||
|
||||
// Skip non-GET requests
|
||||
if (request.method !== 'GET') {
|
||||
return
|
||||
}
|
||||
|
||||
// Handle API requests
|
||||
if (url.pathname.startsWith('/api/')) {
|
||||
event.respondWith(handleApiRequest(request))
|
||||
return
|
||||
}
|
||||
|
||||
// Handle static assets
|
||||
if (isStaticAsset(url.pathname)) {
|
||||
event.respondWith(handleStaticAsset(request))
|
||||
return
|
||||
}
|
||||
|
||||
// Handle dynamic content
|
||||
event.respondWith(handleDynamicContent(request))
|
||||
})
|
||||
|
||||
// Cache-first strategy for static assets
|
||||
async function handleStaticAsset(request) {
|
||||
const cache = await caches.open(STATIC_CACHE_NAME)
|
||||
const cached = await cache.match(request)
|
||||
|
||||
if (cached) {
|
||||
return cached
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(request)
|
||||
if (response.ok) {
|
||||
cache.put(request, response.clone())
|
||||
}
|
||||
return response
|
||||
} catch (error) {
|
||||
return new Response('Offline - Asset not available', {
|
||||
status: 503,
|
||||
statusText: 'Service Unavailable'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Network-first strategy for API requests with fallback
|
||||
async function handleApiRequest(request) {
|
||||
const cache = await caches.open(API_CACHE_NAME)
|
||||
|
||||
try {
|
||||
const response = await fetch(request)
|
||||
|
||||
// Cache successful responses for cacheable endpoints
|
||||
if (response.ok && isCacheableApi(request.url)) {
|
||||
cache.put(request, response.clone())
|
||||
}
|
||||
|
||||
return response
|
||||
} catch (error) {
|
||||
// Try cache on network failure
|
||||
const cached = await cache.match(request)
|
||||
if (cached) {
|
||||
// Add header to indicate cached response
|
||||
const headers = new Headers(cached.headers)
|
||||
headers.set('X-From-Cache', 'true')
|
||||
|
||||
return new Response(cached.body, {
|
||||
status: cached.status,
|
||||
statusText: cached.statusText,
|
||||
headers
|
||||
})
|
||||
}
|
||||
|
||||
// Return offline response
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
error: 'Network unavailable',
|
||||
offline: true
|
||||
}),
|
||||
{
|
||||
status: 503,
|
||||
statusText: 'Service Unavailable',
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Stale-while-revalidate strategy for dynamic content
|
||||
async function handleDynamicContent(request) {
|
||||
const cache = await caches.open(DYNAMIC_CACHE_NAME)
|
||||
const cached = await cache.match(request)
|
||||
|
||||
const fetchPromise = fetch(request).then((response) => {
|
||||
if (response.ok) {
|
||||
cache.put(request, response.clone())
|
||||
}
|
||||
return response
|
||||
})
|
||||
|
||||
return cached || fetchPromise
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
function isStaticAsset(pathname) {
|
||||
return /\.(js|css|png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)$/.test(pathname)
|
||||
}
|
||||
|
||||
function isCacheableApi(url) {
|
||||
return CACHEABLE_API_PATTERNS.some(pattern => pattern.test(url))
|
||||
}
|
||||
|
||||
// Background sync for offline actions
|
||||
self.addEventListener('sync', (event) => {
|
||||
if (event.tag === 'sync-campaigns') {
|
||||
event.waitUntil(syncCampaigns())
|
||||
}
|
||||
})
|
||||
|
||||
async function syncCampaigns() {
|
||||
const cache = await caches.open('offline-campaigns')
|
||||
const requests = await cache.keys()
|
||||
|
||||
for (const request of requests) {
|
||||
try {
|
||||
const response = await fetch(request)
|
||||
if (response.ok) {
|
||||
await cache.delete(request)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to sync:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Push notifications
|
||||
self.addEventListener('push', (event) => {
|
||||
const options = {
|
||||
body: event.data ? event.data.text() : 'New notification',
|
||||
icon: '/images/logo.png',
|
||||
badge: '/images/badge.png',
|
||||
vibrate: [100, 50, 100],
|
||||
data: {
|
||||
dateOfArrival: Date.now(),
|
||||
primaryKey: 1
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
action: 'view',
|
||||
title: 'View',
|
||||
icon: '/images/checkmark.png'
|
||||
},
|
||||
{
|
||||
action: 'close',
|
||||
title: 'Close',
|
||||
icon: '/images/xmark.png'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
event.waitUntil(
|
||||
self.registration.showNotification('Marketing Agent', options)
|
||||
)
|
||||
})
|
||||
|
||||
self.addEventListener('notificationclick', (event) => {
|
||||
event.notification.close()
|
||||
|
||||
if (event.action === 'view') {
|
||||
event.waitUntil(
|
||||
clients.openWindow('/')
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
// Message handling for cache control
|
||||
self.addEventListener('message', (event) => {
|
||||
if (event.data.type === 'SKIP_WAITING') {
|
||||
self.skipWaiting()
|
||||
} else if (event.data.type === 'CLEAR_CACHE') {
|
||||
event.waitUntil(
|
||||
caches.keys().then((cacheNames) => {
|
||||
return Promise.all(
|
||||
cacheNames.map((cacheName) => caches.delete(cacheName))
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
})
|
||||
59
marketing-agent/frontend/public/test.html
Normal file
59
marketing-agent/frontend/public/test.html
Normal file
@@ -0,0 +1,59 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login Test</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Login Test</h1>
|
||||
<button onclick="testLogin()">Test Login</button>
|
||||
<div id="result"></div>
|
||||
|
||||
<script>
|
||||
async function testLogin() {
|
||||
const resultDiv = document.getElementById('result');
|
||||
resultDiv.innerHTML = 'Testing login...';
|
||||
|
||||
try {
|
||||
const response = await axios.post('/api/v1/auth/login', {
|
||||
username: 'admin',
|
||||
password: 'admin123456'
|
||||
});
|
||||
|
||||
console.log('Response:', response.data);
|
||||
|
||||
if (response.data.success) {
|
||||
resultDiv.innerHTML = `
|
||||
<h3>Login Success!</h3>
|
||||
<p>Token: ${response.data.data.accessToken.substring(0, 50)}...</p>
|
||||
<p>User: ${response.data.data.user.username} (${response.data.data.user.role})</p>
|
||||
<button onclick="testDashboard('${response.data.data.accessToken}')">Test Dashboard API</button>
|
||||
`;
|
||||
} else {
|
||||
resultDiv.innerHTML = `<h3>Login Failed: ${response.data.error}</h3>`;
|
||||
}
|
||||
} catch (error) {
|
||||
resultDiv.innerHTML = `<h3>Error: ${error.message}</h3>`;
|
||||
console.error('Login error:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function testDashboard(token) {
|
||||
try {
|
||||
const response = await axios.get('/api/v1/analytics/dashboard', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
console.log('Dashboard response:', response.data);
|
||||
alert('Dashboard API works! Check console for data.');
|
||||
} catch (error) {
|
||||
console.error('Dashboard error:', error);
|
||||
alert('Dashboard API error: ' + error.message);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
72
marketing-agent/frontend/public/verify-fix.html
Normal file
72
marketing-agent/frontend/public/verify-fix.html
Normal file
@@ -0,0 +1,72 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Verify Fix</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; padding: 20px; }
|
||||
.step { margin: 20px 0; padding: 10px; border: 1px solid #ddd; }
|
||||
.success { color: green; }
|
||||
.error { color: red; }
|
||||
button { padding: 10px 20px; margin: 5px; cursor: pointer; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Verify Login Fix</h1>
|
||||
|
||||
<div class="step">
|
||||
<h3>Step 1: Clear Everything</h3>
|
||||
<button onclick="clearAll()">Clear Cache & Reload</button>
|
||||
</div>
|
||||
|
||||
<div class="step">
|
||||
<h3>Step 2: Test Login</h3>
|
||||
<button onclick="testLogin()">Login as Admin</button>
|
||||
<div id="loginResult"></div>
|
||||
</div>
|
||||
|
||||
<div class="step">
|
||||
<h3>Step 3: Navigate to Dashboard</h3>
|
||||
<button onclick="goToDashboard()">Go to Dashboard</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function clearAll() {
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
// Force reload to clear any cached modules
|
||||
location.href = location.href + '?t=' + Date.now();
|
||||
}
|
||||
|
||||
async function testLogin() {
|
||||
const resultDiv = document.getElementById('loginResult');
|
||||
try {
|
||||
const response = await fetch('/api/v1/auth/login', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
username: 'admin',
|
||||
password: 'admin123456'
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
if (data.success) {
|
||||
localStorage.setItem('token', data.data.accessToken);
|
||||
localStorage.setItem('refreshToken', data.data.refreshToken);
|
||||
resultDiv.innerHTML = '<p class="success">✓ Login successful! Token saved.</p>';
|
||||
} else {
|
||||
resultDiv.innerHTML = '<p class="error">✗ Login failed: ' + data.error + '</p>';
|
||||
}
|
||||
} catch (error) {
|
||||
resultDiv.innerHTML = '<p class="error">✗ Error: ' + error.message + '</p>';
|
||||
}
|
||||
}
|
||||
|
||||
function goToDashboard() {
|
||||
window.location.href = '/';
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user