Aleksmorshen commited on
Commit
359498b
·
verified ·
1 Parent(s): e06abaf

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +442 -167
app.py CHANGED
@@ -1,60 +1,82 @@
1
  from flask import Flask, render_template_string, jsonify, request
2
- import sqlite3
 
3
  import math
 
4
 
5
  app = Flask(__name__)
6
 
7
- # Инициализация базы данных SQLite3
8
  def init_db():
9
- conn = sqlite3.connect('markers.db')
10
- cursor = conn.cursor()
11
- cursor.execute('''CREATE TABLE IF NOT EXISTS markers (
12
- id INTEGER PRIMARY KEY AUTOINCREMENT,
13
- name TEXT NOT NULL,
14
- description TEXT,
15
- telegram_link TEXT,
16
- latitude REAL NOT NULL,
17
- longitude REAL NOT NULL
18
- )''')
19
- conn.commit()
20
- conn.close()
21
-
22
- # Получение всех меток из базы данных
 
23
  def get_all_markers():
24
- conn = sqlite3.connect('markers.db')
25
- cursor = conn.cursor()
26
- cursor.execute('SELECT id, name, description, telegram_link, latitude, longitude FROM markers')
27
- markers = cursor.fetchall()
28
- conn.close()
29
- return markers
30
-
31
- # Добавление новой метки в базу данных
32
- def add_marker_to_db(name, description, telegram_link, latitude, longitude):
33
- conn = sqlite3.connect('markers.db')
34
- cursor = conn.cursor()
35
- cursor.execute('INSERT INTO markers (name, description, telegram_link, latitude, longitude) VALUES (?, ?, ?, ?, ?)',
36
- (name, description, telegram_link, latitude, longitude))
37
- conn.commit()
38
- marker_id = cursor.lastrowid
39
- conn.close()
40
- return marker_id
41
-
42
- # Удаление метки из базы данных
 
 
 
 
43
  def remove_marker_from_db(marker_id):
44
- conn = sqlite3.connect('markers.db')
45
- cursor = conn.cursor()
46
- cursor.execute('DELETE FROM markers WHERE id = ?', (marker_id,))
47
- conn.commit()
48
- conn.close()
49
-
50
- # Вычисление расстояния между двумя точками на Земле
51
- def calculate_distance(lat1, lon1, lat2, lon2):
52
- R = 6371.0 # Радиус Земли в километрах
53
- d_lat = math.radians(lat2 - lat1)
54
- d_lon = math.radians(lon2 - lon1)
55
- a = math.sin(d_lat / 2) ** 2 + math.cos(math.radians(lat1)) * math.cos(math.radians(lat2)) * math.sin(d_lon / 2) ** 2
56
- c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
57
- return R * c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
  # HTML-шаблон с улучшенным дизайном
60
  html_template = '''
@@ -66,109 +88,201 @@ html_template = '''
66
  <title>GeoGram</title>
67
  <link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css">
68
  <style>
 
69
  body {
70
  font-family: 'Roboto', sans-serif;
71
- background-color: #f0f4f8;
72
  margin: 0;
73
  padding: 0;
74
- color: #333;
 
 
 
 
 
 
 
 
 
 
 
75
  }
76
  #map {
77
  height: 90vh;
78
  width: 100%;
79
  margin-bottom: 20px;
80
  }
81
- h1 {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  text-align: center;
 
 
 
 
 
 
83
  padding: 20px;
84
- margin: 0;
85
- background-color: #007BFF;
86
- color: white;
87
- font-size: 24px;
88
  }
89
- .leaflet-popup-content-wrapper {
90
- background-color: #fff;
91
- border-radius: 10px;
92
- padding: 10px;
93
- box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
94
  }
95
- .leaflet-popup-content b {
96
- color: #007BFF;
97
  font-size: 18px;
98
- display: block;
99
- margin-bottom: 5px;
100
  }
101
- .leaflet-popup-content a {
102
- color: #007BFF;
 
 
 
 
103
  text-decoration: none;
 
104
  }
105
- .leaflet-popup-content a:hover {
106
  text-decoration: underline;
107
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  .popup-form {
109
- display: flex;
110
- flex-direction: column;
111
- width: 200px;
 
 
112
  }
113
- .popup-form input, .popup-form textarea {
114
- padding: 10px;
 
115
  margin-bottom: 10px;
116
- border: 1px solid #ccc;
117
- border-radius: 5px;
118
  font-size: 14px;
 
 
 
 
 
 
 
 
119
  }
120
  .popup-form button {
121
- background-color: #007BFF;
122
  color: white;
123
  border: none;
124
- padding: 10px;
125
- border-radius: 5px;
126
  cursor: pointer;
 
 
 
127
  }
128
  .popup-form button:hover {
129
- background-color: #0056b3;
130
  }
131
- #search-container {
132
- margin: 20px;
133
- display: flex;
134
- justify-content: center;
135
- align-items: center;
 
 
136
  }
137
- #search-input {
138
- padding: 10px;
139
- width: 300px;
140
- border: 1px solid #ccc;
141
- border-radius: 5px;
142
- margin-right: 10px;
143
- font-size: 14px;
144
  }
145
- #search-button {
146
- padding: 10px 20px;
147
- background-color: #007BFF;
 
 
 
 
 
 
 
 
 
 
 
 
148
  color: white;
149
  border: none;
150
- border-radius: 5px;
 
151
  cursor: pointer;
152
  font-size: 14px;
 
 
153
  }
154
- #search-button:hover {
155
- background-color: #0056b3;
156
- }
157
- #search-results {
158
- margin: 20px;
159
- text-align: center;
160
- }
161
- .result-item {
162
- background: white;
163
- border: 1px solid #ccc;
164
- border-radius: 5px;
165
- margin: 10px auto;
166
- padding: 10px;
167
- transition: background 0.3s;
168
- max-width: 500px;
169
  }
170
- .result-item:hover {
171
- background: #f0f0f0;
 
 
 
 
 
 
172
  }
173
  </style>
174
  </head>
@@ -183,7 +297,7 @@ html_template = '''
183
 
184
  <script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
185
  <script>
186
- const map = L.map('map').setView([55.75, 37.61], 13); // Начальное местоположение по умолчанию
187
 
188
  L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
189
  maxZoom: 19,
@@ -192,16 +306,14 @@ html_template = '''
192
  let userLatitude = 55.75;
193
  let userLongitude = 37.61;
194
 
195
- // Проверка поддержки геолокации и установка центра карты на текущее местоположение пользователя
196
  if (navigator.geolocation) {
197
  navigator.geolocation.getCurrentPosition(function(position) {
198
  userLatitude = position.coords.latitude;
199
  userLongitude = position.coords.longitude;
200
  map.setView([userLatitude, userLongitude], 13);
201
 
202
- // Добавление маркера для местоположения пользователя
203
  L.marker([userLatitude, userLongitude]).addTo(map)
204
- .bindPopup('Вы находитесь здесь').openPopup();
205
  }, function() {
206
  alert('Не удалось получить ваше местоположение.');
207
  });
@@ -209,18 +321,78 @@ html_template = '''
209
  alert('Ваш браузер не поддерживает геолокацию.');
210
  }
211
 
212
- // Загрузка существующих меток из базы данных
 
213
  fetch('/get_markers')
214
  .then(response => response.json())
215
  .then(data => {
216
  data.forEach(marker => {
217
- const markerObj = L.marker([marker.latitude, marker.longitude]).addTo(map);
218
- markerObj.bindPopup(`<b>${marker.name}</b><p>${marker.description}</p><a href="https://t.me/${marker.telegram_link}" target="_blank">Перейти в Telegram</a><br><button onclick="removeMarker(${marker.id})">Удалить</button>`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
  });
220
  });
221
 
222
- // Функция для добавления метки
223
- function addMarker(name, description, telegram_link, position) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
  fetch('/add_marker', {
225
  method: 'POST',
226
  headers: {
@@ -230,43 +402,81 @@ html_template = '''
230
  name: name,
231
  description: description,
232
  telegram_link: telegram_link,
233
- position: [position.lat, position.lng]
 
 
234
  })
235
  })
236
  .then(response => response.json())
237
  .then(data => {
238
- const markerObj = L.marker([data.latitude, data.longitude]).addTo(map);
239
- markerObj.bindPopup(`<b>${data.name}</b><p>${data.description}</p><a href="https://t.me/${data.telegram_link}" target="_blank">Перейти в Telegram</a><br><button onclick="removeMarker(${data.id})">Удалить</button>`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
  });
241
  }
242
 
243
- // Событие клика по карте для добавления формы
244
  map.on('click', function(e) {
245
  const popupContent = document.createElement('div');
246
  popupContent.classList.add('popup-form');
247
 
248
  const inputName = document.createElement('input');
249
  inputName.type = 'text';
250
- inputName.placeholder = 'Название метки';
251
  popupContent.appendChild(inputName);
252
 
253
  const inputDescription = document.createElement('textarea');
254
- inputDescription.placeholder = 'Описание метки';
255
  popupContent.appendChild(inputDescription);
256
 
257
  const inputTelegram = document.createElement('input');
258
  inputTelegram.type = 'text';
259
- inputTelegram.placeholder = 'Telegram-ссылка';
260
  popupContent.appendChild(inputTelegram);
261
 
 
 
 
 
 
262
  const submitButton = document.createElement('button');
263
  submitButton.textContent = 'Добавить метку';
264
  submitButton.onclick = function() {
265
  const name = inputName.value;
266
  const description = inputDescription.value;
267
  const telegram_link = inputTelegram.value;
268
- if (name && description && telegram_link) {
269
- addMarker(name, description, telegram_link, e.latlng);
 
270
  }
271
  map.closePopup();
272
  };
@@ -278,20 +488,26 @@ html_template = '''
278
  .openOn(map);
279
  });
280
 
281
- // Удаление метки
282
- function removeMarker(markerId) {
283
- fetch('/remove_marker', {
284
- method: 'POST',
285
- headers: {
286
- 'Content-Type': 'application/json',
287
- },
288
- body: JSON.stringify({ id: markerId })
289
- }).then(() => {
290
- location.reload(); // Обновление страницы для обновления маркеров
291
- });
 
 
 
 
 
 
292
  }
293
 
294
- // Поиск и сортировка по расстоянию до пользователя
295
  document.getElementById('search-button').addEventListener('click', function() {
296
  const searchTerm = document.getElementById('search-input').value.toLowerCase();
297
  fetch('/get_markers')
@@ -305,35 +521,81 @@ html_template = '''
305
  marker.distance = calculateDistance(userLatitude, userLongitude, marker.latitude, marker.longitude);
306
  });
307
 
308
- // Сортировка по расстоянию
309
- data.sort((a, b) => a.distance - b.distance);
310
-
311
- const results = data.filter(marker =>
312
- marker.name.toLowerCase().includes(searchTerm) ||
313
  marker.description.toLowerCase().includes(searchTerm)
314
  );
315
 
 
 
 
 
 
 
 
 
 
 
316
  results.forEach(marker => {
317
  const resultItem = document.createElement('div');
318
  resultItem.classList.add('result-item');
319
- resultItem.innerHTML = `<b>${marker.name}</b><p>${marker.description}</p><p>Расстояние: ${marker.distance.toFixed(2)} км</p><a href="https://t.me/${marker.telegram_link}" target="_blank">Перейти в Telegram</a>`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
320
  resultsContainer.appendChild(resultItem);
321
  });
322
 
323
  if (results.length === 0) {
324
- resultsContainer.innerHTML = '<p>Нет результатов</p>';
325
  }
326
  });
327
  });
328
 
329
- // Вычисление расстояния между двумя координатами
 
 
 
 
 
 
 
 
 
 
 
330
  function calculateDistance(lat1, lon1, lat2, lon2) {
331
- const R = 6371; // Радиус Земли в км
332
  const dLat = (lat2 - lat1) * Math.PI / 180;
333
  const dLon = (lon2 - lon1) * Math.PI / 180;
334
- const a =
335
- 0.5 - Math.cos(dLat)/2 +
336
- Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
337
  (1 - Math.cos(dLon)) / 2;
338
 
339
  return R * 2 * Math.asin(Math.sqrt(a));
@@ -346,20 +608,18 @@ html_template = '''
346
  # Маршрут для главной страницы
347
  @app.route('/')
348
  def index():
 
349
  return render_template_string(html_template)
350
 
351
  # Маршрут для получения всех меток
352
  @app.route('/get_markers')
353
- def get_markers():
354
  markers = get_all_markers()
355
- return jsonify([{
356
- 'id': marker[0],
357
- 'name': marker[1],
358
- 'description': marker[2],
359
- 'telegram_link': marker[3],
360
- 'latitude': marker[4],
361
- 'longitude': marker[5]
362
- } for marker in markers])
363
 
364
  # Маршрут для добавления метки
365
  @app.route('/add_marker', methods=['POST'])
@@ -368,26 +628,41 @@ def add_marker():
368
  name = data['name']
369
  description = data['description']
370
  telegram_link = data['telegram_link']
 
371
  latitude, longitude = data['position']
 
372
 
373
- marker_id = add_marker_to_db(name, description, telegram_link, latitude, longitude)
 
374
  return jsonify({
375
  'id': marker_id,
376
  'name': name,
377
  'description': description,
378
  'telegram_link': telegram_link,
 
379
  'latitude': latitude,
380
- 'longitude': longitude
 
 
 
381
  })
382
 
383
  # Маршрут для удаления метки
384
  @app.route('/remove_marker', methods=['POST'])
385
- def remove_marker():
386
  data = request.json
387
  marker_id = data['id']
388
  remove_marker_from_db(marker_id)
389
  return jsonify({'success': True})
390
 
 
 
 
 
 
 
 
 
391
  if __name__ == '__main__':
392
  init_db()
393
- app.run(debug=True, host='0.0.0.0', port=7860) # Запуск приложения на всех интерфейсах и порту 7860
 
1
  from flask import Flask, render_template_string, jsonify, request
2
+ import json
3
+ import os
4
  import math
5
+ from datetime import datetime, timedelta
6
 
7
  app = Flask(__name__)
8
 
9
+ # Инициализация файла JSON (если файла нет – создаётся пустой список)
10
  def init_db():
11
+ if not os.path.exists('markers.json'):
12
+ with open('markers.json', 'w', encoding='utf-8') as f:
13
+ json.dump([], f, ensure_ascii=False, indent=4)
14
+
15
+ # Загрузка меток из файла JSON
16
+ def load_markers():
17
+ with open('markers.json', 'r', encoding='utf-8') as f:
18
+ return json.load(f)
19
+
20
+ # Сохранение меток в файл JSON
21
+ def save_markers(markers):
22
+ with open('markers.json', 'w', encoding='utf-8') as f:
23
+ json.dump(markers, f, ensure_ascii=False, indent=4)
24
+
25
+ # Получение всех меток
26
  def get_all_markers():
27
+ return load_markers()
28
+
29
+ # Добавление новой метки в "базу" (JSON-файл)
30
+ def add_marker_to_db(name, description, telegram_link, logo_link, latitude, longitude, delete_password):
31
+ markers = load_markers()
32
+ new_id = max((marker["id"] for marker in markers), default=0) + 1
33
+ new_marker = {
34
+ "id": new_id,
35
+ "name": name,
36
+ "description": description,
37
+ "telegram_link": telegram_link,
38
+ "logo_link": logo_link,
39
+ "latitude": latitude,
40
+ "longitude": longitude,
41
+ "delete_password": delete_password,
42
+ "premium": 0,
43
+ "premium_start_date": None
44
+ }
45
+ markers.append(new_marker)
46
+ save_markers(markers)
47
+ return new_id
48
+
49
+ # Удаление метки из JSON-файла
50
  def remove_marker_from_db(marker_id):
51
+ markers = load_markers()
52
+ markers = [marker for marker in markers if marker["id"] != marker_id]
53
+ save_markers(markers)
54
+
55
+ # Активация премиум-статуса для метки
56
+ def activate_premium(marker_id):
57
+ markers = load_markers()
58
+ current_date = datetime.now().isoformat()
59
+ for marker in markers:
60
+ if marker["id"] == marker_id:
61
+ marker["premium"] = 1
62
+ marker["premium_start_date"] = current_date
63
+ break
64
+ save_markers(markers)
65
+
66
+ # Проверка и сброс премиум-статуса по истечении 30 дней
67
+ def check_premium_status():
68
+ markers = load_markers()
69
+ updated = False
70
+ for marker in markers:
71
+ if marker["premium"] == 1 and marker["premium_start_date"]:
72
+ premium_start_date = datetime.fromisoformat(marker["premium_start_date"])
73
+ current_date = datetime.now()
74
+ if (current_date - premium_start_date).days > 30:
75
+ marker["premium"] = 0
76
+ marker["premium_start_date"] = None
77
+ updated = True
78
+ if updated:
79
+ save_markers(markers)
80
 
81
  # HTML-шаблон с улучшенным дизайном
82
  html_template = '''
 
88
  <title>GeoGram</title>
89
  <link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css">
90
  <style>
91
+ /* Общие стили */
92
  body {
93
  font-family: 'Roboto', sans-serif;
94
+ background: linear-gradient(135deg, #483D8B, #2c3e50);
95
  margin: 0;
96
  padding: 0;
97
+ color: #fff; /* Белый текст */
98
+ overflow-x: hidden;
99
+ }
100
+ h1 {
101
+ text-align: center;
102
+ padding: 20px;
103
+ margin: 0;
104
+ background: linear-gradient(135deg, #6a0dad, #483D8B);
105
+ color: white;
106
+ font-size: 28px;
107
+ letter-spacing: 2px;
108
+ text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.7);
109
  }
110
  #map {
111
  height: 90vh;
112
  width: 100%;
113
  margin-bottom: 20px;
114
  }
115
+
116
+ /* Поиск */
117
+ #search-container {
118
+ display: flex;
119
+ justify-content: center;
120
+ align-items: center;
121
+ margin: 20px;
122
+ }
123
+ #search-input {
124
+ padding: 15px;
125
+ width: 300px;
126
+ border: none;
127
+ border-radius: 30px 0 0 30px;
128
+ font-size: 16px;
129
+ background: #34495e;
130
+ color: #fff;
131
+ outline: none;
132
+ transition: background 0.3s ease;
133
+ }
134
+ #search-input:focus {
135
+ background: #2c3e50;
136
+ }
137
+ #search-button {
138
+ padding: 15px 30px;
139
+ background: linear-gradient(135deg, #6a0dad, #483D8B);
140
+ color: white;
141
+ border: none;
142
+ border-radius: 0 30px 30px 0;
143
+ cursor: pointer;
144
+ font-size: 16px;
145
+ transition: background 0.3s ease;
146
+ box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.5);
147
+ }
148
+ #search-button:hover {
149
+ background: linear-gradient(135deg, #483D8B, #6a0dad);
150
+ }
151
+
152
+ /* Результаты поиска */
153
+ #search-results {
154
+ margin: 20px;
155
  text-align: center;
156
+ }
157
+ .result-item {
158
+ background: linear-gradient(135deg, #34495e, #2c3e50);
159
+ border: 1px solid rgba(106, 13, 173, 0.5);
160
+ border-radius: 15px;
161
+ margin: 10px auto;
162
  padding: 20px;
163
+ max-width: 500px;
164
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
165
+ box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.5);
 
166
  }
167
+ .result-item:hover {
168
+ transform: translateY(-5px);
169
+ box-shadow: 0px 8px 16px rgba(0, 0, 0, 0.7);
 
 
170
  }
171
+ .result-item b {
172
+ color: #fff;
173
  font-size: 18px;
 
 
174
  }
175
+ .result-item p {
176
+ color: #ecf0f1;
177
+ margin: 5px 0;
178
+ }
179
+ .result-item a {
180
+ color: #fff;
181
  text-decoration: none;
182
+ font-weight: bold;
183
  }
184
+ .result-item a:hover {
185
  text-decoration: underline;
186
  }
187
+ .result-item button {
188
+ background: linear-gradient(135deg, #6a0dad, #483D8B);
189
+ color: white;
190
+ border: none;
191
+ padding: 10px 20px;
192
+ border-radius: 30px;
193
+ cursor: pointer;
194
+ font-size: 14px;
195
+ transition: background 0.3s ease;
196
+ box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.5);
197
+ }
198
+ .result-item button:hover {
199
+ background: linear-gradient(135deg, #483D8B, #6a0dad);
200
+ }
201
+
202
+ /* Окно добавления метки */
203
  .popup-form {
204
+ background: linear-gradient(135deg, #34495e, #2c3e50);
205
+ border-radius: 15px;
206
+ padding: 20px;
207
+ box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.5);
208
+ width: 300px;
209
  }
210
+ .popup-form input,
211
+ .popup-form textarea {
212
+ padding: 12px;
213
  margin-bottom: 10px;
214
+ border: none;
215
+ border-radius: 10px;
216
  font-size: 14px;
217
+ background: #483D8B;
218
+ color: #fff;
219
+ outline: none;
220
+ transition: background 0.3s ease;
221
+ }
222
+ .popup-form input:focus,
223
+ .popup-form textarea:focus {
224
+ background: #6a0dad;
225
  }
226
  .popup-form button {
227
+ background: linear-gradient(135deg, #6a0dad, #483D8B);
228
  color: white;
229
  border: none;
230
+ padding: 12px;
231
+ border-radius: 30px;
232
  cursor: pointer;
233
+ font-size: 14px;
234
+ transition: background 0.3s ease;
235
+ box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.5);
236
  }
237
  .popup-form button:hover {
238
+ background: linear-gradient(135deg, #483D8B, #6a0dad);
239
  }
240
+
241
+ /* Попапы на карте */
242
+ .leaflet-popup-content-wrapper {
243
+ background: linear-gradient(135deg, #34495e, #2c3e50);
244
+ border-radius: 15px;
245
+ padding: 15px;
246
+ box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.5);
247
  }
248
+ .leaflet-popup-content {
249
+ color: #fff;
 
 
 
 
 
250
  }
251
+ .leaflet-popup-content b {
252
+ color: #fff;
253
+ font-size: 18px;
254
+ display: block;
255
+ margin-bottom: 5px;
256
+ }
257
+ .leaflet-popup-content a {
258
+ color: #fff;
259
+ text-decoration: none;
260
+ }
261
+ .leaflet-popup-content a:hover {
262
+ text-decoration: underline;
263
+ }
264
+ .leaflet-popup-content button {
265
+ background: linear-gradient(135deg, #6a0dad, #483D8B);
266
  color: white;
267
  border: none;
268
+ padding: 10px 20px;
269
+ border-radius: 30px;
270
  cursor: pointer;
271
  font-size: 14px;
272
+ transition: background 0.3s ease;
273
+ box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.5);
274
  }
275
+ .leaflet-popup-content button:hover {
276
+ background: linear-gradient(135deg, #483D8B, #6a0dad);
 
 
 
 
 
 
 
 
 
 
 
 
 
277
  }
278
+
279
+ /* Маркеры */
280
+ .marker-logo {
281
+ width: 25px;
282
+ height: 25px;
283
+ border-radius: 50%;
284
+ border: 2px solid #6a0dad;
285
+ box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.5);
286
  }
287
  </style>
288
  </head>
 
297
 
298
  <script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
299
  <script>
300
+ const map = L.map('map').setView([55.75, 37.61], 13);
301
 
302
  L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
303
  maxZoom: 19,
 
306
  let userLatitude = 55.75;
307
  let userLongitude = 37.61;
308
 
 
309
  if (navigator.geolocation) {
310
  navigator.geolocation.getCurrentPosition(function(position) {
311
  userLatitude = position.coords.latitude;
312
  userLongitude = position.coords.longitude;
313
  map.setView([userLatitude, userLongitude], 13);
314
 
 
315
  L.marker([userLatitude, userLongitude]).addTo(map)
316
+ .bindPopup('<span style="color:white;">Вы находитесь здесь</span>').openPopup();
317
  }, function() {
318
  alert('Не удалось получить ваше местоположение.');
319
  });
 
321
  alert('Ваш браузер не поддерживает геолокацию.');
322
  }
323
 
324
+ let markersOnMap = {};
325
+
326
  fetch('/get_markers')
327
  .then(response => response.json())
328
  .then(data => {
329
  data.forEach(marker => {
330
+ const customIcon = L.divIcon({
331
+ className: 'custom-icon',
332
+ html: `<img src="${marker.logo_link || 'https://simpleicon.com/wp-content/uploads/map-marker-1.png'}" class="marker-logo"/>`,
333
+ iconSize: [25, 25],
334
+ iconAnchor: [12.5, 25]
335
+ });
336
+
337
+ const markerObj = L.marker([marker.latitude, marker.longitude], {icon: customIcon}).addTo(map);
338
+ markersOnMap[marker.id] = markerObj;
339
+
340
+ let premiumLabel = '';
341
+ if (marker.premium) {
342
+ const premiumStartDate = new Date(marker.premium_start_date);
343
+ const currentDate = new Date();
344
+ const daysLeft = Math.max(0, Math.floor((premiumStartDate.getTime() + (30 * 24 * 60 * 60 * 1000) - currentDate.getTime()) / (1000 * 60 * 60 * 24)));
345
+ premiumLabel = `<span style="color: gold;">Премиум метка (${daysLeft} дней осталось)</span><br>`;
346
+ }
347
+
348
+ markerObj.bindPopup(`
349
+ <div class="leaflet-popup-content">
350
+ ${premiumLabel}
351
+ <b>${marker.name}</b>
352
+ <p>${marker.description}</p>
353
+ <button style="background: linear-gradient(135deg, #6a0dad, #483D8B);
354
+ color: white;
355
+ border: none;
356
+ padding: 10px 20px;
357
+ border-radius: 30px;
358
+ cursor: pointer;
359
+ font-size: 14px;
360
+ transition: background 0.3s ease;
361
+ box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.5);"
362
+ onclick="window.open('https://t.me/${marker.telegram_link}', '_blank')">
363
+ Перейти в Telegram
364
+ </button><br>
365
+ <button onclick="buyPremium(${marker.id})">Купить премиум</button>
366
+ <button onclick="removeMarker(${marker.id}, '${marker.delete_password}')">Удалить</button>
367
+ </div>
368
+ `);
369
  });
370
  });
371
 
372
+ function buyPremium(markerId) {
373
+ const activationCode = prompt("Введите код активации премиума:");
374
+
375
+ if (activationCode === "morshenadmin87132") {
376
+ fetch('/activate_premium', {
377
+ method: 'POST',
378
+ headers: {
379
+ 'Content-Type': 'application/json',
380
+ },
381
+ body: JSON.stringify({id: markerId})
382
+ }).then(() => {
383
+ alert("Премиум активирован!");
384
+ location.reload();
385
+ });
386
+ } else {
387
+ alert("Неверный код активации!");
388
+ }
389
+ }
390
+
391
+ function addMarker(name, description, telegram_link, logo_link, position) {
392
+ const deletePassword = prompt("Введите пароль для удаления метки:");
393
+
394
+ const finalLogoLink = logo_link || 'https://icons.veryicon.com/png/o/miscellaneous/high-icon-library/geo-fence.png';
395
+
396
  fetch('/add_marker', {
397
  method: 'POST',
398
  headers: {
 
402
  name: name,
403
  description: description,
404
  telegram_link: telegram_link,
405
+ logo_link: finalLogoLink,
406
+ position: [position.lat, position.lng],
407
+ delete_password: deletePassword
408
  })
409
  })
410
  .then(response => response.json())
411
  .then(data => {
412
+ const customIcon = L.divIcon({
413
+ className: 'custom-icon',
414
+ html: `<img src="${data.logo_link}" class="marker-logo"/>`,
415
+ iconSize: [25, 25],
416
+ iconAnchor: [12.5, 25]
417
+ });
418
+
419
+ const markerObj = L.marker([data.latitude, data.longitude], {icon: customIcon}).addTo(map);
420
+ markersOnMap[data.id] = markerObj;
421
+
422
+ let premiumLabel = data.premium ? '<span style="color: gold;">Премиум метка</span><br>' : '';
423
+
424
+ markerObj.bindPopup(`
425
+ <div class="leaflet-popup-content">
426
+ ${premiumLabel}
427
+ <b>${data.name}</b>
428
+ <p>${data.description}</p>
429
+ <button style="background: linear-gradient(135deg, #6a0dad, #483D8B);
430
+ color: white;
431
+ border: none;
432
+ padding: 10px 20px;
433
+ border-radius: 30px;
434
+ cursor: pointer;
435
+ font-size: 14px;
436
+ transition: background 0.3s ease;
437
+ box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.5);"
438
+ onclick="window.open('https://t.me/${data.telegram_link}', '_blank')">
439
+ Перейти в Telegram
440
+ </button><br>
441
+ <button onclick="buyPremium(${data.id})">Купить премиум</button>
442
+ <button onclick="removeMarker(${data.id}, '${data.delete_password}')">Удалить</button>
443
+ </div>
444
+ `);
445
  });
446
  }
447
 
 
448
  map.on('click', function(e) {
449
  const popupContent = document.createElement('div');
450
  popupContent.classList.add('popup-form');
451
 
452
  const inputName = document.createElement('input');
453
  inputName.type = 'text';
454
+ inputName.placeholder = 'Название ';
455
  popupContent.appendChild(inputName);
456
 
457
  const inputDescription = document.createElement('textarea');
458
+ inputDescription.placeholder = 'Ваши товары/услуги';
459
  popupContent.appendChild(inputDescription);
460
 
461
  const inputTelegram = document.createElement('input');
462
  inputTelegram.type = 'text';
463
+ inputTelegram.placeholder = 'Telegram канал (без @)';
464
  popupContent.appendChild(inputTelegram);
465
 
466
+ const inputLogoLink = document.createElement('input');
467
+ inputLogoLink.type = 'text';
468
+ inputLogoLink.placeholder = 'Ссылка на логотип (необязательно)';
469
+ popupContent.appendChild(inputLogoLink);
470
+
471
  const submitButton = document.createElement('button');
472
  submitButton.textContent = 'Добавить метку';
473
  submitButton.onclick = function() {
474
  const name = inputName.value;
475
  const description = inputDescription.value;
476
  const telegram_link = inputTelegram.value;
477
+ const logo_link = inputLogoLink.value;
478
+ if(name && description && telegram_link) {
479
+ addMarker(name, description, telegram_link, logo_link, e.latlng);
480
  }
481
  map.closePopup();
482
  };
 
488
  .openOn(map);
489
  });
490
 
491
+ function removeMarker(markerId, markerPassword) {
492
+ const universalPassword = "morshenadmin87132";
493
+ const passwordInput = prompt("Введите пароль для удаления ��етки:");
494
+
495
+ if (passwordInput === universalPassword || passwordInput === markerPassword) {
496
+ fetch('/remove_marker', {
497
+ method: 'POST',
498
+ headers: {
499
+ 'Content-Type': 'application/json',
500
+ },
501
+ body: JSON.stringify({id: markerId})
502
+ }).then(() => {
503
+ location.reload();
504
+ });
505
+ } else {
506
+ alert("Неверный пароль!");
507
+ }
508
  }
509
 
510
+ // Обновленная функция поиска с приоритетом премиум меток
511
  document.getElementById('search-button').addEventListener('click', function() {
512
  const searchTerm = document.getElementById('search-input').value.toLowerCase();
513
  fetch('/get_markers')
 
521
  marker.distance = calculateDistance(userLatitude, userLongitude, marker.latitude, marker.longitude);
522
  });
523
 
524
+ // Фильтрация результатов по поисковому запросу
525
+ const results = data.filter(marker =>
526
+ marker.name.toLowerCase().includes(searchTerm) ||
 
 
527
  marker.description.toLowerCase().includes(searchTerm)
528
  );
529
 
530
+ // Сортировка результатов: сначала премиум метки, затем обычные,
531
+ // и внутри каждой категории сортировка по расстоянию
532
+ results.sort((a, b) => {
533
+ if (a.premium !== b.premium) {
534
+ return b.premium - a.premium; // Премиум метки идут первыми
535
+ }
536
+ return a.distance - b.distance; // Сортировка по расстоянию
537
+ });
538
+
539
+ // Отображение результатов
540
  results.forEach(marker => {
541
  const resultItem = document.createElement('div');
542
  resultItem.classList.add('result-item');
543
+
544
+ let premiumLabel = '';
545
+ if (marker.premium) {
546
+ const premiumStartDate = new Date(marker.premium_start_date);
547
+ const currentDate = new Date();
548
+ const daysLeft = Math.max(0, Math.floor((premiumStartDate.getTime() + (30 * 24 * 60 * 60 * 1000) - currentDate.getTime()) / (1000 * 60 * 60 * 24)));
549
+ premiumLabel = ` (${daysLeft} дней осталось)`;
550
+ }
551
+
552
+ resultItem.innerHTML = `
553
+ <b>${marker.name}</b>
554
+ <p>${marker.description}</p>
555
+ <p>Расстояние: ${marker.distance.toFixed(2)} км</p>
556
+ <p>Статус: ${marker.premium ? 'Премиум' + premiumLabel : 'Обычная'}</p>
557
+ <button style="background: linear-gradient(135deg, #6a0dad, #483D8B);
558
+ color: white;
559
+ border: none;
560
+ padding: 10px 20px;
561
+ border-radius: 30px;
562
+ cursor: pointer;
563
+ font-size: 14px;
564
+ transition: background 0.3s ease;
565
+ box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.5);"
566
+ onclick="window.open('https://t.me/${marker.telegram_link}', '_blank')">
567
+ Перейти в Telegram
568
+ </button>
569
+ <button onclick="showOnMap(${marker.id}, ${marker.latitude}, ${marker.longitude}); hideResults()">Показать на карте</button>`;
570
+
571
  resultsContainer.appendChild(resultItem);
572
  });
573
 
574
  if (results.length === 0) {
575
+ resultsContainer.innerHTML = '<p style="color:white;">Нет результатов</p>';
576
  }
577
  });
578
  });
579
 
580
+ function showOnMap(markerId, lat, lng) {
581
+ map.setView([lat, lng], 13);
582
+ if (markersOnMap[markerId]) {
583
+ markersOnMap[markerId].openPopup();
584
+ }
585
+ }
586
+
587
+ function hideResults() {
588
+ const resultsContainer = document.getElementById('search-results');
589
+ resultsContainer.innerHTML = '';
590
+ }
591
+
592
  function calculateDistance(lat1, lon1, lat2, lon2) {
593
+ const R = 6371;
594
  const dLat = (lat2 - lat1) * Math.PI / 180;
595
  const dLon = (lon2 - lon1) * Math.PI / 180;
596
+ const a =
597
+ 0.5 - Math.cos(dLat) / 2 +
598
+ Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
599
  (1 - Math.cos(dLon)) / 2;
600
 
601
  return R * 2 * Math.asin(Math.sqrt(a));
 
608
  # Маршрут для главной страницы
609
  @app.route('/')
610
  def index():
611
+ check_premium_status()
612
  return render_template_string(html_template)
613
 
614
  # Маршрут для получения всех меток
615
  @app.route('/get_markers')
616
+ def get_markers_route():
617
  markers = get_all_markers()
618
+ # Если для метки не указан логотип, используем изображение по умолчанию
619
+ for marker in markers:
620
+ if not marker.get("logo_link"):
621
+ marker["logo_link"] = "https://simpleicon.com/wp-content/uploads/map-marker-1.png"
622
+ return jsonify(markers)
 
 
 
623
 
624
  # Маршрут для добавления метки
625
  @app.route('/add_marker', methods=['POST'])
 
628
  name = data['name']
629
  description = data['description']
630
  telegram_link = data['telegram_link']
631
+ logo_link = data['logo_link'] if data['logo_link'] else 'https://simpleicon.com/wp-content/uploads/map-marker-1.png'
632
  latitude, longitude = data['position']
633
+ delete_password = data['delete_password']
634
 
635
+ marker_id = add_marker_to_db(name, description, telegram_link, logo_link, latitude, longitude, delete_password)
636
+
637
  return jsonify({
638
  'id': marker_id,
639
  'name': name,
640
  'description': description,
641
  'telegram_link': telegram_link,
642
+ 'logo_link': logo_link,
643
  'latitude': latitude,
644
+ 'longitude': longitude,
645
+ 'delete_password': delete_password,
646
+ 'premium': 0,
647
+ 'premium_start_date': None
648
  })
649
 
650
  # Маршрут для удаления метки
651
  @app.route('/remove_marker', methods=['POST'])
652
+ def remove_marker_route():
653
  data = request.json
654
  marker_id = data['id']
655
  remove_marker_from_db(marker_id)
656
  return jsonify({'success': True})
657
 
658
+ # Маршрут для активации премиум-статуса
659
+ @app.route('/activate_premium', methods=['POST'])
660
+ def activate_premium_route():
661
+ data = request.json
662
+ marker_id = data['id']
663
+ activate_premium(marker_id)
664
+ return jsonify({'success': True})
665
+
666
  if __name__ == '__main__':
667
  init_db()
668
+ app.run(debug=True, host='0.0.0.0', port=7860)