DocUA commited on
Commit
21fefa5
·
1 Parent(s): 467c2f8

Альтернативний варіант

Browse files
Files changed (9) hide show
  1. Dockerfile +11 -12
  2. app.py +122 -85
  3. requirements.txt +2 -1
  4. start.sh +16 -8
  5. static/style.css +62 -0
  6. templates/error.html +23 -0
  7. templates/index.html +133 -0
  8. templates/result.html +77 -0
  9. templates/table.html +140 -0
Dockerfile CHANGED
@@ -12,25 +12,24 @@ RUN mkdir -p /docker-entrypoint-initdb.d
12
  # Копіювання SQL файлу ініціалізації
13
  COPY init.sql /docker-entrypoint-initdb.d/
14
 
15
- # Створення директорії для веб-додатку
16
- WORKDIR /app
17
-
18
- # Копіювання файлів веб-додатку
19
- COPY app.py requirements.txt /app/
20
-
21
- # Встановлення необхідних пакетів для веб-інтерфейсу, без використання репозиторію MySQL
22
  RUN rm -f /etc/apt/sources.list.d/mysql.list && \
23
  apt-get update && \
24
  apt-get install -y python3 python3-pip && \
25
- pip3 install --no-cache-dir -r requirements.txt && \
26
  rm -rf /var/lib/apt/lists/*
27
 
28
- # Скрипт для запуску обох сервісів
 
 
 
 
 
29
  COPY start.sh /start.sh
30
  RUN chmod +x /start.sh
31
 
32
- # Відкриття портів (MySQL на 443 замість 3306)
33
- EXPOSE 443 7860
34
 
35
- # Запуск обох сервісів
36
  CMD ["/start.sh"]
 
12
  # Копіювання SQL файлу ініціалізації
13
  COPY init.sql /docker-entrypoint-initdb.d/
14
 
15
+ # Встановлення необхідних пакетів для API
 
 
 
 
 
 
16
  RUN rm -f /etc/apt/sources.list.d/mysql.list && \
17
  apt-get update && \
18
  apt-get install -y python3 python3-pip && \
19
+ pip3 install --no-cache-dir flask mysql-connector-python && \
20
  rm -rf /var/lib/apt/lists/*
21
 
22
+ # Копіювання файлів API
23
+ COPY app.py /app.py
24
+ COPY static /static
25
+ COPY templates /templates
26
+
27
+ # Скрипт для запуску MySQL та API
28
  COPY start.sh /start.sh
29
  RUN chmod +x /start.sh
30
 
31
+ # Відкриття порту для API
32
+ EXPOSE 7860
33
 
34
+ # Запуск сервісів
35
  CMD ["/start.sh"]
app.py CHANGED
@@ -1,100 +1,137 @@
1
- from flask import Flask, render_template_string
2
- import socket
 
 
 
3
 
4
  app = Flask(__name__)
5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  @app.route('/')
7
  def index():
8
- """Головна сторінка з інформацією про MySQL сервер"""
 
9
 
10
- # HTML шаблон
11
- template = """
12
- <!DOCTYPE html>
13
- <html>
14
- <head>
15
- <title>MySQL Server для навчальних цілей</title>
16
- <style>
17
- body {
18
- font-family: Arial, sans-serif;
19
- margin: 0;
20
- padding: 20px;
21
- line-height: 1.6;
22
- }
23
- .container {
24
- max-width: 800px;
25
- margin: 0 auto;
26
- background-color: #f5f5f5;
27
- padding: 20px;
28
- border-radius: 5px;
29
- box-shadow: 0 2px 5px rgba(0,0,0,0.1);
30
- }
31
- h1 {
32
- color: #333;
33
- border-bottom: 1px solid #ddd;
34
- padding-bottom: 10px;
35
- }
36
- .status {
37
- padding: 10px;
38
- margin: 10px 0;
39
- border-radius: 4px;
40
- background-color: #dff0d8;
41
- border: 1px solid #d6e9c6;
42
- color: #3c763d;
43
- }
44
- pre {
45
- background-color: #f8f8f8;
46
- border: 1px solid #ddd;
47
- padding: 10px;
48
- border-radius: 4px;
49
- overflow-x: auto;
50
- }
51
- .connection-info {
52
- background-color: #e8f4f8;
53
- border: 1px solid #bce8f1;
54
- padding: 15px;
55
- margin-top: 20px;
56
- border-radius: 4px;
57
- }
58
- </style>
59
- </head>
60
- <body>
61
- <div class="container">
62
- <h1>MySQL Сервер 8.0.22 для навчальних цілей</h1>
63
 
64
- <div class="status">
65
- <strong>Статус MySQL:</strong> Запущено
66
- </div>
67
 
68
- <div class="connection-info">
69
- <h3>Параметри підключення:</h3>
70
- <p><strong>Хост:</strong> {{ host }}</p>
71
- <p><strong>Порт:</strong> 443</p>
72
- <p><strong>Користувач:</strong> test_user</p>
73
- <p><strong>Пароль:</strong> test_password</p>
74
- <p><strong>База даних:</strong> test_db</p>
75
- </div>
76
 
77
- <h3>Практичне заняття №1:</h3>
78
- <p>Для виконання тестового завдання підключіться до серверу та виконайте наступний SQL скрипт:</p>
79
- <pre>-- Нереформатований скрипт
80
- create table if not exists test (numbers int, words varchar (10));
 
 
81
 
82
- -- Відформатований скрипт
83
- CREATE TABLE IF NOT EXISTS test (
84
- numbers INT, -- Поле для цілих чисел
85
- words VARCHAR(10) -- Поле для текстових значень максимум 10 символів
86
- );</pre>
87
- </div>
88
- </body>
89
- </html>
90
- """
91
 
92
- # Отримання імені хоста для підключення
93
- hostname = socket.gethostname()
94
- host = socket.gethostbyname(hostname)
95
 
96
- return render_template_string(template, host=host)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
  if __name__ == '__main__':
99
- # Запускаємо веб-сервер на порту 7860 (стандартний для Hugging Face Spaces)
 
100
  app.run(host='0.0.0.0', port=7860)
 
1
+ from flask import Flask, request, jsonify, render_template
2
+ import mysql.connector
3
+ import json
4
+ import os
5
+ import time
6
 
7
  app = Flask(__name__)
8
 
9
+ def get_db_connection():
10
+ # Спробуємо підключитися кілька разів (може знадобитися, якщо MySQL ще запускається)
11
+ for i in range(5):
12
+ try:
13
+ return mysql.connector.connect(
14
+ host="localhost",
15
+ user="test_user",
16
+ password="test_password",
17
+ database="test_db"
18
+ )
19
+ except Exception as e:
20
+ if i < 4: # Якщо це не остання спроба
21
+ time.sleep(2) # Чекаємо 2 секунди
22
+ else:
23
+ raise e # Перекидаємо помилку далі
24
+
25
+ def get_mysql_status():
26
+ try:
27
+ conn = get_db_connection()
28
+ cursor = conn.cursor()
29
+ cursor.execute("SELECT VERSION()")
30
+ version = cursor.fetchone()[0]
31
+ cursor.close()
32
+ conn.close()
33
+ return {"status": "online", "version": version}
34
+ except Exception as e:
35
+ return {"status": "offline", "error": str(e)}
36
+
37
  @app.route('/')
38
  def index():
39
+ # Перевіряємо статус MySQL
40
+ status = get_mysql_status()
41
 
42
+ # Отримуємо списки баз даних і таблиць, якщо MySQL онлайн
43
+ databases = []
44
+ tables = []
45
+
46
+ if status["status"] == "online":
47
+ try:
48
+ conn = get_db_connection()
49
+ cursor = conn.cursor()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
 
51
+ # Отримання списку баз даних
52
+ cursor.execute("SHOW DATABASES")
53
+ databases = [db[0] for db in cursor.fetchall()]
54
 
55
+ # Отримання списку таблиць у поточній базі даних
56
+ cursor.execute("SHOW TABLES FROM test_db")
57
+ tables = [table[0] for table in cursor.fetchall()]
 
 
 
 
 
58
 
59
+ cursor.close()
60
+ conn.close()
61
+ except Exception as e:
62
+ print(f"Помилка: {e}")
63
+
64
+ return render_template('index.html', status=status, databases=databases, tables=tables)
65
 
66
+ @app.route('/execute', methods=['POST'])
67
+ def execute_query():
68
+ if request.content_type == 'application/json':
69
+ data = request.json
70
+ query = data.get('query', '')
71
+ else:
72
+ query = request.form.get('query', '')
 
 
73
 
74
+ if not query:
75
+ return jsonify({'error': 'Запит не може бути порожнім'}), 400
 
76
 
77
+ try:
78
+ conn = get_db_connection()
79
+ cursor = conn.cursor(dictionary=True)
80
+ cursor.execute(query)
81
+
82
+ if query.lower().strip().startswith('select'):
83
+ result = cursor.fetchall()
84
+ conn.close()
85
+
86
+ # Якщо запит з веб-форми, повертаємо HTML
87
+ if request.content_type != 'application/json':
88
+ return render_template('result.html', results=result, query=query)
89
+ else:
90
+ return jsonify({'result': result})
91
+ else:
92
+ conn.commit()
93
+ affected_rows = cursor.rowcount
94
+ conn.close()
95
+
96
+ # Якщо запит з веб-форми, повертаємо HTML
97
+ if request.content_type != 'application/json':
98
+ return render_template('result.html',
99
+ affected_rows=affected_rows,
100
+ query=query)
101
+ else:
102
+ return jsonify({'affected_rows': affected_rows})
103
+
104
+ except Exception as e:
105
+ error_msg = str(e)
106
+ # Якщо запит з веб-форми, повертаємо HTML
107
+ if request.content_type != 'application/json':
108
+ return render_template('result.html', error=error_msg, query=query)
109
+ else:
110
+ return jsonify({'error': error_msg}), 500
111
+
112
+ @app.route('/table/<table_name>')
113
+ def show_table(table_name):
114
+ try:
115
+ conn = get_db_connection()
116
+ cursor = conn.cursor(dictionary=True)
117
+
118
+ # Отримуємо структуру таблиці
119
+ cursor.execute(f"DESCRIBE `{table_name}`")
120
+ columns = cursor.fetchall()
121
+
122
+ # Отримуємо дані таблиці
123
+ cursor.execute(f"SELECT * FROM `{table_name}` LIMIT 100")
124
+ data = cursor.fetchall()
125
+
126
+ conn.close()
127
+ return render_template('table.html',
128
+ table_name=table_name,
129
+ columns=columns,
130
+ data=data)
131
+ except Exception as e:
132
+ return render_template('error.html', error=str(e))
133
 
134
  if __name__ == '__main__':
135
+ # Чекаємо кілька секунд для повного запуску MySQL
136
+ time.sleep(2)
137
  app.run(host='0.0.0.0', port=7860)
requirements.txt CHANGED
@@ -1 +1,2 @@
1
- flask==2.0.1
 
 
1
+ flask==2.0.1
2
+ mysql-connector-python
start.sh CHANGED
@@ -1,16 +1,24 @@
1
  #!/bin/bash
2
 
3
- # Змінюємо порт MySQL на 443
4
- sed -i 's/port.*=.*3306/port = 443/' /etc/mysql/mysql.conf.d/mysqld.cnf
5
-
6
  # Запуск MySQL у фоновому режимі
7
- echo "Запуск MySQL на порту 443..."
8
  /entrypoint.sh mysqld &
9
 
10
  # Чекаємо, поки MySQL запуститься
11
  echo "Очікування запуску MySQL..."
12
- sleep 10
 
 
 
 
 
 
 
 
 
 
 
13
 
14
- # Запуск веб-сервера
15
- echo "Запуск веб-інтерфейсу на порту 7860..."
16
- python3 /app/app.py
 
1
  #!/bin/bash
2
 
 
 
 
3
  # Запуск MySQL у фоновому режимі
4
+ echo "Запуск MySQL..."
5
  /entrypoint.sh mysqld &
6
 
7
  # Чекаємо, поки MySQL запуститься
8
  echo "Очікування запуску MySQL..."
9
+ sleep 15
10
+
11
+ # Перевірка, чи запустився MySQL
12
+ echo "Перевірка стану MySQL..."
13
+ for i in {1..10}; do
14
+ if mysqladmin ping -h localhost -u root -proot_password --silent; then
15
+ echo "MySQL успішно запущено"
16
+ break
17
+ fi
18
+ echo "Очікування..."
19
+ sleep 3
20
+ done
21
 
22
+ # Запуск Flask API
23
+ echo "Запуск API на порту 7860..."
24
+ python3 /app.py
static/style.css ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ body {
2
+ background-color: #f5f5f5;
3
+ }
4
+
5
+ .card {
6
+ border-radius: 8px;
7
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
8
+ margin-bottom: 20px;
9
+ }
10
+
11
+ .card-header {
12
+ border-top-left-radius: 8px !important;
13
+ border-top-right-radius: 8px !important;
14
+ }
15
+
16
+ pre {
17
+ background-color: #f8f9fa;
18
+ padding: 15px;
19
+ border-radius: 5px;
20
+ border: 1px solid #ddd;
21
+ font-size: 14px;
22
+ }
23
+
24
+ code {
25
+ color: #007bff;
26
+ }
27
+
28
+ .table-responsive {
29
+ max-height: 400px;
30
+ overflow-y: auto;
31
+ }
32
+
33
+ thead th {
34
+ position: sticky;
35
+ top: 0;
36
+ z-index: 1;
37
+ }
38
+
39
+ .bg-success.text-white {
40
+ transition: all 0.3s ease;
41
+ }
42
+
43
+ .btn-outline-primary {
44
+ margin-top: 10px;
45
+ }
46
+
47
+ textarea {
48
+ font-family: monospace;
49
+ }
50
+
51
+ /* Мобільні адаптації */
52
+ @media (max-width: 768px) {
53
+ pre {
54
+ font-size: 12px;
55
+ padding: 10px;
56
+ }
57
+
58
+ .container {
59
+ padding-left: 10px;
60
+ padding-right: 10px;
61
+ }
62
+ }
templates/error.html ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="uk">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Помилка</title>
7
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
8
+ <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
9
+ </head>
10
+ <body>
11
+ <div class="container mt-4">
12
+ <div class="d-flex justify-content-between align-items-center mb-4">
13
+ <h1>Помилка</h1>
14
+ <a href="/" class="btn btn-primary">Назад</a>
15
+ </div>
16
+
17
+ <div class="alert alert-danger">
18
+ <h4 class="alert-heading">Виникла помилка!</h4>
19
+ <p>{{ error }}</p>
20
+ </div>
21
+ </div>
22
+ </body>
23
+ </html>
templates/index.html ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="uk">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>MySQL 8.0.22 для навчальних цілей</title>
7
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
8
+ <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
9
+ </head>
10
+ <body>
11
+ <div class="container mt-4">
12
+ <h1 class="mb-4">MySQL Сервер 8.0.22 для навчальних цілей</h1>
13
+
14
+ <!-- Статус сервера -->
15
+ <div class="card mb-4">
16
+ <div class="card-header {% if status.status == 'online' %}bg-success text-white{% else %}bg-danger text-white{% endif %}">
17
+ <h5>Статус MySQL: {% if status.status == 'online' %}Онлайн{% else %}Офлайн{% endif %}</h5>
18
+ </div>
19
+ <div class="card-body">
20
+ {% if status.status == 'online' %}
21
+ <p><strong>Версія MySQL:</strong> {{ status.version }}</p>
22
+ {% else %}
23
+ <p class="text-danger"><strong>Помилка:</strong> {{ status.error }}</p>
24
+ {% endif %}
25
+ </div>
26
+ </div>
27
+
28
+ <!-- Параметри підключення -->
29
+ <div class="card mb-4">
30
+ <div class="card-header bg-info text-white">
31
+ <h5>Параметри підключення через API</h5>
32
+ </div>
33
+ <div class="card-body">
34
+ <p>Для виконання SQL запитів надсилайте POST запити на <code>/execute</code> з JSON тілом:</p>
35
+ <pre><code>{
36
+ "query": "ваш SQL запит"
37
+ }</code></pre>
38
+ <p>Приклад використання з curl:</p>
39
+ <pre><code>curl -X POST https://mysql-itud-docsa.hf.space/execute \
40
+ -H "Content-Type: application/json" \
41
+ -d '{"query":"SELECT * FROM test"}'</code></pre>
42
+ </div>
43
+ </div>
44
+
45
+ <!-- SQL Консоль -->
46
+ <div class="card mb-4">
47
+ <div class="card-header bg-primary text-white">
48
+ <h5>SQL Консоль</h5>
49
+ </div>
50
+ <div class="card-body">
51
+ <form action="/execute" method="post">
52
+ <div class="mb-3">
53
+ <label for="query" class="form-label">Введіть SQL запит:</label>
54
+ <textarea class="form-control" id="query" name="query" rows="6" placeholder="SELECT * FROM test"></textarea>
55
+ </div>
56
+ <button type="submit" class="btn btn-primary">Виконати</button>
57
+ </form>
58
+ </div>
59
+ </div>
60
+
61
+ <!-- Список баз даних і таблиць -->
62
+ <div class="row">
63
+ <!-- Бази даних -->
64
+ <div class="col-md-6">
65
+ <div class="card mb-4">
66
+ <div class="card-header bg-secondary text-white">
67
+ <h5>Бази даних</h5>
68
+ </div>
69
+ <div class="card-body">
70
+ {% if databases %}
71
+ <ul class="list-group">
72
+ {% for db in databases %}
73
+ <li class="list-group-item">{{ db }}</li>
74
+ {% endfor %}
75
+ </ul>
76
+ {% else %}
77
+ <p>Немає доступних баз даних або MySQL не запущено</p>
78
+ {% endif %}
79
+ </div>
80
+ </div>
81
+ </div>
82
+
83
+ <!-- Таблиці -->
84
+ <div class="col-md-6">
85
+ <div class="card mb-4">
86
+ <div class="card-header bg-secondary text-white">
87
+ <h5>Таблиці в test_db</h5>
88
+ </div>
89
+ <div class="card-body">
90
+ {% if tables %}
91
+ <ul class="list-group">
92
+ {% for table in tables %}
93
+ <li class="list-group-item">
94
+ <a href="/table/{{ table }}">{{ table }}</a>
95
+ </li>
96
+ {% endfor %}
97
+ </ul>
98
+ {% else %}
99
+ <p>Немає доступних таблиць або MySQL не запущено</p>
100
+ {% endif %}
101
+ </div>
102
+ </div>
103
+ </div>
104
+ </div>
105
+
106
+ <!-- Практичне завдання -->
107
+ <div class="card mb-4">
108
+ <div class="card-header bg-dark text-white">
109
+ <h5>Практичне заняття №1</h5>
110
+ </div>
111
+ <div class="card-body">
112
+ <p>Для виконання завдання 1.3 виконайте наступний SQL скрипт:</p>
113
+ <pre><code>-- Нереформатований скрипт
114
+ create table if not exists test (numbers int, words varchar (10));
115
+
116
+ -- Відформатований скрипт
117
+ CREATE TABLE IF NOT EXISTS test (
118
+ numbers INT, -- Поле для цілих чисел
119
+ words VARCHAR(10) -- Поле для текстових значень максимум 10 символів
120
+ );</code></pre>
121
+ <button class="btn btn-outline-primary" onclick="copyToQueryConsole('create table if not exists test (numbers int, words varchar (10));')">Копіювати до консолі</button>
122
+ </div>
123
+ </div>
124
+ </div>
125
+
126
+ <script>
127
+ function copyToQueryConsole(sql) {
128
+ document.getElementById('query').value = sql;
129
+ document.getElementById('query').focus();
130
+ }
131
+ </script>
132
+ </body>
133
+ </html>
templates/result.html ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="uk">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Результат SQL запиту</title>
7
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
8
+ <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
9
+ </head>
10
+ <body>
11
+ <div class="container mt-4">
12
+ <div class="d-flex justify-content-between align-items-center mb-4">
13
+ <h1>Результат запиту</h1>
14
+ <a href="/" class="btn btn-primary">Назад</a>
15
+ </div>
16
+
17
+ <!-- Виконаний запит -->
18
+ <div class="card mb-4">
19
+ <div class="card-header bg-secondary text-white">
20
+ <h5>SQL запит</h5>
21
+ </div>
22
+ <div class="card-body">
23
+ <pre><code>{{ query }}</code></pre>
24
+ </div>
25
+ </div>
26
+
27
+ <!-- Результат або помилка -->
28
+ {% if error %}
29
+ <div class="alert alert-danger">
30
+ <h4 class="alert-heading">Помилка!</h4>
31
+ <p>{{ error }}</p>
32
+ </div>
33
+ {% elif affected_rows is defined %}
34
+ <div class="alert alert-success">
35
+ <h4 class="alert-heading">Запит виконано успішно!</h4>
36
+ <p>Змінено рядків: {{ affected_rows }}</p>
37
+ </div>
38
+ {% elif results %}
39
+ <div class="card">
40
+ <div class="card-header bg-success text-white">
41
+ <h5>Результати запиту</h5>
42
+ </div>
43
+ <div class="card-body p-0">
44
+ <div class="table-responsive">
45
+ <table class="table table-striped table-bordered mb-0">
46
+ <thead class="table-dark">
47
+ <tr>
48
+ {% for column in results[0].keys() %}
49
+ <th>{{ column }}</th>
50
+ {% endfor %}
51
+ </tr>
52
+ </thead>
53
+ <tbody>
54
+ {% for row in results %}
55
+ <tr>
56
+ {% for value in row.values() %}
57
+ <td>{{ value }}</td>
58
+ {% endfor %}
59
+ </tr>
60
+ {% endfor %}
61
+ </tbody>
62
+ </table>
63
+ </div>
64
+ </div>
65
+ <div class="card-footer">
66
+ <p class="mb-0">Знайдено записів: {{ results|length }}</p>
67
+ </div>
68
+ </div>
69
+ {% else %}
70
+ <div class="alert alert-info">
71
+ <h4 class="alert-heading">Запит виконано успішно</h4>
72
+ <p>Запит не повернув результатів.</p>
73
+ </div>
74
+ {% endif %}
75
+ </div>
76
+ </body>
77
+ </html>
templates/table.html ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="uk">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Таблиця {{ table_name }}</title>
7
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
8
+ <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
9
+ </head>
10
+ <body>
11
+ <div class="container mt-4">
12
+ <div class="d-flex justify-content-between align-items-center mb-4">
13
+ <h1>Таблиця: {{ table_name }}</h1>
14
+ <a href="/" class="btn btn-primary">Назад</a>
15
+ </div>
16
+
17
+ <!-- Структура таблиці -->
18
+ <div class="card mb-4">
19
+ <div class="card-header bg-info text-white">
20
+ <h5>Структура таблиці</h5>
21
+ </div>
22
+ <div class="card-body p-0">
23
+ <div class="table-responsive">
24
+ <table class="table table-striped table-bordered mb-0">
25
+ <thead class="table-dark">
26
+ <tr>
27
+ <th>Поле</th>
28
+ <th>Тип</th>
29
+ <th>Null</th>
30
+ <th>Ключ</th>
31
+ <th>За замовчуванням</th>
32
+ <th>Додатково</th>
33
+ </tr>
34
+ </thead>
35
+ <tbody>
36
+ {% for column in columns %}
37
+ <tr>
38
+ <td>{{ column.Field }}</td>
39
+ <td>{{ column.Type }}</td>
40
+ <td>{{ column.Null }}</td>
41
+ <td>{{ column.Key }}</td>
42
+ <td>{{ column.Default }}</td>
43
+ <td>{{ column.Extra }}</td>
44
+ </tr>
45
+ {% endfor %}
46
+ </tbody>
47
+ </table>
48
+ </div>
49
+ </div>
50
+ </div>
51
+
52
+ <!-- Дані таблиці -->
53
+ <div class="card">
54
+ <div class="card-header bg-success text-white">
55
+ <h5>Дані таблиці (перші 100 рядків)</h5>
56
+ </div>
57
+ <div class="card-body p-0">
58
+ <div class="table-responsive">
59
+ <table class="table table-striped table-bordered mb-0">
60
+ <thead class="table-dark">
61
+ <tr>
62
+ {% for column in columns %}
63
+ <th>{{ column.Field }}</th>
64
+ {% endfor %}
65
+ </tr>
66
+ </thead>
67
+ <tbody>
68
+ {% for row in data %}
69
+ <tr>
70
+ {% for column in columns %}
71
+ <td>{{ row[column.Field] }}</td>
72
+ {% endfor %}
73
+ </tr>
74
+ {% endfor %}
75
+ </tbody>
76
+ </table>
77
+ </div>
78
+ </div>
79
+ <div class="card-footer">
80
+ <p class="mb-0">Показано записів: {{ data|length }} (максимум 100)</p>
81
+ </div>
82
+ </div>
83
+
84
+ <!-- SQL генератор -->
85
+ <div class="card mt-4">
86
+ <div class="card-header bg-primary text-white">
87
+ <h5>SQL запити для таблиці {{ table_name }}</h5>
88
+ </div>
89
+ <div class="card-body">
90
+ <div class="mb-3">
91
+ <h6>SELECT всіх даних</h6>
92
+ <div class="input-group">
93
+ <input type="text" class="form-control" value="SELECT * FROM {{ table_name }}" readonly id="select-query">
94
+ <button class="btn btn-outline-secondary" type="button" onclick="copyToClipboard('select-query')">Копіювати</button>
95
+ <a href="/execute?query=SELECT * FROM {{ table_name }}" class="btn btn-primary" target="_blank">Виконати</a>
96
+ </div>
97
+ </div>
98
+
99
+ <div class="mb-3">
100
+ <h6>INSERT новий запис</h6>
101
+ <div class="input-group">
102
+ <input type="text" class="form-control" value="INSERT INTO {{ table_name }} ({% for column in columns %}{{ column.Field }}{% if not loop.last %}, {% endif %}{% endfor %}) VALUES (/* значення */);" readonly id="insert-query">
103
+ <button class="btn btn-outline-secondary" type="button" onclick="copyToClipboard('insert-query')">Копіювати</button>
104
+ </div>
105
+ </div>
106
+
107
+ <div class="mb-3">
108
+ <h6>UPDATE запис</h6>
109
+ <div class="input-group">
110
+ <input type="text" class="form-control" value="UPDATE {{ table_name }} SET /* поле = значення */ WHERE /* умова */" readonly id="update-query">
111
+ <button class="btn btn-outline-secondary" type="button" onclick="copyToClipboard('update-query')">Копіювати</button>
112
+ </div>
113
+ </div>
114
+
115
+ <div>
116
+ <h6>DELETE запис</h6>
117
+ <div class="input-group">
118
+ <input type="text" class="form-control" value="DELETE FROM {{ table_name }} WHERE /* умова */" readonly id="delete-query">
119
+ <button class="btn btn-outline-secondary" type="button" onclick="copyToClipboard('delete-query')">Копіювати</button>
120
+ </div>
121
+ </div>
122
+ </div>
123
+ </div>
124
+ </div>
125
+
126
+ <script>
127
+ function copyToClipboard(elementId) {
128
+ const element = document.getElementById(elementId);
129
+ element.select();
130
+ document.execCommand('copy');
131
+
132
+ // Додати підсвічування для зворотного зв'язку
133
+ element.classList.add('bg-success', 'text-white');
134
+ setTimeout(() => {
135
+ element.classList.remove('bg-success', 'text-white');
136
+ }, 500);
137
+ }
138
+ </script>
139
+ </body>
140
+ </html>