/
site_start.py
343 lines (268 loc) · 12.5 KB
/
site_start.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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
# Copyright (c) David Wilson 2015, 2016, 2017, 2021
# This file is part of Zoetrope.
#
# Zoetrope is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Zoetrope is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Zoetrope. If not, see <http://www.gnu.org/licenses/>.
from io import StringIO
import json
import logging
import os
import cherrypy
import cherrypy_cors
import lib.boincindicator.client as client
import lib.boincindicator.resulttypes.SuccessError as se
import config as conf
import boincsite.dbinit as dbinit
import boincsite.boinc.ProjectTasks as pt
import boincsite.boinc.TaskTasks as tt
import boincsite.boinc.SystemInfoTasks as sit
import boincsite.boinc.UserTasks as ut
import boincsite.status.DiskUsage as duj
import boincsite.status.Status as status
import boincsite.util.JSONAttrEncoder as jsae
WorkingDirectory = os.path.dirname(os.path.abspath(__file__))
class WebServer(object):
def __init__(self):
logging.basicConfig(filename=conf.log_file_name, level=conf.log_level, format=conf.log_message_format)
dbinit.init_db()
self.__client = client.BoincClient(host=conf.rpc_hostname)
if conf.rpc_password is not None:
self.__client.authorize(conf.rpc_password)
self.__project_tasks = pt.ProjectTasks(self.__client)
self.__task_tasks = tt.TaskTasks(self.__client)
self.__system_info_tasks = sit.SystemInfoTasks(self.__client)
self.__user_tasks = ut.UserTasks()
self.__io = StringIO()
self.__cwd = ''
def start(self, cwd):
cherrypy_cors.install()
self.__cwd = cwd
cherrypy.quickstart(self, '/', 'server.conf')
@cherrypy.expose
def index(self, **kwargs):
with open(self.__cwd + '/boincsite/templates/index.html') as f:
return f.read()
@cherrypy.expose
def task_json(self, **kwargs):
def f():
task_name = kwargs.get('task_name', '')
return self.__task_tasks.get_task(task_name)
return self.do_authenticated_request(f, False)
@cherrypy.expose
def projects_json(self, **kwargs):
return self.do_authenticated_request(self.__get_projects)
@cherrypy.expose
def get_statistics_json(self, **kwargs):
f = lambda: self.project_operation(kwargs, self.__project_tasks.get_project_statistics)
return self.do_authenticated_request(f)
@cherrypy.expose
def project_json(self, **kwargs):
f = lambda: [t for t in self.__get_projects() if t.project_name==kwargs.get('project', '')].pop()
return self.do_authenticated_request(f, False)
def __get_projects(self):
return list(self.__project_tasks.get_project_status())
@cherrypy.expose
def messages_json(self, **kwargs):
return self.do_authenticated_request(self.__system_info_tasks.get_messages)
@cherrypy.expose
def tasks_json(self, **kwargs):
return self.do_authenticated_request(self.__task_tasks.get_tasks)
@cherrypy.expose
def disk_usage_json(self, **kwargs):
return self.do_authenticated_request(self.__system_info_tasks.get_disk_usage, False, duj.JSONEncoder)
@cherrypy.expose
def host_info_json(self, **kwargs):
return self.do_authenticated_request(self.__system_info_tasks.get_host_info, False)
@cherrypy.expose
def daily_transfer_history_json(self, **kwargs):
return self.do_authenticated_request(self.__system_info_tasks.get_daily_transfer_history)
@cherrypy.expose
def notices_json(self, **kwargs):
return self.do_authenticated_request(self.__system_info_tasks.get_notices)
@cherrypy.expose
def get_global_preferences_json(self, **kwargs):
return self.do_authenticated_request(self.__system_info_tasks.get_global_preferences, False)
@cherrypy.expose
def get_all_projects_list_json(self, **kwargs):
return self.do_authenticated_request(self.__project_tasks.get_all_projects_list)
@cherrypy.expose
def detach_project(self, **kwargs):
f = lambda: self.project_operation(kwargs, self.__project_tasks.detach_project)
return self.do_authenticated_request(f, False)
@cherrypy.expose
def detach_project_when_done(self, **kwargs):
f = lambda: self.project_operation(kwargs, self.__project_tasks.detach_project_when_done)
return self.do_authenticated_request(f, False)
@cherrypy.expose
def dont_detach_project_when_done(self, **kwargs):
f = lambda: self.project_operation(kwargs, self.__project_tasks.dont_detach_project_when_done)
return self.do_authenticated_request(f, False)
@cherrypy.expose
def suspend_task(self, **kwargs):
f = lambda: self.task_name_operation(kwargs, self.__task_tasks.suspend_task)
return self.do_authenticated_request(f, False)
@cherrypy.expose
def resume_task(self, **kwargs):
f = lambda: self.task_name_operation(kwargs, self.__task_tasks.resume_task)
return self.do_authenticated_request(f, False)
@cherrypy.expose
def abort_task(self, **kwargs):
f = lambda: self.task_name_operation(kwargs, self.__task_tasks.abort_task)
return self.do_authenticated_request(f, False)
def task_name_operation(self, kwargs, operation_func):
task_name = kwargs.get('task_name', '')
if task_name=='':
return
operation_func(task_name)
@cherrypy.expose
def attach_project(self, **kwargs):
def f():
project_url = kwargs.get('projectUrl', '')
email_address = kwargs.get('email', '')
password_hash = kwargs.get('password', '')
return self.__project_tasks.attach_project(project_url, email_address, password_hash)
return self.do_authenticated_request(f, False)
@cherrypy.expose
def create_account(self, **kwargs):
def f():
project_url = kwargs.get('projectUrl', '')
email_address = kwargs.get('email', '')
password_hash = kwargs.get('password', '')
username = kwargs.get('username', '')
return self.__project_tasks.create_account_and_attach_to_project(project_url,
email_address,
password_hash,
username)
return self.do_authenticated_request(f, False)
@cherrypy.expose
def get_platform_json(self, **kwargs):
platform = self.__system_info_tasks.get_platform()
return '{{"platform": "{platform}"}}'.format(platform=platform)
@cherrypy.expose
def no_more_work(self, **kwargs):
f = lambda: self.project_operation(kwargs, self.__project_tasks.no_more_work_for_project)
return self.do_authenticated_request(f, False)
@cherrypy.expose
def allow_more_work(self, **kwargs):
f = self.project_operation(kwargs, self.__project_tasks.allow_more_work_for_project)
return self.do_authenticated_request(f, False)
@cherrypy.expose
def suspend_project(self, **kwargs):
f = lambda: self.project_operation(kwargs, self.__project_tasks.suspend_project)
return self.do_authenticated_request(f, False)
@cherrypy.expose
def resume_project(self, **kwargs):
f = lambda: self.project_operation(kwargs, self.__project_tasks.resume_project)
return self.do_authenticated_request(f, False)
@cherrypy.expose
def update_project(self, **kwargs):
f = lambda: self.project_operation(kwargs, self.__project_tasks.update_project)
return self.do_authenticated_request(f, False)
def project_operation(self, kwargs, operation_function):
project_url = kwargs.get('projectUrl', '')
return operation_function(project_url)
@cherrypy.expose
def add_user_json(self, **kwargs):
def f():
user_id = kwargs.get('userId', '')
password = kwargs.get('password', '')
result = self.__user_tasks.add_user(user_id, password)
return result
return self.do_authenticated_request(f, False)
@cherrypy.expose
def get_users_json(self, **kwargs):
return self.do_authenticated_request(self.__user_tasks.get_users)
"""
Run a function if the user is authenticated, or if authentication is turned off
Parameters:
func: The function to potentially run
as_list: Whether the result of func should be returned in a list
json_encoder: The JSON encoder to run on the result of func. By default this is a generic JSON encoder.
Returns:
The JSON-encoded result of running func if it is run. If not then an instance of SuccessError that indicates that
authentication failed. If func runs and returns None, then None is returned.
"""
def do_authenticated_request(self, func, as_list=True, json_encoder=jsae.JSONEncoder):
authentication_result = self.authenticate(as_list)
if authentication_result is not None and conf.authentication_enabled:
return json.dumps(authentication_result, cls=jsae.JSONEncoder)
result = list(func()) if as_list else func()
if result is not None:
return json.dumps(result, cls=json_encoder)
@cherrypy.expose
def delete_user_json(self, **kwargs):
def f():
user_no = kwargs.get('userNo', '')
user_id = kwargs.get('userId', '')
row_no = kwargs.get('rowNo', '')
result = self.__user_tasks.delete_user(user_no)
# We add user_id and row_no back in here because I don't want such front-end concerns in UserTasks.
result.error_message += '|{user_id}|{row_no}'.format(user_id=user_id, row_no=row_no)
return result
return self.do_authenticated_request(f, False)
@cherrypy.expose
def change_password_json(self, **kwargs):
def f():
user_no = kwargs.get('userNo', '')
user_id = kwargs.get('userId', '')
password = kwargs.get('password', '')
confirm_password = kwargs.get('confirmPassword', '')
result = self.__user_tasks.change_password(user_no, password, confirm_password)
# We add user_id back in here because I don't want such front-end concerns in UserTasks.
result.error_message += '|{user_id}'.format(user_id=user_id)
return result
return self.do_authenticated_request(f, False)
@cherrypy.expose
def login_json(self, **kwargs):
username = kwargs.get('username', '')
password = kwargs.get('password', '')
result = self.__user_tasks.login(username, password)
if result.success==True:
cherrypy.session['LoggedIn'] = 1
return json.dumps(result, cls=jsae.JSONEncoder)
@cherrypy.expose
def logout(self, **kwargs):
cherrypy.session['LoggedIn'] = None
raise cherrypy.HTTPRedirect('/')
@cherrypy.expose
def zoetrope_status(self, **kwargs):
s = status.Status()
s.logged_in = cherrypy.session.get('LoggedIn', 0) == 1
return json.dumps(s, cls=jsae.JSONEncoder)
@cherrypy.expose
def experimental_task(self, **kwargs):
pass
"""
Check if the user is authenticated
The check is performed by checking the session for a 'LoggedIn' entry.
Parameters:
as_list: Whether or not the return value should be contained in a list
Returns:
If the user is not authenticated then an instance of SuccessError is returned to indicate that the user is not
logged in. If the user is authenticated then None is returned.
"""
def authenticate(self, as_list):
ret_val = se.SuccessError()
if cherrypy.session.get('LoggedIn', 0) != 1:
ret_val.success = False
ret_val.error_message = -1414
ret_val = [ret_val] if as_list else ret_val
return ret_val
if __name__=='__main__':
cwd = os.getcwd()
from cherrypy.process.plugins import Daemonizer
if conf.daemonize:
d = Daemonizer(cherrypy.engine)
d.subscribe()
ws = WebServer()
ws.start(cwd)