]>
git.ipfire.org Git - pbs.git/blob - src/web/mirrors.py
12 log
= logging
.getLogger("pbs.web.mirrors")
14 class IndexHandler(base
.BaseHandler
):
16 await self
.render("mirrors/index.html", mirrors
=self
.backend
.mirrors
)
19 class ShowHandler(base
.BaseHandler
):
20 async def get(self
, hostname
):
21 mirror
= await self
.backend
.mirrors
.get_by_hostname(hostname
)
23 raise tornado
.web
.HTTPError(404, "Could not find mirror %s" % hostname
)
25 await self
.render("mirrors/show.html", mirror
=mirror
)
28 class CheckHandler(base
.BaseHandler
):
30 async def post(self
, hostname
):
31 mirror
= await self
.backend
.mirrors
.get_by_hostname(hostname
)
33 raise tornado
.web
.HTTPError(404, "Could not find mirror %s" % hostname
)
36 if not mirror
.has_perm(self
.current_user
):
37 raise tornado
.web
.HTTPError(403, "%s has no permission for %s" \
38 % (self
.current_user
, mirror
))
41 await mirror
.check(force
=True)
43 # Redirect back to the mirror
44 self
.redirect("/mirrors/%s" % mirror
.hostname
)
47 class CreateHandler(base
.AdminHandler
):
50 await self
.render("mirrors/edit.html", mirror
=None)
55 mirror
= await self
.backend
.mirrors
.create(
56 hostname
= self
.get_argument("hostname"),
57 path
= self
.get_argument("path"),
58 owner
= self
.get_argument("owner"),
59 contact
= self
.get_argument("contact"),
60 notes
= self
.get_argument("notes", ""),
61 user
= self
.current_user
,
64 # Redirect the user back
65 self
.redirect("/mirrors/%s" % mirror
.hostname
)
68 class EditHandler(base
.BaseHandler
):
70 async def get(self
, hostname
):
71 mirror
= await self
.backend
.mirrors
.get_by_hostname(hostname
)
73 raise tornado
.web
.HTTPError(404, "Could not find mirror %s" % hostname
)
76 if not mirror
.has_perm(self
.current_user
):
77 raise tornado
.web
.HTTPError(403)
79 await self
.render("mirrors/edit.html", mirror
=mirror
)
82 async def post(self
, hostname
):
83 mirror
= await self
.backend
.mirrors
.get_by_hostname(hostname
)
85 raise tornado
.web
.HTTPError(404, "Could not find mirror %s" % hostname
)
88 if not mirror
.has_perm(self
.current_user
):
89 raise tornado
.web
.HTTPError(403)
92 mirror
.owner
= self
.get_argument("owner")
93 mirror
.contact
= self
.get_argument("contact")
94 mirror
.notes
= self
.get_argument("notes", None)
96 self
.redirect("/mirrors/%s" % mirror
.hostname
)
99 class DeleteHandler(base
.BaseHandler
):
101 async def get(self
, hostname
):
102 mirror
= await self
.backend
.mirrors
.get_by_hostname(hostname
)
104 raise tornado
.web
.HTTPError(404, "Could not find mirror %s" % hostname
)
107 if not mirror
.has_perm(self
.current_user
):
108 raise tornado
.web
.HTTPError(403)
110 await self
.render("mirrors/delete.html", mirror
=mirror
)
113 async def post(self
, hostname
):
114 mirror
= await self
.backend
.mirrors
.get_by_hostname(hostname
)
116 raise tornado
.web
.HTTPError(404, "Could not find mirror %s" % hostname
)
119 if not mirror
.has_perm(self
.current_user
):
120 raise tornado
.web
.HTTPError(403)
123 await mirror
.delete(deleted_by
=self
.current_user
)
125 # Redirect back to all mirrors
126 self
.redirect("/mirrors")
129 class DownloadsHandler(base
.BaseHandler
):
131 A universal download redirector which does not need to keep any state.
133 It will check if the mirror serves the file and redirect the client. We are
134 starting with the closest mirror and walk through all of the until we have
135 found the right file.
137 As a last resort, we will try to serve the file locally.
140 # Don't send any Content-Type header
141 self
.clear_header("Content-Type")
143 @base.ratelimit(limit
=100, minutes
=60, key
="downloads")
144 async def get(self
, path
):
145 # Check if the file exists
146 if not await self
.backend
.stat(path
, stat
.S_IFREG
):
147 raise tornado
.web
.HTTPError(404)
149 # Tell the clients to never cache the redirect
150 self
.set_header("Cache-Control", "no-store")
152 # Fetch all mirrors for this client
153 mirrors
= await self
.backend
.mirrors
.get_mirrors_for_address(self
.current_address
)
155 # Walk through all mirrors
156 for mirror
in mirrors
:
157 # Don't send clients to a mirror they don't support
158 if isinstance(self
.current_address
, ipaddress
.IPv6Address
):
159 if not mirror
.supports_ipv6():
161 elif isinstance(self
.current_address
, ipaddress
.IPv4Address
):
162 if not mirror
.supports_ipv4():
165 # Skip the mirror if it does not serve the file we are looking for
166 if not await mirror
.serves_file(path
):
170 log
.info("Sending %s to download %s from %s", self
.current_address
, path
, mirror
)
173 url
= mirror
.make_url(path
)
176 return self
.redirect(url
)
178 # If we go here, we did not find any working mirror.
179 # We will send the user to our local file store and hopefully the right
180 # file will be there. If not, the client will receive 404 from there.
181 url
= self
.backend
.path_to_url(path
, mirrored
=False)
185 @base.ratelimit(limit
=100, minutes
=60, key
="downloads")
186 async def head(self
, path
):
188 s
= await self
.backend
.stat(path
, stat
.S_IFREG
)
190 # Send 404 if the file does not exist
192 raise tornado
.web
.HTTPError(404)
194 # Fetch the MIME type
195 mimetype
= await self
.backend
.mimetype(path
)
197 # Send a couple of headers
198 self
.set_header("Content-Type", mimetype
or "application/octet-stream")
199 self
.set_header("Content-Length", s
.st_size
)
200 self
.set_header("Last-Modified", datetime
.datetime
.fromtimestamp(s
.st_mtime
))
201 self
.set_header("Etag", "%x-%x" % (int(s
.st_mtime
), s
.st_size
))
203 async def write_error(self
, *args
, **kwargs
):
205 Don't send any body in error responses