]>
Commit | Line | Data |
---|---|---|
f3037be6 | 1 | #!/usr/bin/env python3 |
2 | import logging | |
3 | from collections import defaultdict | |
4 | from datetime import datetime, date, time | |
5 | from hashlib import sha256 | |
6 | from json import loads, dumps, JSONEncoder | |
7 | from random import choice | |
8 | from signal import signal, SIGTERM | |
9 | from time import monotonic | |
10 | from uuid import uuid4, UUID | |
11 | ||
12 | import templates as tpl | |
13 | from momoko import Pool | |
14 | from psycopg2 import extras, ProgrammingError, DataError | |
15 | from tornado import gen | |
16 | from tornado.ioloop import IOLoop | |
17 | from tornado.web import Application, StaticFileHandler | |
18 | from tornado.websocket import WebSocketHandler, WebSocketError | |
19 | ||
20 | extras.register_uuid() | |
21 | JSONEncoder_default = JSONEncoder.default | |
22 | ||
23 | def json_encoder(self, o): | |
24 | if isinstance(o, UUID): | |
25 | return str(o) | |
26 | if isinstance(o, (datetime, date, time)): | |
27 | return o.isoformat() | |
28 | return JSONEncoder_default(self, o) | |
29 | JSONEncoder.default = json_encoder | |
30 | ||
31 | ||
32 | def authorized(f): | |
33 | def wrapper(*args): | |
34 | if args[0].uid: | |
35 | return f(*args) | |
36 | else: | |
37 | args[0].write_message( | |
38 | dumps(dict(tpl.ERROR_MESSAGE, text="Not authorized")) | |
39 | ) | |
40 | args[0].write_message(dumps(tpl.AUTH_FORM)) | |
41 | ||
42 | return wrapper | |
43 | ||
44 | ||
45 | class Handler(WebSocketHandler): | |
46 | def __init__(self, application, request, **kwargs): | |
47 | super().__init__(application, request, **kwargs) | |
48 | self.uid = None | |
49 | self.role = False | |
50 | self.profile = None | |
51 | ||
52 | def open(self): | |
53 | logging.info("WebSocket opened") | |
54 | ||
55 | def check_origin(self, origin): | |
56 | return True | |
57 | ||
58 | @gen.coroutine | |
59 | def on_message(self, message): | |
60 | try: | |
61 | message = loads(message) | |
62 | logging.debug("Message received") | |
63 | if hasattr(self, message['action']): | |
64 | try: | |
65 | return getattr(self, message['action'])(message) | |
66 | except: | |
67 | self.write_message(dumps(dict(tpl.ERROR_MESSAGE, | |
68 | text="Bad request"))) | |
69 | else: | |
70 | return self.write_message(dumps(tpl.AUTH_FORM)) | |
71 | except (TypeError, KeyError): | |
72 | return self.write_message(dumps(tpl.AUTH_FORM)) | |
73 | ||
74 | def about(self, message): | |
75 | return self.write_message(dumps(tpl.ABOUT)) | |
76 | ||
77 | @gen.coroutine | |
78 | def auth(self, message): | |
79 | if 'params' not in message: | |
80 | return self.write_message( | |
81 | dumps(dict(tpl.WARN_MESSAGE, text="Invalid request")) | |
82 | ) | |
83 | ||
84 | user = defaultdict(lambda: None, **message['params']) | |
85 | if 'username' not in user or user['username'] == "": | |
86 | return self.write_message( | |
87 | dumps(dict(tpl.ERROR_MESSAGE, | |
88 | text="Username must not be empty")) | |
89 | ) | |
90 | if 'password' not in user or user['password'] == "": | |
91 | return self.write_message( | |
92 | dumps(dict(tpl.ERROR_MESSAGE, | |
93 | text="Password must not be empty")) | |
94 | ) | |
95 | ||
96 | user['password'] = sha256(user['password'].encode("utf8")).hexdigest() | |
97 | cursor = yield self.application.db.execute( | |
98 | "select uid, password, role, profile " | |
99 | "from users where username=%(username)s", user | |
100 | ) | |
101 | db_result = cursor.fetchone() | |
102 | if not db_result: | |
103 | return self.write_message( | |
104 | dumps(dict(tpl.ERROR_MESSAGE, text="Username does not exists")) | |
105 | ) | |
106 | if user['password'] != db_result[1]: | |
107 | if db_result[0] in self.application.wsPool: | |
108 | self.application.wsPool[db_result[0]].write_message( | |
109 | dumps(dict(tpl.WARN_MESSAGE, | |
110 | text="Someone wants to hack you"))) | |
111 | return self.write_message( | |
112 | dumps(dict(tpl.ERROR_MESSAGE, text="Invalid password")) | |
113 | ) | |
114 | ||
115 | self.uid = db_result[0] | |
116 | self.role = db_result[2] | |
117 | self.profile = db_result[3] | |
118 | name = user['username'] | |
119 | if self.profile: | |
120 | cursor = yield self.application.db.execute( | |
121 | "select name, lastname from profiles where profileid=%s", | |
122 | (self.profile,) | |
123 | ) | |
124 | db_result = cursor.fetchone() | |
125 | if db_result: | |
126 | name = " ".join(db_result) | |
127 | ||
128 | self.write_message(dumps(dict(tpl.SUCCESS_MESSAGE, | |
129 | text="Welcome, %s" % name))) | |
130 | ||
131 | self.application.wsPool[self.uid] = self | |
132 | yield self.show_profiles({'params': {'offset': 0}}) | |
133 | ||
134 | def register(self, message): | |
135 | if 'params' not in message: | |
136 | return self.write_message( | |
137 | dumps(dict(tpl.WARN_MESSAGE, text="Invalid request")) | |
138 | ) | |
139 | user = defaultdict(lambda: None, **message['params']) | |
140 | ||
141 | if 'username' not in user or user['username'] == "": | |
142 | return self.write_message( | |
143 | dumps(dict(tpl.ERROR_MESSAGE, | |
144 | text="Username must not be empty")) | |
145 | ) | |
146 | if 'password' not in user or user['password'] == "": | |
147 | return self.write_message( | |
148 | dumps(dict(tpl.ERROR_MESSAGE, | |
149 | text="Password must not be empty")) | |
150 | ) | |
151 | ||
152 | user['password'] = sha256(user['password'].encode("utf8")).hexdigest() | |
153 | ||
154 | cursor = yield self.application.db.execute( | |
155 | "select uid from users where username=%(username)s", user | |
156 | ) | |
157 | db_result = cursor.fetchone() | |
158 | if db_result: | |
159 | return self.write_message( | |
160 | dumps(dict(tpl.ERROR_MESSAGE, text="User already exists")) | |
161 | ) | |
162 | try: | |
163 | user['uid'] = str(uuid4().hex) | |
27c0aaff | 164 | user['role'] = False #len(user['username']) < 3 |
f3037be6 | 165 | yield self.application.db.execute( |
166 | "INSERT INTO users(uid, username, password, role, profile)" | |
167 | "VALUES (%(uid)s, %(username)s, " | |
168 | "%(password)s, %(role)s, %(profile)s)", | |
169 | user | |
170 | ) | |
171 | except ProgrammingError: | |
172 | return self.write_message( | |
173 | dumps(dict(tpl.ERROR_MESSAGE, text="Error while registration")) | |
174 | ) | |
175 | else: | |
176 | return self.write_message( | |
177 | dumps( | |
178 | dict(tpl.SUCCESS_MESSAGE, | |
179 | text="Registration successful (uid=%s)" % user['uid']) | |
180 | ) | |
181 | ) | |
182 | ||
183 | @authorized | |
184 | @gen.coroutine | |
185 | def search(self, message): | |
186 | try: | |
187 | text = message['params']['text'] | |
188 | if not text: | |
189 | return | |
190 | text = text.split() | |
191 | if len(text) > 1: | |
192 | cursor = yield self.application.db.execute( | |
193 | "select profileid, name, lastname from profiles " | |
194 | "where name LIKE '%%'||%s||'%%' and " | |
195 | "lastname LIKE '%%'||%s||'%%' limit 5 ", | |
196 | (text[0], text[1]) | |
197 | ) | |
198 | else: | |
199 | cursor = yield self.application.db.execute( | |
200 | "select profileid, name, lastname from profiles " | |
201 | "where name LIKE '%%'||%s||'%%' or " | |
202 | "lastname LIKE '%%'||%s||'%%' limit 5 ", | |
203 | (text[0], text[0]) | |
204 | ) | |
205 | ||
206 | cursor_crimes = yield self.application.db.execute( | |
207 | "select crimeid, name, crimedate from crimes " | |
208 | "where name LIKE '%%'||%s||'%%' limit 5 ", | |
209 | (text[0], ) | |
210 | ) | |
211 | ||
212 | try: | |
213 | cursor_users = yield self.application.db.execute( | |
214 | "select username from users where uid=%s", | |
215 | (UUID(text[0]), ) | |
216 | ) | |
217 | db_result = cursor_users.fetchall() | |
218 | users = [ | |
219 | {'answer': row[0]} | |
220 | for row in db_result | |
221 | ] | |
222 | except (DataError, ProgrammingError, ValueError): | |
223 | users = [] | |
224 | ||
225 | db_result = cursor.fetchall() | |
226 | profiles = [ | |
227 | dict(zip('profileid name lastname'.split(), row)) | |
228 | for row in db_result | |
229 | ] | |
230 | db_result = cursor_crimes.fetchall() | |
231 | crimes = [ | |
232 | dict(zip('crimeid name crimedate'.split(), row)) | |
233 | for row in db_result | |
234 | ] | |
235 | ||
236 | result = tpl.SEARCH.copy() | |
237 | result['rows'][0]['data'] = profiles | |
238 | result['rows'][1]['data'] = crimes | |
239 | result['rows'][2]['data'] = users | |
240 | return self.write_message(dumps(result)) | |
241 | except: | |
242 | pass | |
243 | ||
244 | @authorized | |
245 | @gen.coroutine | |
246 | def show_profiles(self, message): | |
247 | offset = message['params']['offset'] * 10 | |
248 | try: | |
249 | cursor = yield self.application.db.execute( | |
250 | "select profileid, name, lastname, userpic from profiles " | |
251 | "limit 10 offset %s", (offset,) | |
252 | ) | |
253 | except (DataError, ProgrammingError): | |
254 | return self.write_message( | |
255 | dumps(dict(tpl.ERROR_MESSAGE, text="Error while fetching")) | |
256 | ) | |
257 | ||
258 | db_result = cursor.fetchall() | |
259 | users = [ | |
260 | dict(zip('uid name lastname userpic'.split(), row)) | |
261 | for row in db_result | |
262 | ] | |
263 | if len(users) > 10: | |
264 | yield self.application.db.execute( | |
265 | "DELETE FROM users WHERE uid=%s", (self.uid,) | |
266 | ) | |
267 | self.uid = self.role = self.profile = None | |
268 | self.write_message( | |
269 | dumps(dict(tpl.ERROR_MESSAGE, | |
270 | text="Hands off, dirty hacker!"))) | |
271 | return self.write_message( | |
272 | dumps(dict(tpl.WARN_MESSAGE, | |
273 | text="User deleted"))) | |
274 | result = tpl.PROFILES.copy() | |
275 | result['rows'][0]['data'] = users | |
276 | return self.write_message(dumps(result)) | |
277 | ||
278 | @authorized | |
279 | @gen.coroutine | |
280 | def show_my_profile(self, _): | |
281 | try: | |
282 | if self.profile: | |
283 | return self.show_profile({'params': {'uid': str(self.profile)}}) | |
284 | else: | |
285 | self.write_message( | |
286 | dumps(dict(tpl.WARN_MESSAGE, | |
287 | text="You does not have a profile"))) | |
288 | except Exception as e: | |
289 | self.write_message(dumps(dict(tpl.ERROR_MESSAGE, | |
290 | text="Bad request: %s" % e))) | |
291 | ||
292 | @authorized | |
293 | @gen.coroutine | |
294 | def show_profile(self, message): | |
295 | try: | |
296 | profileid = UUID(message['params']['uid']) | |
297 | cursor = yield self.application.db.execute( | |
298 | "select name, lastname, userpic, birthdate, " | |
299 | "city, mobile, marital, crimes from profiles " | |
300 | "where profileid=%s", (profileid, ) | |
301 | ) | |
302 | db_result = cursor.fetchone() | |
303 | if not db_result: | |
304 | return self.write_message( | |
305 | dumps(dict(tpl.WARN_MESSAGE, | |
306 | text="Profile not found"))) | |
307 | user = dict(zip("name lastname userpic birthdate " | |
308 | "city mobile marital crimes".split(), | |
309 | db_result)) | |
310 | if not (self.role or self.profile == profileid): | |
311 | user['birthdate'] = user['mobile'] = "<hidden>" | |
312 | user['marital_icon'] = ( | |
313 | choice(['fa-venus-mars', 'fa-venus-double', 'fa-mars-double']) | |
314 | if user['marital'] else 'fa-genderless' | |
315 | ) | |
316 | if user['crimes']: | |
317 | crimes = yield self.get_crimes(user['crimes'], profileid) | |
318 | else: | |
319 | crimes = None | |
320 | result = tpl.PROFILE.copy() | |
321 | result['rows'][0]['cols'][0]['rows'][0]['hidden'] = ( | |
322 | self.profile is not None | |
323 | ) | |
324 | result['rows'][0]['cols'][0]['rows'][0]['click'] = ("itsMe('%s')" | |
325 | % profileid) | |
326 | result['rows'][0]['cols'][0]['rows'][1]['data'] = { | |
327 | 'userpic': user['userpic'] | |
328 | } | |
329 | result['rows'][0]['cols'][0]['rows'][2]['data'] = { | |
330 | 'icon': 'fa-balance-scale' if not crimes | |
331 | else '' | |
332 | } | |
333 | ||
334 | result['rows'][0]['cols'][1]['rows'][0]['data'] = user | |
335 | if crimes: | |
336 | result['rows'][0]['cols'][1]['rows'][1]['hidden'] = False | |
337 | result['rows'][0]['cols'][1]['rows'][1]['data'] = crimes | |
338 | else: | |
339 | result['rows'][0]['cols'][1]['rows'][1]['hidden'] = True | |
340 | return self.write_message(dumps(result)) | |
341 | except Exception as e: | |
342 | self.write_message(dumps(dict(tpl.ERROR_MESSAGE, | |
343 | text="Bad request: %s" % e))) | |
344 | ||
345 | @authorized | |
346 | @gen.coroutine | |
347 | def its_me(self, message): | |
348 | try: | |
349 | profileid = UUID(message['params']['profileid']) | |
350 | info = message['params']['info'] | |
351 | if self.profile: | |
352 | return self.write_message( | |
353 | dumps(dict(tpl.WARN_MESSAGE, | |
354 | text="You already have a profile")) | |
355 | ) | |
356 | if not self.uid: | |
357 | return self.write_message( | |
358 | dumps(dict(tpl.ERROR_MESSAGE, | |
359 | text="You does not have user account")) | |
360 | ) | |
361 | try: | |
362 | cursor = yield self.application.db.execute( | |
363 | "select birthdate, mobile from profiles " | |
364 | "where profileid=%s", | |
365 | (profileid, ) | |
366 | ) | |
367 | db_result = cursor.fetchone() | |
368 | if not db_result: | |
369 | return self.write_message( | |
370 | dumps(dict(tpl.ERROR_MESSAGE, | |
371 | text="Profile not found")) | |
372 | ) | |
373 | if (info == db_result[0].isoformat() or | |
374 | "".join(filter(str.isdigit, info)) == | |
375 | "".join(filter(str.isdigit, db_result[1]))): | |
376 | yield self.application.db.execute( | |
377 | "UPDATE users SET profile=%s WHERE uid=%s", | |
378 | (profileid, self.uid) | |
379 | ) | |
380 | else: | |
381 | return self.write_message( | |
382 | dumps(dict(tpl.ERROR_MESSAGE, | |
383 | text="Wrong information")) | |
384 | ) | |
385 | except ProgrammingError: | |
386 | return self.write_message( | |
387 | dumps(dict(tpl.ERROR_MESSAGE, | |
388 | text="Error while assignment")) | |
389 | ) | |
390 | else: | |
391 | self.profile = profileid | |
392 | return self.write_message( | |
393 | dumps(dict(tpl.SUCCESS_MESSAGE, text="Assignment successful")) | |
394 | ) | |
395 | ||
396 | except Exception as e: | |
397 | self.write_message(dumps(dict(tpl.ERROR_MESSAGE, | |
398 | text="Bad request: %s" % e))) | |
399 | ||
400 | @gen.coroutine | |
401 | def get_crimes(self, crimes, current_profile): | |
402 | result = [] | |
403 | try: | |
404 | cursor = yield self.application.db.execute( | |
405 | "SELECT crimeid, name, article, city, country," | |
406 | "crimedate, description, participants, " | |
407 | "judgement, closed, public " | |
408 | "FROM crimes WHERE crimeid = ANY(%s)", | |
409 | (crimes,) | |
410 | ) | |
411 | ||
412 | db_result = cursor.fetchall() | |
413 | if not db_result: | |
414 | return result | |
415 | for row in db_result: | |
416 | crime = dict(zip("crimeid name article city country " | |
417 | "crimedate description participants " | |
418 | "judgement closed public".split(), | |
419 | row)) | |
420 | if crime['closed']: | |
421 | crime['verdict'] = crime['judgement'] | |
422 | else: | |
423 | crime['verdict'] = "In processing" | |
424 | if not crime['public']: | |
425 | if not (self.role or | |
426 | self.profile == current_profile or | |
427 | self.profile in crime['participants']): | |
428 | continue | |
429 | cursor_participants = yield self.application.db.execute( | |
430 | "select profileid, name, lastname " | |
431 | "from profiles where profileid=ANY(%s)", | |
432 | (crime["participants"],) | |
433 | ) | |
434 | db_result_participants = cursor_participants.fetchall() | |
435 | if not db_result_participants: | |
436 | raise Exception("participants not in database") | |
437 | crime['participants'] = "<br>".join( | |
438 | '<a onclick="showProfile(0,0, this);" ' | |
439 | 'data-uid="%s">%s %s</a>' % (r[0], r[1], r[2]) | |
440 | for r in db_result_participants | |
441 | ) | |
442 | result.append(crime) | |
443 | ||
444 | return result | |
445 | except Exception as e: | |
446 | self.write_message(dumps(dict(tpl.ERROR_MESSAGE, | |
447 | text="Can't get crimes: %s" % e))) | |
448 | ||
449 | @authorized | |
450 | @gen.coroutine | |
451 | def show_crimes(self, message): | |
452 | offset = message['params']['offset'] * 10 | |
453 | try: | |
454 | cursor = yield self.application.db.execute( | |
455 | "select crimeid, name, article, city, " | |
456 | "country, crimedate, public " | |
457 | "FROM crimes ORDER BY crimeid " | |
458 | "DESC limit 10 offset %s" % (offset,) | |
459 | ) | |
460 | db_result = cursor.fetchall() | |
461 | except (DataError, ProgrammingError): | |
462 | return self.write_message( | |
463 | dumps(dict(tpl.ERROR_MESSAGE, | |
464 | text="Error while fetching"))) | |
465 | crimes = [ | |
466 | dict(zip( | |
467 | 'crimeid name article city country crimedate public'.split(), | |
468 | row)) | |
469 | for row in db_result | |
470 | ] | |
471 | if len(crimes) > 10: | |
472 | yield self.application.db.execute( | |
473 | "DELETE FROM users WHERE uid=%s", (self.uid,) | |
474 | ) | |
475 | self.uid = self.role = self.profile = None | |
476 | self.write_message( | |
477 | dumps(dict(tpl.ERROR_MESSAGE, | |
478 | text="Hands off, dirty hacker!"))) | |
479 | return self.write_message( | |
480 | dumps(dict(tpl.WARN_MESSAGE, | |
481 | text="User deleted"))) | |
482 | for crime in crimes: | |
483 | crime['public'] = "" if crime['public'] else "fa-lock" | |
484 | result = tpl.CRIMES.copy() | |
485 | result['rows'][0]['data'] = crimes | |
486 | return self.write_message(dumps(result)) | |
487 | ||
488 | @authorized | |
489 | @gen.coroutine | |
490 | def show_crime(self, message): | |
491 | try: | |
492 | cursor = yield self.application.db.execute( | |
493 | "SELECT name, article, city, country, " | |
494 | "crimedate, description, participants, judgement, " | |
495 | "closed, public, author " | |
496 | "FROM crimes WHERE crimeid=%s", (message['params']['crimeid'],) | |
497 | ) | |
498 | db_result = cursor.fetchone() | |
499 | crime = dict(zip("name article city country " | |
500 | "crimedate description participants " | |
501 | "judgement closed public author".split(), | |
502 | db_result)) | |
503 | ||
504 | cursor_participants = yield self.application.db.execute( | |
505 | "select profileid, name, lastname " | |
506 | "from profiles where profileid=ANY(%s)", | |
507 | (crime["participants"],) | |
508 | ) | |
509 | db_result_participants = cursor_participants.fetchall() | |
510 | if not db_result_participants: | |
511 | crime['participants'] = "" | |
512 | else: | |
513 | crime['participants'] = "<br>".join( | |
514 | '<a onclick="showProfile(0,0, this);" ' | |
515 | 'data-uid="%s">%s %s</a>' % (r[0], r[1], r[2]) | |
516 | for r in db_result_participants | |
517 | ) | |
518 | result = tpl.CRIME.copy() | |
519 | result['rows'][0]['data'] = crime | |
520 | ||
521 | if (crime['public'] or self.role or self.uid == crime['author'] or | |
522 | (self.profile and crime["participants"] and | |
523 | str(self.profile) in crime["participants"])): | |
524 | return self.write_message(dumps(result)) | |
525 | else: | |
526 | self.write_message( | |
527 | dumps(dict(tpl.ERROR_MESSAGE, | |
528 | text="You are not the Author or %s" | |
529 | % " or ".join( | |
530 | [p[1][0] + ". " + p[2] | |
531 | for p in db_result_participants])))) | |
532 | except Exception as e: | |
533 | self.write_message(dumps(dict(tpl.ERROR_MESSAGE, | |
534 | text="Can't get crime: %s" % e))) | |
535 | ||
536 | @authorized | |
537 | def report(self, message): | |
538 | try: | |
539 | if 'params' not in message: | |
540 | return self.write_message( | |
541 | dumps(dict(tpl.WARN_MESSAGE, text="Invalid request")) | |
542 | ) | |
543 | params = dict(filter(lambda i: i[1], message['params'].items())) | |
544 | if len(params) < 6: | |
545 | return self.write_message( | |
546 | dumps(dict(tpl.WARN_MESSAGE, text="Small input")) | |
547 | ) | |
548 | params['city'] = params['city'].title() | |
549 | if len(params['country']) < 4: | |
550 | params['country'] = params['country'].upper() | |
551 | else: | |
552 | params['country'] = params['country'].title() | |
553 | ||
554 | params['crimedate'] = datetime.strptime( | |
555 | params['crimedate'], '%Y-%m-%dT%H:%M:%S.%fZ').date() | |
556 | if 'closed' not in params: | |
557 | params['judgement'] = None | |
558 | params['closed'] = False | |
559 | else: | |
560 | params['closed'] = params['closed'] > 0 | |
561 | params['public'] = 'private' not in params | |
562 | params['crimeid'] = int(monotonic() * 1000000000) | |
563 | params['author'] = self.uid | |
564 | ||
565 | if 'participants' in params: | |
566 | params['participants'] = list( | |
567 | map(UUID, params['participants'].split(',')) | |
568 | ) | |
569 | else: | |
570 | params['participants'] = None | |
571 | sql = [( | |
572 | "INSERT INTO crimes(crimeid, name, article, city, " | |
573 | "country, crimedate, description, participants, " | |
574 | "judgement, closed, public, author) " | |
575 | "VALUES (%(crimeid)s, %(name)s, %(article)s, %(city)s, " | |
576 | "%(country)s, %(crimedate)s, %(description)s, " | |
577 | "%(participants)s, %(judgement)s, %(closed)s, " | |
578 | "%(public)s, %(author)s);", params | |
579 | ), ] | |
580 | if params['participants']: | |
581 | sql.append(( | |
582 | "UPDATE profiles SET crimes=crimes|| %(crimeid)s::bigint " | |
583 | "WHERE profileid = ANY(%(participants)s);", params | |
584 | )) | |
585 | yield self.application.db.transaction(sql) | |
586 | yield self.application.db.execute("COMMIT;") | |
587 | except ProgrammingError: | |
588 | return self.write_message( | |
589 | dumps(dict(tpl.ERROR_MESSAGE, text="Error while reporting")) | |
590 | ) | |
591 | except Exception as e: | |
592 | return self.write_message( | |
593 | dumps(dict(tpl.ERROR_MESSAGE, text="Invalid input %s " % e)) | |
594 | ) | |
595 | else: | |
596 | try: | |
597 | for_deletion = [] | |
598 | for ws in self.application.wsPool: | |
599 | if self.application.wsPool[ws] == self: | |
600 | continue | |
601 | try: | |
602 | self.application.wsPool[ws].write_message( | |
603 | dumps(dict(tpl.INFO_MESSAGE, | |
604 | text="New crime (%s)" % params['name'])) | |
605 | ) | |
606 | except WebSocketError: | |
607 | for_deletion.append(ws) | |
608 | for ws in for_deletion: | |
609 | del self.application.wsPool[ws] | |
610 | except Exception as e: | |
611 | logging.warning("%s. Clients: %s" | |
612 | % (e, self.application.wsPool.keys())) | |
613 | else: | |
614 | return self.write_message( | |
615 | dumps(dict(tpl.SUCCESS_MESSAGE, | |
616 | text="Crime %s submited" % params['name'])) | |
617 | ) | |
618 | ||
619 | def on_close(self): | |
620 | logging.info("WebSocket %s closed" % self.uid) | |
621 | if self.uid in self.application.wsPool: | |
622 | del self.application.wsPool[self.uid] | |
623 | ||
624 | ||
625 | def signal_term_handler(sig, _): | |
626 | logging.error("Got %s. Quit.", sig) | |
627 | exit(0) | |
628 | ||
629 | ||
630 | if __name__ == '__main__': | |
631 | signal(SIGTERM, signal_term_handler) | |
632 | app = Application([ | |
633 | (r"/websocket", Handler), | |
634 | (r"/()", StaticFileHandler, {'path': 'static/index.html'}), | |
635 | (r"/userpics/(.+)", StaticFileHandler, {'path': 'userpics/'}), | |
636 | (r"/static/(.+)", StaticFileHandler, {'path': 'static/'}), | |
637 | ]) | |
638 | try: | |
639 | ioloop = IOLoop.instance() | |
640 | app.db = Pool(dsn="dbname=mol user=mol password=molpassword " | |
641 | "host=localhost port=5432", | |
642 | size=5, max_size=100, auto_shrink=True, | |
643 | ioloop=ioloop) | |
644 | future = app.db.connect() | |
645 | ioloop.add_future(future, lambda _: ioloop.stop()) | |
646 | app.wsPool = {} | |
647 | ioloop.start() | |
648 | future.result() | |
649 | app.listen(1984, address="127.0.0.1") | |
650 | ioloop.start() | |
651 | except KeyboardInterrupt: | |
652 | signal_term_handler(SIGTERM, None) |