From a3700cc582342d013efbec2d63a03e68e54ac1f9 Mon Sep 17 00:00:00 2001 From: Brian Warner Date: Mon, 4 Dec 2006 04:06:09 -0700 Subject: [PATCH] add a (read-only) web frontend. Put a 'webport' file in your base directory to activate it. --- allmydata/client.py | 10 +++ allmydata/download.py | 4 + allmydata/web/directory.xhtml | 36 +++++++++ allmydata/web/welcome.xhtml | 15 ++++ allmydata/webish.py | 145 ++++++++++++++++++++++++++++++++++ roadmap.txt | 3 + 6 files changed, 213 insertions(+) create mode 100644 allmydata/web/directory.xhtml create mode 100644 allmydata/web/welcome.xhtml create mode 100644 allmydata/webish.py diff --git a/allmydata/client.py b/allmydata/client.py index 8afc3ac2..f0ecd6e6 100644 --- a/allmydata/client.py +++ b/allmydata/client.py @@ -14,6 +14,7 @@ from allmydata.storageserver import StorageServer from allmydata.upload import Uploader from allmydata.download import Downloader from allmydata.vdrive import VDrive +from allmydata.webish import WebishServer class Client(node.Node, Referenceable): implements(RIClient) @@ -21,6 +22,7 @@ class Client(node.Node, Referenceable): PORTNUMFILE = "client.port" STOREDIR = 'storage' NODETYPE = "client" + WEBPORTFILE = "webport" def __init__(self, basedir="."): node.Node.__init__(self, basedir) @@ -31,6 +33,12 @@ class Client(node.Node, Referenceable): self.add_service(Uploader()) self.add_service(Downloader()) self.add_service(VDrive()) + WEBPORTFILE = os.path.join(self.basedir, self.WEBPORTFILE) + if os.path.exists(WEBPORTFILE): + f = open(WEBPORTFILE, "r") + webport = int(f.read()) + f.close() + self.add_service(WebishServer(webport)) self.queen_pburl = None self.queen_connector = None @@ -73,6 +81,8 @@ class Client(node.Node, Referenceable): def _got_vdrive_root(self, root): self.getServiceNamed("vdrive").set_root(root) + if "webish" in self.namedServices: + self.getServiceNamed("webish").set_root_dirnode(root) def _lost_queen(self): self.log("lost connection to queen") diff --git a/allmydata/download.py b/allmydata/download.py index 58b8b4b2..a344d039 100644 --- a/allmydata/download.py +++ b/allmydata/download.py @@ -195,10 +195,14 @@ class FileHandle: def finish(self): pass +class IDownloader(Interface): + def download(verifierid, target): + pass class Downloader(service.MultiService): """I am a service that allows file downloading. """ + implements(IDownloader) name = "downloader" def download(self, verifierid, t): diff --git a/allmydata/web/directory.xhtml b/allmydata/web/directory.xhtml new file mode 100644 index 00000000..1e68ca3d --- /dev/null +++ b/allmydata/web/directory.xhtml @@ -0,0 +1,36 @@ + + + + + + + + +

+ +
Refresh this view
+
Parent Directory
+ + + + + + + + + + + + + + + +
FilenameTypefileid
directory is empty!
+ + + + + + + diff --git a/allmydata/web/welcome.xhtml b/allmydata/web/welcome.xhtml new file mode 100644 index 00000000..8ff2a8ba --- /dev/null +++ b/allmydata/web/welcome.xhtml @@ -0,0 +1,15 @@ + + + Welcome To AllMyData (tahoe2) + + + + + +

Welcome To AllMyData!

+ +

To view the global shared filestore, Click Here!

+ + + diff --git a/allmydata/webish.py b/allmydata/webish.py new file mode 100644 index 00000000..499f4051 --- /dev/null +++ b/allmydata/webish.py @@ -0,0 +1,145 @@ + +from twisted.application import service, internet +from twisted.web import static, resource, server +from twisted.python import util +from nevow import inevow, rend, loaders, appserver, tags as T +from allmydata.util import idlib +from allmydata.download import IDownloadTarget#, IDownloader +from zope.interface import implements +import urllib + +def getxmlfile(name): + return loaders.xmlfile(util.sibpath(__file__, "web/%s" % name)) + +class WebishServer(service.MultiService): + name = "webish" + WEBPORTFILE = "webport" + + def __init__(self, webport): + service.MultiService.__init__(self) + self.root = root = static.Data("root", "text/plain") + w = Welcome() + root.putChild("", w) + root.putChild("vdrive", + static.Data("sorry, still initializing", "text/plain")) + self.site = site = appserver.NevowSite(root) + internet.TCPServer(webport, site).setServiceParent(self) + + def set_root_dirnode(self, dirnode): + dl = self.parent.getServiceNamed("downloader") + self.root.putChild("vdrive", Directory(dirnode, "/", dl)) + #print "REMEMBERING", self.site, dl, IDownloader + #self.site.remember(dl, IDownloader) + + +class Welcome(rend.Page): + addSlash = True + docFactory = getxmlfile("welcome.xhtml") + +class Directory(rend.Page): + addSlash = True + docFactory = getxmlfile("directory.xhtml") + + def __init__(self, dirnode, dirname, downloader): + self._dirnode = dirnode + self._dirname = dirname + self._downloader = downloader + + def childFactory(self, ctx, name): + if name.startswith("freeform"): # ick + return None + if name == "_download": + args = inevow.IRequest(ctx).args + filename = args["filename"][0] + verifierid = args["verifierid"][0] + return Downloader(self._downloader, + self._dirname, filename, idlib.a2b(verifierid)) + if self._dirname == "/": + dirname = "/" + name + else: + dirname = self._dirname + "/" + name + d = self._dirnode.callRemote("get", name) + d.addCallback(lambda newnode: + Directory(newnode, dirname, self._downloader)) + return d + + def render_title(self, ctx, data): + return ctx.tag["Directory of '%s':" % self._dirname] + + def render_header(self, ctx, data): + return "Directory of '%s':" % self._dirname + + def data_children(self, ctx, data): + d = self._dirnode.callRemote("list") + return d + + def render_row(self, ctx, data): + name, target = data + if isinstance(target, str): + # file + args = {'verifierid': idlib.b2a(target), + 'filename': name, + } + dlurl = "_download?%s" % urllib.urlencode(args) + ctx.fillSlots("filename", T.a(href=dlurl)[name]) + ctx.fillSlots("type", "FILE") + ctx.fillSlots("fileid", idlib.b2a(target)) + else: + # directory + ctx.fillSlots("filename", T.a(href=name)[name]) + ctx.fillSlots("type", "DIR") + ctx.fillSlots("fileid", "-") + return ctx.tag + +class WebDownloadTarget: + implements(IDownloadTarget) + def __init__(self, req): + self._req = req + def open(self): + pass + def write(self, data): + self._req.write(data) + def close(self): + self._req.finish() + def fail(self): + self._req.finish() + def register_canceller(self, cb): + pass + def finish(self): + pass + +class TypedFile(static.File): + # serve data from a named file, but using a Content-Type derived from a + # different filename + isLeaf = True + def __init__(self, path, requested_filename): + static.File.__init__(self, path) + gte = static.getTypeAndEncoding + self.type, self.encoding = gte(requested_filename, + self.contentTypes, + self.contentEncodings, + self.defaultType) + +class Downloader(resource.Resource): + def __init__(self, downloader, dirname, name, verifierid): + self._downloader = downloader + self._dirname = dirname + self._name = name + self._verifierid = verifierid + + def render(self, ctx): + req = inevow.IRequest(ctx) + gte = static.getTypeAndEncoding + type, encoding = gte(self._name, + static.File.contentTypes, + static.File.contentEncodings, + defaultType="text/plain") + req.setHeader("content-type", type) + if encoding: + req.setHeader('content-encoding', encoding) + + t = WebDownloadTarget(req) + #dl = IDownloader(ctx) + dl = self._downloader + dl.download(self._verifierid, t) + return server.NOT_DONE_YET diff --git a/roadmap.txt b/roadmap.txt index 14ee9d64..0f595eaf 100644 --- a/roadmap.txt +++ b/roadmap.txt @@ -28,6 +28,9 @@ storage: RobK leases never expire v2: leases expire, delete expired data on demand, multiple owners per share +UI: + webish? webfront? PB + CLI tool? FUSE? + v1: back pocket ideas: when nodes are unable to reach storage servers, make a note of it, inform -- 2.45.2