]> git.ipfire.org Git - pbs.git/blob - src/web/__init__.py
repo: Add simple API operations
[pbs.git] / src / web / __init__.py
1 #!/usr/bin/python
2 # encoding: utf-8
3
4 import datetime
5 import logging
6 import tornado.locale
7 import tornado.web
8 import urllib.parse
9
10 from .. import Backend
11 from ..constants import *
12 from .. import misc
13
14 # Import all handlers
15 from . import auth
16 from . import bugs
17 from . import builders
18 from . import builds
19 from . import distributions
20 from . import errors
21 from . import events
22 from . import jobs
23 from . import mirrors
24 from . import monitorings
25 from . import packages
26 from . import repos
27 from . import search
28 from . import sources
29 from . import uploads
30 from . import users
31 from .handlers import *
32
33 from . import ui_modules
34
35 class Application(tornado.web.Application):
36 def __init__(self, **kwargs):
37 settings = dict(
38 login_url = "/login",
39 template_path = TEMPLATESDIR,
40 static_path = STATICDIR,
41 ui_modules = {
42 "Highlight" : ui_modules.HighlightModule,
43 "Text" : ui_modules.TextModule,
44
45 # Bugs
46 "BugsList" : bugs.ListModule,
47
48 # Builds
49 "BuildsList" : builds.ListModule,
50 "BuildWatchers" : builds.WatchersModule,
51
52 # BuildGroups
53 "BuildGroupList" : builds.GroupListModule,
54
55 # Builders
56 "BuilderStats" : builders.StatsModule,
57
58 # Distros
59 "DistrosList" : distributions.ListModule,
60
61 # Events
62 "EventsList" : events.ListModule,
63 "EventBuildComment" : events.BuildCommentModule,
64 "EventSystemMessage" : events.SystemMessageModule,
65 "EventUserMessage" : events.UserMessageModule,
66
67 # Jobs
68 "JobsList" : jobs.ListModule,
69 "JobsLogStream" : jobs.LogStreamModule,
70 "JobsQueue" : jobs.QueueModule,
71
72 # Mirrors
73 "MirrorsList" : mirrors.ListModule,
74
75 # Monitorings
76 "MonitoringsReleasesList" : monitorings.ReleasesListModule,
77
78 # Packages
79 "PackageInfo" : packages.InfoModule,
80 "PackageDependencies": packages.DependenciesModule,
81
82 # Releases
83 "ReleasesList" : distributions.ReleasesListModule,
84
85 # Repositories
86 "ReposList" : repos.ListModule,
87
88 # Sources
89 "SourcesList" : sources.ListModule,
90 "CommitsList" : sources.CommitsListModule,
91
92 # Users
93 "UsersList" : users.ListModule,
94 "UserPushSubscribeButton" : users.PushSubscribeButton,
95
96 "CommitMessage" : ui_modules.CommitMessageModule,
97 "LinkToUser" : ui_modules.LinkToUserModule,
98 "PackageFilesTable" : ui_modules.PackageFilesTableModule,
99 },
100 ui_methods = {
101 "extract_hostname" : self.extract_hostname,
102 "format_time" : self.format_time,
103 "group" : self.group,
104 "make_url" : self.make_url,
105 },
106 xsrf_cookies = True,
107 )
108 settings.update(kwargs)
109
110 # Load translations.
111 tornado.locale.load_gettext_translations(LOCALEDIR, PACKAGE_NAME)
112
113 tornado.web.Application.__init__(self, [
114 # Entry site that lead the user to index
115 (r"/", IndexHandler),
116
117 # Authentication
118 (r"/login", auth.LoginHandler),
119 (r"/logout", auth.LogoutHandler),
120
121 # Users
122 (r"/users", users.IndexHandler),
123 (r"/users/(\w+)", users.ShowHandler),
124 (r"/users/(\w+)/delete", users.DeleteHandler),
125 (r"/users/(\w+)/edit", users.EditHandler),
126 (r"/users/push/subscribe", users.PushSubscribeHandler),
127
128 # User Repositories
129 (r"/users/(\w+)/repos/create", repos.CreateCustomHandler),
130 (r"/users/(?P<user_slug>\w+)/repos/(?P<distro_slug>[A-Za-z0-9\-\.]+)/(?P<repo_slug>[A-Za-z0-9\-]+)",
131 repos.ShowHandler),
132 (r"/users/(?P<user_slug>\w+)/repos/(?P<distro_slug>[A-Za-z0-9\-\.]+)/(?P<repo_slug>[A-Za-z0-9\-]+)\.repo",
133 repos.ConfigHandler),
134 (r"/users/(?P<user_slug>\w+)/repos/(?P<distro_slug>[A-Za-z0-9\-\.]+)/(?P<repo_slug>[A-Za-z0-9\-]+)/builds",
135 repos.BuildsHandler),
136 (r"/users/(?P<user_slug>\w+)/repos/(?P<distro_slug>[A-Za-z0-9\-\.]+)/(?P<repo_slug>[A-Za-z0-9\-]+)/delete",
137 repos.DeleteHandler),
138 (r"/users/(?P<user_slug>\w+)/repos/(?P<distro_slug>[A-Za-z0-9\-\.]+)/(?P<repo_slug>[A-Za-z0-9\-]+)/edit",
139 repos.EditHandler),
140 (r"/users/(?P<user_slug>\w+)/repos/(?P<distro_slug>[A-Za-z0-9\-\.]+)/(?P<repo_slug>[A-Za-z0-9\-]+)/mirrorlist",
141 repos.MirrorlistHandler),
142 (r"/users/(?P<user_slug>\w+)/repos/(?P<distro_slug>[A-Za-z0-9\-\.]+)/(?P<repo_slug>[A-Za-z0-9\-]+)/sources/(?P<source_slug>[A-Za-z0-9\-]+)",
143 sources.ShowHandler),
144 (r"/users/(?P<user_slug>\w+)/repos/(?P<distro_slug>[A-Za-z0-9\-\.]+)/(?P<repo_slug>[A-Za-z0-9\-]+)/sources/(?P<source_slug>[A-Za-z0-9\-]+)/commits/(?P<commit_slug>[a-f0-9]{40})",
145 sources.ShowCommitHandler),
146
147 # Packages
148 (r"/packages", packages.IndexHandler),
149 (r"/packages/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})", packages.ShowHandler),
150 (r"/packages/([\w\-\+]+)", packages.NameHandler),
151 (r"/packages/([\w\-\+]+)/builds", packages.NameBuildsHandler),
152 (r"/packages/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/download(.*)",
153 packages.FileDownloadHandler),
154 (r"/packages/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/view(.*)",
155 packages.FileViewHandler),
156
157 # Builds
158 (r"/builds", builds.IndexHandler),
159 (r"/builds/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})", builds.ShowHandler),
160 (r"/builds/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/bug", builds.BugHandler),
161 (r"/builds/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/clone", builds.CloneHandler),
162 (r"/builds/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/comment", builds.CommentHandler),
163 (r"/builds/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/delete", builds.DeleteHandler),
164 (r"/builds/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/repos/add", builds.ReposAddHandler),
165 (r"/builds/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/repos/remove", builds.ReposRemoveHandler),
166 (r"/builds/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/watch", builds.WatchHandler),
167 (r"/builds/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/unwatch", builds.UnwatchHandler),
168
169 (r"/api/v1/builds", builds.APIv1IndexHandler),
170
171 # Build Groups
172 (r"/builds/groups/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})", builds.GroupShowHandler),
173
174 # Jobs
175 (r"/jobs", jobs.IndexHandler),
176 (r"/jobs/queue", jobs.QueueHandler),
177 (r"/jobs/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/abort", jobs.AbortHandler),
178 (r"/jobs/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/log", jobs.LogHandler),
179 (r"/jobs/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/retry", jobs.RetryHandler),
180 (r"/api/v1/jobs/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})",
181 jobs.APIv1ControlHandler),
182 (r"/api/v1/jobs/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/finished",
183 jobs.APIv1FinishedHandler),
184 (r"/api/v1/jobs/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/log/stream",
185 jobs.APIv1LogStreamHandler),
186
187 # Builders
188 (r"/builders", builders.IndexHandler),
189 (r"/builders/create", builders.CreateHandler),
190 (r"/builders/([A-Za-z0-9\-\.]+)", builders.ShowHandler),
191 (r"/builders/([A-Za-z0-9\-\.]+)/delete", builders.DeleteHandler),
192 (r"/builders/([A-Za-z0-9\-\.]+)/edit", builders.BuilderEditHandler),
193 (r"/builders/([A-Za-z0-9\-\.]+)/start", builders.StartHandler),
194 (r"/builders/([A-Za-z0-9\-\.]+)/stats", builders.StatsHandler),
195 (r"/builders/([A-Za-z0-9\-\.]+)/stop", builders.StopHandler),
196 (r"/api/v1/builders/control", builders.APIv1ControlHandler),
197
198 # Distributions
199 (r"/distros", distributions.IndexHandler),
200 (r"/distros/([A-Za-z0-9\-\.]+)", distributions.ShowHandler),
201 (r"/distros/([A-Za-z0-9\-\.]+)/edit", distributions.EditHandler),
202 (r"/distros/(?P<distro_slug>[A-Za-z0-9\-\.]+)/repos/(?P<repo_slug>[A-Za-z0-9\-]+)",
203 repos.ShowHandler),
204 (r"/distros/(?P<distro_slug>[A-Za-z0-9\-\.]+)/repos/(?P<repo_slug>[A-Za-z0-9\-]+)\.repo",
205 repos.ConfigHandler),
206 (r"/distros/(?P<distro_slug>[A-Za-z0-9\-\.]+)/repos/(?P<repo_slug>[A-Za-z0-9\-]+)/builds",
207 repos.BuildsHandler),
208 (r"/distros/(?P<distro_slug>[A-Za-z0-9\-\.]+)/repos/(?P<repo_slug>[A-Za-z0-9\-]+)/delete",
209 repos.DeleteHandler),
210 (r"/distros/(?P<distro_slug>[A-Za-z0-9\-\.]+)/repos/(?P<repo_slug>[A-Za-z0-9\-]+)/edit",
211 repos.EditHandler),
212 (r"/distros/(?P<distro_slug>[A-Za-z0-9\-\.]+)/repos/(?P<repo_slug>[A-Za-z0-9\-]+)/mirrorlist",
213 repos.MirrorlistHandler),
214 (r"/distros/(?P<distro_slug>[A-Za-z0-9\-\.]+)/repos/(?P<repo_slug>[A-Za-z0-9\-]+)/sources/(?P<source_slug>[A-Za-z0-9\-]+)",
215 sources.ShowHandler),
216 (r"/distros/(?P<distro_slug>[A-Za-z0-9\-\.]+)/repos/(?P<repo_slug>[A-Za-z0-9\-]+)/sources/(?P<source_slug>[A-Za-z0-9\-]+)/commits/(?P<commit_slug>[a-f0-9]{40})",
217 sources.ShowCommitHandler),
218
219 # Distro Monitorings
220 (r"/distros/([A-Za-z0-9\-\.]+)/monitorings/([\w\-_]+)", monitorings.ShowHandler),
221 (r"/distros/([A-Za-z0-9\-\.]+)/monitorings/([\w\-_]+)/check", monitorings.CheckHandler),
222 (r"/distros/([A-Za-z0-9\-\.]+)/monitorings/([\w\-_]+)/create", monitorings.CreateHandler),
223 (r"/distros/([A-Za-z0-9\-\.]+)/monitorings/([\w\-_]+)/delete", monitorings.DeleteHandler),
224 (r"/distros/([A-Za-z0-9\-\.]+)/monitorings/([\w\-_]+)/edit", monitorings.EditHandler),
225
226 # Distro Releases
227 (r"/distros/([A-Za-z0-9\-\.]+)/releases", distributions.ReleasesIndexHandler),
228 (r"/distros/([A-Za-z0-9\-\.]+)/releases/create", distributions.ReleasesCreateHandler),
229 (r"/distros/([A-Za-z0-9\-\.]+)/releases/([\w\-_]+)", distributions.ReleasesShowHandler),
230 (r"/distros/([A-Za-z0-9\-\.]+)/releases/([\w\-_]+)/delete", distributions.ReleasesDeleteHandler),
231 (r"/distros/([A-Za-z0-9\-\.]+)/releases/([\w\-_]+)/edit", distributions.ReleasesEditHandler),
232 (r"/distros/([A-Za-z0-9\-\.]+)/releases/([\w\-_]+)/publish", distributions.ReleasesPublishHandler),
233
234 # Mirrors
235 (r"/mirrors", mirrors.IndexHandler),
236 (r"/mirrors/create", mirrors.CreateHandler),
237 (r"/mirrors/([\w\-\.]+)", mirrors.ShowHandler),
238 (r"/mirrors/([\w\-\.]+)/check", mirrors.CheckHandler),
239 (r"/mirrors/([\w\-\.]+)/delete", mirrors.DeleteHandler),
240 (r"/mirrors/([\w\-\.]+)/edit", mirrors.EditHandler),
241
242 # Search
243 (r"/search", search.SearchHandler),
244
245 # Log
246 (r"/log", handlers.LogHandler),
247
248 # Repositories
249 (r"/api/v1/repos/([\w\d\-]+)", repos.APIv1IndexHandler),
250 (r"/api/v1/repos/([\w\d\-]+)/([\w\d\-]+)", repos.APIv1ShowHandler),
251
252 # Uploads
253 (r"/api/v1/uploads", uploads.APIv1IndexHandler),
254 (r"/api/v1/uploads/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})",
255 uploads.APIv1DetailHandler),
256 ], default_handler_class=errors.Error404Handler, **settings)
257
258 # Launch backend
259 self.backend = Backend("/etc/pakfire/pbs.conf")
260
261 logging.info("Successfully initialied application")
262
263 # Launch some initial tasks
264 self.backend.run_task(self.backend.users.generate_vapid_keys)
265 self.backend.run_task(self.backend.builders.autoscale)
266
267 # Regularly check the mirrors
268 self.backend.run_periodic_task(300, self.backend.mirrors.check)
269
270 # Regularly fetch sources
271 self.backend.run_periodic_task(300, self.backend.sources.fetch)
272
273 # Regularly check for new releases
274 self.backend.run_periodic_task(300, self.backend.monitorings.check)
275
276 ## UI methods
277
278 def extract_hostname(self, handler, url):
279 url = urllib.parse.urlparse(url)
280
281 return url.hostname
282
283 def format_time(self, handler, s, shorter=False):
284 _ = handler.locale.translate
285
286 if isinstance(s, datetime.timedelta):
287 s = s.total_seconds()
288
289 args = {
290 "s" : round(s % 60),
291 "m" : round(s / 60 % 60),
292 "h" : round(s / 3600 % 3600),
293 "d" : round(s / 86400),
294 }
295
296 # Less than one minute
297 if s < 60:
298 return _("%(s)d s") % args
299
300 # Less than one hour
301 elif s < 3600:
302 return _("%(m)d:%(s)02d m") % args
303
304 # Less than one day
305 elif s < 86400:
306 return _("%(h)d:%(m)02d h") % args
307
308 # More than one day
309 else:
310 return _("%(d)d:%(h)02d d") % args
311
312 def group(self, handler, *args, **kwargs):
313 return misc.group(*args, **kwargs)
314
315 def make_url(self, handler, url, **kwargs):
316 # Format any query arguments and append them to the URL
317 if kwargs:
318 # Filter out None
319 args = { k : kwargs[k] for k in kwargs if kwargs[k] is not None }
320
321 # Encode into URL format
322 args = urllib.parse.urlencode(args)
323
324 url = "%s?%s" % (url, args)
325
326 return url