-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontrollers.py
180 lines (157 loc) · 4.82 KB
/
controllers.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
import json
import os
import flask
from flask_socketio import SocketIO, emit, ConnectionRefusedError
# TODO absolute import
from config import NAMESPACE, STATIC_DIR
from crypt import crypter, get_hash_of_password
from app import app, socketio
from data import (
add_user,
get_login_hash,
add_session_info,
delete_session_info,
get_all_chats,
add_message,
mark_readed_messages,
)
# TODO real ban
def make_ban(error):
print(error)
return "error"
# Отдача главной страницы, если не найдено в статике
@app.route("/", methods=["GET"], defaults={"useless_error": ""})
@app.errorhandler(404)
def index(useless_error):
return flask.send_from_directory(STATIC_DIR, "index.html"), 200
def login_and_register(action, data):
login = str(data["login"])
password = str(data["password"])
if not login or not password:
raise AttributeError("hack?")
if action == "login":
hash_password = get_login_hash(login)
if hash_password:
is_have_auth_error = hash_password != get_hash_of_password(password)
else:
is_have_auth_error = True
if is_have_auth_error:
return "invalid password"
# register block
elif get_login_hash(login):
return "error_is_busy"
else:
add_user(login, get_hash_of_password(password))
socketio.emit(
"new_user",
login,
broadcast=True,
namespace=NAMESPACE,
)
cookie = crypter.encrypt(login.encode("utf-8")).decode("utf-8")
response = flask.make_response(login)
response.set_cookie("login", value=cookie)
return response
def send_message(sender, receiver, message):
if not sender or not receiver or not message:
raise AttributeError("hack attempt?")
unique_id, unix_time, data = add_message(sender, receiver, message)
data["unix_time"] = unix_time
socketio.emit(
"new_message",
data,
to=unique_id,
include_self=True,
namespace=NAMESPACE,
)
return "OK"
def read_messages(login, messages):
if not isinstance(messages, list):
raise ValueError("invalid type")
else:
parsed_info = []
for readed_info in messages:
parsed_info.append(
{
"sender": readed_info["sender"],
"receiver": login,
"timestamp": readed_info["timestamp"],
}
)
messages_to_send = mark_readed_messages(parsed_info)
for chat_name, messages in messages_to_send.items():
socketio.emit(
"read_messages",
messages,
to=chat_name,
include_self=True,
namespace=NAMESPACE,
)
return "OK"
@app.route("/api", methods=["POST"])
def api_handler():
try:
data = json.loads(flask.request.data.decode("utf-8"))
action = data.pop("action")
if action in ["login", "register"]:
return login_and_register(action, data)
login = get_login_from_cookie()
if not login:
raise AttributeError("unathorized user")
if action == "logout":
response = flask.make_response("OK")
response.delete_cookie("login")
return response
elif action == "get_login":
return login
elif action == "get_chats":
return get_all_chats(login)
elif action == "send_message":
return send_message(login, data.get("receiver"), data.get("message"))
elif action == "read_messages":
return read_messages(login, data.get("messages"))
raise ValueError("unknown path")
except Exception as error:
return make_ban(error)
# Этот код нужен только для дебага (если nginx/nodejs не запущен)
# поэтому не очень красивый
@app.route("/", defaults={"path2": "", "path3": ""}, methods=["GET"])
@app.route(
"/favicon.ico",
defaults={"path2": "favicon.ico", "path3": ""},
methods=["GET"],
)
@app.route("/<path:path2>", defaults={"path3": ""}, methods=["GET"])
@app.route("/<path:path2>/<path:path3>", methods=["GET"])
def send_static(path2, path3):
result_path = [STATIC_DIR]
for item in path2, path3:
if item:
result_path.append(item)
return flask.send_from_directory(
os.path.join(*result_path[:-1]), result_path[-1]
)
def get_login_from_cookie():
login_cookie = flask.request.cookies.get("login")
if login_cookie:
try:
login = crypter.decrypt(login_cookie.encode("utf-8")).decode("utf-8")
if login:
hash = get_login_hash(login)
sid = getattr(flask.request, "sid", None)
if not hash:
raise AttributeError("deleted account")
elif sid:
add_session_info(sid, login)
return login
except Exception as error:
make_ban(error)
@socketio.on("connect", namespace=NAMESPACE)
def test_connect(auth=None):
if get_login_from_cookie():
return "OK"
else:
raise ConnectionRefusedError("need_login")
@socketio.on("disconnect", namespace=NAMESPACE)
def test_disconnect():
delete_session_info(flask.request.sid)