envs
This commit is contained in:
7
.github/workflows/security.yml
vendored
7
.github/workflows/security.yml
vendored
@@ -34,13 +34,7 @@ jobs:
|
||||
- name: Run yarn audit
|
||||
run: yarn audit --level high
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: javascript
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
||||
docker-security:
|
||||
name: Docker Security Scan
|
||||
@@ -59,4 +53,3 @@ jobs:
|
||||
image-ref: 'quixotic:scan'
|
||||
format: 'sarif'
|
||||
output: 'trivy-results.sarif'
|
||||
|
||||
|
||||
Binary file not shown.
31
.serena/memories/layout_issue_analysis.md
Normal file
31
.serena/memories/layout_issue_analysis.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Layout Issue Analysis
|
||||
|
||||
## Problem Description
|
||||
User reports that the application layout is broken:
|
||||
1. "Nothing found" notification shows always
|
||||
2. Search field that was at bottom is now at top
|
||||
|
||||
## Current Structure Analysis
|
||||
Based on code review of public/index.html and public/script.ts:
|
||||
|
||||
### HTML Structure (Current):
|
||||
1. Search form (input + button) - at TOP
|
||||
2. Welcome placeholder - initially visible
|
||||
3. Loading spinner - hidden by default
|
||||
4. Results list - hidden by default
|
||||
5. No results placeholder - hidden by default
|
||||
|
||||
### JavaScript Logic Issues Found:
|
||||
1. **No Results always showing**: In `showNoResults()` method, the noResults element is made visible with `classList.remove('tg-hidden')` but there's no logic to hide it when new search starts or results are found.
|
||||
|
||||
2. **Search field position**: The search form is structurally at the top in HTML, which matches user complaint.
|
||||
|
||||
### Display Logic Flow:
|
||||
- `showLoading()`: Hides welcome, shows loading, hides results, hides noResults
|
||||
- `displayResults()`: Calls `hideLoading()`, shows results if any, otherwise calls `showNoResults()`
|
||||
- `showNoResults()`: Calls `hideLoading()`, hides results, SHOWS noResults (but doesn't hide welcome)
|
||||
|
||||
## Root Causes:
|
||||
1. **NoResults visibility issue**: The noResults element is never hidden when starting a new search
|
||||
2. **Welcome placeholder management**: Not properly hidden when showing noResults
|
||||
3. **Search position**: Structurally at top, needs to be moved to bottom if that was the intended design
|
||||
18
.serena/memories/telegram_token_fix.md
Normal file
18
.serena/memories/telegram_token_fix.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Telegram Bot Token Fix
|
||||
|
||||
## Problem
|
||||
Docker container was showing "⚠️ TELEGRAM_BOT_TOKEN not found or invalid - bot will not start" error despite the token being present in `.env.docker` file.
|
||||
|
||||
## Root Cause
|
||||
The `docker-compose.yml` file was missing the `env_file` configuration to load environment variables from `.env.docker`.
|
||||
|
||||
## Solution
|
||||
Added `env_file: - .env.docker` at the top level of docker-compose.yml to ensure all services load environment variables from the .env.docker file.
|
||||
|
||||
## Files Modified
|
||||
- `docker-compose.yml`: Added env_file configuration
|
||||
|
||||
## Token Details
|
||||
- Token is present in `.env.docker`: `8262100335:AAFUBycadhKwV4oWPtF_Uq3c_R95SfWUElM`
|
||||
- WEB_APP_URL is configured: `https://quixy.uk`
|
||||
- Environment variables are properly referenced in services
|
||||
@@ -4,6 +4,8 @@ services:
|
||||
image: traefik:v3.0
|
||||
container_name: quixotic-traefik
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- .env.docker
|
||||
command:
|
||||
- --api.dashboard=true
|
||||
- --api.insecure=false
|
||||
@@ -37,6 +39,8 @@ services:
|
||||
image: postgres:15-alpine
|
||||
container_name: quixotic-postgres
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- .env.docker
|
||||
environment:
|
||||
POSTGRES_DB: ${POSTGRES_DB:-quixotic}
|
||||
POSTGRES_USER: ${POSTGRES_USER:-quixotic}
|
||||
@@ -59,10 +63,13 @@ services:
|
||||
dockerfile: Dockerfile
|
||||
container_name: quixotic-app
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- .env.docker
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- PORT=3000
|
||||
- DATABASE_URL=postgresql://${POSTGRES_USER:-quixotic}:${POSTGRES_PASSWORD:-quixotic123}@postgres:5432/${POSTGRES_DB:-quixotic}
|
||||
- DATABASE_SSL=false
|
||||
volumes:
|
||||
- downloads:/app/downloads
|
||||
labels:
|
||||
|
||||
@@ -12,34 +12,28 @@
|
||||
</head>
|
||||
<body>
|
||||
<div class="tg-root">
|
||||
</div>
|
||||
|
||||
<div class="tg-content">
|
||||
<div class="tg-placeholder" id="welcomePlaceholder">
|
||||
<div class="tg-placeholder__icon">🎵</div>
|
||||
<div class="tg-placeholder__title">Найти музыку</div>
|
||||
<div class="tg-placeholder__description">Введите название песни или исполнителя для поиска на YouTube</div>
|
||||
<div class="tg-placeholder__description">Введите название песни или исполнителя для поиска на SoundCloud</div>
|
||||
</div>
|
||||
|
||||
<button class="tg-button tg-button--primary tg-button--large" id="searchBtn">
|
||||
<span class="tg-button__text">Найти</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="tg-spinner" id="loading">
|
||||
<div class="tg-spinner tg-hidden" id="loading">
|
||||
<div class="tg-spinner__icon"></div>
|
||||
<div class="tg-spinner__text">Поиск музыки...</div>
|
||||
</div>
|
||||
|
||||
<div class="tg-list" id="results">
|
||||
<div class="tg-list tg-hidden" id="results">
|
||||
<!-- Search results will appear here -->
|
||||
</div>
|
||||
|
||||
<div class="tg-placeholder tg-placeholder--secondary" id="noResults">
|
||||
<div class="tg-placeholder tg-placeholder--secondary tg-hidden" id="noResults">
|
||||
<div class="tg-placeholder__icon">🔍</div>
|
||||
<div class="tg-placeholder__title">Ничего не найдено</div>
|
||||
<div class="tg-placeholder__description">Попробуйте изменить поисковый запрос</div>
|
||||
</div>
|
||||
|
||||
<div class="tg-form">
|
||||
<div class="tg-input-wrapper">
|
||||
<input type="text"
|
||||
@@ -58,4 +52,4 @@
|
||||
|
||||
<script src="dist/script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -110,6 +110,7 @@ class QuixoticApp {
|
||||
this.welcomePlaceholder.classList.add('tg-hidden');
|
||||
this.loading.classList.remove('tg-hidden');
|
||||
this.loading.classList.add('tg-spinner--visible');
|
||||
this.results.classList.add('tg-hidden');
|
||||
this.results.classList.remove('tg-list--visible');
|
||||
this.noResults.classList.add('tg-hidden');
|
||||
this.searchBtn.disabled = true;
|
||||
@@ -147,12 +148,14 @@ class QuixoticApp {
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
this.results.classList.remove('tg-hidden');
|
||||
this.results.classList.add('tg-list--visible');
|
||||
this.noResults.classList.add('tg-hidden');
|
||||
}
|
||||
|
||||
private showNoResults(): void {
|
||||
this.hideLoading();
|
||||
this.results.classList.add('tg-hidden');
|
||||
this.results.classList.remove('tg-list--visible');
|
||||
this.noResults.classList.remove('tg-hidden');
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ body {
|
||||
.tg-content {
|
||||
flex: 1;
|
||||
padding: var(--tg-spacing-lg);
|
||||
padding-bottom: 140px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--tg-spacing-xl);
|
||||
@@ -74,9 +75,17 @@ body {
|
||||
|
||||
/* Form components */
|
||||
.tg-form {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--tg-spacing-md);
|
||||
padding: var(--tg-spacing-lg);
|
||||
background: var(--tg-color-bg);
|
||||
border-top: 1px solid var(--tg-color-secondary-bg);
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.tg-input-wrapper {
|
||||
|
||||
24
src/bot.ts
24
src/bot.ts
@@ -84,7 +84,7 @@ export class QuixoticBot {
|
||||
|
||||
await this.bot.sendMessage(chatId,
|
||||
'🎵 Добро пожаловать в Quixotic!\n\n' +
|
||||
'Найди любую песню на YouTube и получи MP3 файл прямо в чат.\n\n' +
|
||||
'Найди любую песню на SoundCloud и получи MP3 файл прямо в чат.\n\n' +
|
||||
'Нажми кнопку ниже, чтобы начать поиск:',
|
||||
{ reply_markup: keyboard }
|
||||
);
|
||||
@@ -98,7 +98,7 @@ export class QuixoticBot {
|
||||
this.bot.onText(/\/help/, async (msg: Message) => {
|
||||
const chatId = msg.chat.id;
|
||||
|
||||
const helpText = `🎵 *Quixotic - YouTube to MP3*
|
||||
const helpText = `🎵 *Quixotic - SoundCloud to MP3*
|
||||
|
||||
*Как пользоваться:*
|
||||
1️⃣ Нажми кнопку "Открыть Quixotic"
|
||||
@@ -112,8 +112,8 @@ export class QuixoticBot {
|
||||
/history - История поиска
|
||||
|
||||
*Возможности:*
|
||||
✅ Поиск по YouTube
|
||||
✅ Высокое качество MP3 (128kbps)
|
||||
✅ Поиск по SoundCloud
|
||||
✅ Высокое качество MP3 (192kbps)
|
||||
✅ Быстрая конвертация
|
||||
✅ История поиска`;
|
||||
|
||||
@@ -218,19 +218,7 @@ export class QuixoticBot {
|
||||
}
|
||||
|
||||
private async getSearchHistory(userId: number): Promise<SearchResult[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.db['db'].all(
|
||||
`SELECT query, created_at FROM search_history
|
||||
WHERE user_id = ?
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 10`,
|
||||
[userId],
|
||||
(err: Error | null, rows: SearchResult[]) => {
|
||||
if (err) reject(err);
|
||||
else resolve(rows || []);
|
||||
}
|
||||
);
|
||||
});
|
||||
return this.db.getSearchHistory(userId);
|
||||
}
|
||||
|
||||
private async sendAudioFile(chatId: number, audioUrl: string, title: string): Promise<void> {
|
||||
@@ -272,4 +260,4 @@ if (require.main === module) {
|
||||
}
|
||||
|
||||
new QuixoticBot(token, webAppUrl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Pool, QueryResult } from 'pg';
|
||||
import { Pool } from 'pg';
|
||||
|
||||
interface TelegramUser {
|
||||
id: number;
|
||||
@@ -52,7 +52,7 @@ export class Database {
|
||||
await this.pool.query(`CREATE TABLE IF NOT EXISTS downloads (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INTEGER REFERENCES users(id),
|
||||
youtube_id TEXT NOT NULL,
|
||||
track_id TEXT NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
file_path TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
@@ -85,10 +85,10 @@ export class Database {
|
||||
return result.rows[0].id;
|
||||
}
|
||||
|
||||
async addDownload(userId: number, youtubeId: string, title: string, filePath: string): Promise<number> {
|
||||
async addDownload(userId: number, trackId: string, title: string, filePath: string): Promise<number> {
|
||||
const result = await this.pool.query(
|
||||
'INSERT INTO downloads (user_id, youtube_id, title, file_path) VALUES ($1, $2, $3, $4) RETURNING id',
|
||||
[userId, youtubeId, title, filePath]
|
||||
'INSERT INTO downloads (user_id, track_id, title, file_path) VALUES ($1, $2, $3, $4) RETURNING id',
|
||||
[userId, trackId, title, filePath]
|
||||
);
|
||||
return result.rows[0].id;
|
||||
}
|
||||
@@ -101,7 +101,15 @@ export class Database {
|
||||
return result.rows[0] || undefined;
|
||||
}
|
||||
|
||||
async getSearchHistory(userId: number, limit: number = 10): Promise<{query: string, created_at: string}[]> {
|
||||
const result = await this.pool.query(
|
||||
'SELECT query, created_at FROM search_history WHERE user_id = $1 ORDER BY created_at DESC LIMIT $2',
|
||||
[userId, limit]
|
||||
);
|
||||
return result.rows;
|
||||
}
|
||||
|
||||
async close(): Promise<void> {
|
||||
await this.pool.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,8 +198,8 @@ app.get('/health', (req: Request, res: Response) => {
|
||||
});
|
||||
|
||||
// Error handler
|
||||
app.use((err: Error, req: Request, res: Response, next: any) => {
|
||||
console.error(err.stack);
|
||||
app.use((_err: Error, _req: Request, res: Response, _next: any) => {
|
||||
console.error(_err.stack);
|
||||
res.status(500).json({ error: 'Something went wrong!' });
|
||||
});
|
||||
|
||||
@@ -238,11 +238,16 @@ app.listen(port, () => {
|
||||
const botToken = process.env.TELEGRAM_BOT_TOKEN;
|
||||
const webAppUrl = process.env.WEB_APP_URL || `http://localhost:${port}`;
|
||||
|
||||
if (botToken) {
|
||||
const bot = new QuixoticBot(botToken, webAppUrl);
|
||||
console.log('🤖 Telegram bot started');
|
||||
if (botToken && botToken.length > 10) {
|
||||
try {
|
||||
new QuixoticBot(botToken, webAppUrl);
|
||||
console.log('🤖 Telegram bot started');
|
||||
} catch (error: any) {
|
||||
console.error('❌ Bot initialization failed:', error.message);
|
||||
console.warn('⚠️ Bot disabled due to error');
|
||||
}
|
||||
} else {
|
||||
console.warn('⚠️ TELEGRAM_BOT_TOKEN not found - bot will not start');
|
||||
console.warn('⚠️ TELEGRAM_BOT_TOKEN not found or invalid - bot will not start');
|
||||
}
|
||||
|
||||
export default app;
|
||||
export default app;
|
||||
|
||||
Reference in New Issue
Block a user