This repository has been archived by the owner on Nov 30, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path__main__.py
203 lines (163 loc) · 4.74 KB
/
__main__.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
#!/usr/bin/env python
import sys
if sys.version_info < (2, 5):
print "Sorry, requires Python 2.5, 2.6 or 2.7."
sys.exit(1)
import json, bottle
import sys, os
import utilities
import urllib
# Child APIs
from api import API
from tellstick import TellstickAPI
from config import ConfigAPI
from scheduler import Scheduler
from schedulerApi import SchedulerAPI
# Resources
from configobj import ConfigObj
from bottle import template, request
from configObserver import ConfigObserver
from validate import Validator
from beaker.middleware import SessionMiddleware
from werkzeug.security import check_password_hash
# Constants
VERSION = '0.28'
CONFIG_PATH = 'config.ini'
CONFIG_SPEC = 'configspec.ini'
validator = Validator()
config = ConfigObserver(CONFIG_PATH, configspec = CONFIG_SPEC)
config.setValidator(validator)
bottle.TEMPLATE_PATH.insert(0, utilities.full_path('/views'))
app = bottle.Bottle()
api = None
def main():
global api
root_app = bottle.Bottle()
result = config.validate(validator, copy = True)
if result is False:
print "Config file validation failed"
sys.exit(1)
api = API(app, config, VERSION)
ConfigAPI(api, config)
tellstick = TellstickAPI(api, config, VERSION)
scheduler = Scheduler(config, tellstick)
SchedulerAPI(api, config)
class ConfigWatcher(object):
def notify(self, observable, key):
print "writing"
config.write()
watcher = ConfigWatcher()
config.observe(watcher)
if config['installed'] != VERSION:
api.install()
if config['webroot']:
root_app.mount(config['webroot'], app)
else:
root_app.merge(app)
session_opts = {
'session.type': 'cookie',
'session.validate_key': config['cookieKey'],
'session.auto': True,
}
bottle.run(SessionMiddleware(root_app, session_opts),
host = config['host'],
port = config['port'],
debug = config['debug'],
reloader = False,
server = 'cherrypy')
if scheduler:
scheduler.stop()
# The goal was to have all changes to the config call the watcher
# method which writes to file. Unfortunately subsections wouldn't
# play along, so write to file once we've finished with the service
config.write()
def authenticated(func):
def wrapped(*args, **kwargs):
if config['password']:
try:
beaker_session = request.environ['beaker.session']
except:
redirectRelative('/login')
if beaker_session.get('logged_in', 0):
valid = True
else:
redirectRelative('/login')
return func(*args, **kwargs)
return wrapped
def render_template(view, extra={}):
jsAPI = ''
if config['debug']:
jsAPI = api.generate_jsapi()
vars = {
'apikey' : config['apikey'] or '',
'password' : config['password'],
'debug' : config['debug'],
'jsAPI' : jsAPI
}
vars.update(extra)
return template(view, vars);
@app.route('/')
def home_page():
""" Specific redirect as we cannot redirect relatively if the trailing slash is ommitted """
return """<html><body><script>
window.location.replace(window.location.href.replace(/\/?$/, '/') + 'devices')
</script></body></html>"""
@app.route('/login')
def login():
if config['password']:
return render_template('login')
else:
redirectRelative('/')
def post_get(name, default=''):
return bottle.request.POST.get(name, default).strip()
@app.post('/postlogin')
def post_login():
"""Authenticate users"""
username = post_get('username')
password = post_get('password')
if username == config['username']:
if check_password_hash(config['password'], password):
s = bottle.request.environ.get('beaker.session')
s['logged_in'] = True
redirectRelative('.')
return
redirectRelative('/login')
@app.route('/devices')
@authenticated
def devices():
return render_template('devices')
@app.route('/logout')
def logout():
s = bottle.request.environ['beaker.session']
s['logged_in'] = False
redirectRelative('/login')
@app.route('/api')
@authenticated
def api():
return render_template('api', {'outputFormat': config['outputFormat']})
@app.route('/config')
@authenticated
def home_page():
return render_template('config')
@app.route('/scheduler')
@authenticated
def scheduler():
return render_template('scheduler')
def redirectRelative(url, code=None):
""" Aborts execution and causes a 303 or 302 redirect, depending on
the HTTP protocol version. """
if code is None:
code = 303 if request.get('SERVER_PROTOCOL') == "HTTP/1.1" else 302
if config['webroot']:
url = config['webroot'] + url
if len(url) > 1:
url = url.rstrip('/').lstrip('/')
res = bottle.HTTPResponse("", status=code, Location=url)
if bottle.response._cookies:
res._cookies = response._cookies
raise res
@app.route('/static/<filepath:path>')
def server_static(filepath='index.html'):
return bottle.static_file(filepath, root=utilities.full_path('/static'))
if __name__ == "__main__":
main()