version 0.2.1, better browser open, site size limit, save number of peers to sites.json to faster warmup, silent wsgihandler error, siteSetLimit API comment, grant ADMIN permissions to wrapper, display site changetime from includes too, loading screen warning support

This commit is contained in:
HelloZeroNet 2015-02-14 14:05:00 +01:00
parent 882577968a
commit fa7164a0f0
14 changed files with 243 additions and 48 deletions

View File

@ -15,7 +15,7 @@ Decentralized websites using Bitcoin crypto and BitTorrent network
- After starting `zeronet.py` you will be able to visit zeronet sites using http://127.0.0.1:43110/{zeronet_address} (eg. http://127.0.0.1:43110/1EU1tbG9oC1A8jz2ouVwGZyQ5asrNsE4Vr).
- When you visit a new zeronet site, it's trying to find peers using BitTorrent network and download the site files (html, css, js...) from them.
- Each visited sites become also served by You.
- Every site containing a `site.json` which holds all other files sha1 hash and a sign generated using site's private key.
- Every site containing a `site.json` which holds all other files sha512 hash and a sign generated using site's private key.
- If the site owner (who has the private key for the site address) modifies the site, then he/she signs the new `content.json` and publish it to the peers. After the peers have verified the `content.json` integrity (using the sign), they download the modified files and publish the new content to other peers.

View File

@ -3,7 +3,7 @@ import ConfigParser
class Config(object):
def __init__(self):
self.version = "0.2.0"
self.version = "0.2.1"
self.parser = self.createArguments()
argv = sys.argv[:] # Copy command line arguments
argv = self.parseConfig(argv) # Add arguments from config file
@ -60,7 +60,9 @@ class Config(object):
parser.add_argument('--ui_ip', help='Web interface bind address', default="127.0.0.1", metavar='ip')
parser.add_argument('--ui_port', help='Web interface bind port', default=43110, type=int, metavar='port')
parser.add_argument('--ui_restrict', help='Restrict web access', default=False, metavar='ip')
parser.add_argument('--open_browser', help='Open homepage in web browser automatically', nargs='?', const="default_browser", metavar='browser_name')
parser.add_argument('--homepage', help='Web interface Homepage', default='1EU1tbG9oC1A8jz2ouVwGZyQ5asrNsE4Vr', metavar='address')
parser.add_argument('--size_limit', help='Default site size limit in MB', default=10, metavar='size_limit')
parser.add_argument('--fileserver_ip', help='FileServer bind address', default="*", metavar='ip')
parser.add_argument('--fileserver_port',help='FileServer bind port', default=15441, type=int, metavar='port')

View File

@ -9,6 +9,7 @@ class ContentManager:
self.log = self.site.log
self.contents = {} # Known content.json (without files and includes)
self.loadContent(add_bad_files = False)
self.site.settings["size"] = self.getTotalSize()
# Load content.json to self.content
@ -68,9 +69,24 @@ class ContentManager:
for inner_path in changed:
self.site.bad_files[inner_path] = True
if new_content["modified"] > self.site.settings.get("modified", 0):
self.site.settings["modified"] = new_content["modified"]
return changed
# Get total size of site
# Return: 32819 (size of files in kb)
def getTotalSize(self, ignore=None):
total_size = 0
for inner_path, content in self.contents.iteritems():
if inner_path == ignore: continue
total_size += os.path.getsize(self.site.getPath(inner_path)) # Size of content.json
for file, info in content.get("files", {}).iteritems():
total_size += info["size"]
return total_size
# Find the file info line from self.contents
# Return: { "sha512": "c29d73d30ee8c9c1b5600e8a84447a6de15a3c3db6869aca4a2a578c1721f518", "size": 41 , "content_inner_path": "content.json"}
def getFileInfo(self, inner_path):
@ -216,30 +232,50 @@ class ContentManager:
return 1 # Todo: Multisig
# Checks if the content.json content is valid
# Return: True or False
def validContent(self, inner_path, content):
if inner_path == "content.json": return True # Always ok
content_size = len(json.dumps(content)) + sum([file["size"] for file in content["files"].values()]) # Size of new content
site_size = self.getTotalSize(ignore=inner_path)+content_size # Site size without old content
if site_size > self.site.settings.get("size", 0): self.site.settings["size"] = site_size # Save to settings if larger
site_size_limit = self.site.getSizeLimit()*1024*1024
# Check total site size limit
if site_size > site_size_limit:
self.log.error("%s: Site too large %s > %s, aborting task..." % (inner_path, site_size, site_size_limit))
task = self.site.worker_manager.findTask(inner_path)
if task: # Dont try to download from other peers
self.site.worker_manager.failTask(task)
return False
if inner_path == "content.json": return True # Root content.json is passed
# Load include details
include_info = self.getIncludeInfo(inner_path)
if not include_info:
self.log.error("%s: No include info" % inner_path)
return False
if include_info.get("max_size"): # Size limit
total_size = len(json.dumps(content)) + sum([file["size"] for file in content["files"].values()])
if total_size > include_info["max_size"]:
self.log.error("%s: Too large %s > %s" % (inner_path, total_size, include_info["max_size"]))
# Check include size limit
if include_info.get("max_size"): # Include size limit
if content_size > include_info["max_size"]:
self.log.error("%s: Include too large %s > %s" % (inner_path, total_size, include_info["max_size"]))
return False
# Check if content includes allowed
if include_info.get("includes_allowed") == False and content.get("includes"):
self.log.error("%s: Includes not allowed" % inner_path)
return False # Includes not allowed
if include_info.get("files_allowed"): # Filename limit
# Filename limit
if include_info.get("files_allowed"):
for file_inner_path in content["files"].keys():
if not re.match("^%s$" % include_info["files_allowed"], file_inner_path):
self.log.error("%s: File not allowed: " % file_inner_path)
return False
return True
return True # All good
@ -292,7 +328,7 @@ class ContentManager:
self.log.error("Verify sign error: %s" % Debug.formatException(err))
return False
else: # Check using sha1 hash
else: # Check using sha512 hash
file_info = self.getFileInfo(inner_path)
if file_info:
if "sha512" in file_info:

View File

@ -41,7 +41,9 @@ class FileRequest:
return False
if site.settings["own"] and params["inner_path"].endswith("content.json"):
self.log.debug("Someone trying to push a file to own site %s, reload local %s first" % (site.address, params["inner_path"]))
site.content_manager.loadContent(params["inner_path"])
changed = site.content_manager.loadContent(params["inner_path"], add_bad_files=False)
if changed: # Content.json changed locally
site.settings["size"] = site.content_manager.getTotalSize() # Update site size
buff = StringIO(params["body"])
valid = site.content_manager.verifyFile(params["inner_path"], buff)
if valid == True: # Valid and changed

View File

@ -33,8 +33,8 @@ class Site:
self.notifications = [] # Pending notifications displayed once on page load [error|ok|info, message, timeout]
self.page_requested = False # Page viewed in browser
self.content_manager = ContentManager(self) # Load contents
self.loadSettings() # Load settings from sites.json
self.content_manager = ContentManager(self) # Load contents
if not self.settings.get("auth_key"): # To auth user in site (Obsolete, will be removed)
self.settings["auth_key"] = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(24))
@ -74,6 +74,22 @@ class Site:
return
# Max site size in MB
def getSizeLimit(self):
return self.settings.get("size_limit", config.size_limit)
# Next size limit based on current size
def getNextSizeLimit(self):
size_limits = [10,20,50,100,200,500,1000,2000,5000,10000,20000,50000,100000]
size = self.settings.get("size", 0)
for size_limit in size_limits:
if size*1.2 < size_limit*1024*1024:
return size_limit
return 999999
# Sercurity check and return path of site's file
def getPath(self, inner_path):
inner_path = inner_path.replace("\\", "/") # Windows separator fix
@ -123,10 +139,14 @@ class Site:
# Download all files of the site
@util.Noparallel(blocking=False)
def download(self):
def download(self, check_size=False):
self.log.debug("Start downloading...%s" % self.bad_files)
self.announce()
self.last_downloads = []
if check_size: # Check the size first
valid = downloadContent(download_files=False)
if not valid: return False # Cant download content.jsons or size is not fits
found = self.downloadContent("content.json")
return found
@ -147,6 +167,8 @@ class Site:
if not self.settings["own"]: self.checkFiles(quick_check=True) # Quick check files based on file size
if self.bad_files:
self.download()
self.settings["size"] = self.content_manager.getTotalSize() # Update site size
return changed
@ -266,6 +288,8 @@ class Site:
if added:
self.worker_manager.onPeers()
self.updateWebsocket(peers_added=added)
self.settings["peers"] = len(peers)
self.saveSettings()
self.log.debug("Found %s peers, new: %s" % (len(peers), added))
else:
pass # TODO: http tracker support

View File

@ -43,6 +43,7 @@ def isAddress(address):
# Return site and start download site files
def need(address, all_file=True):
from Site import Site
new = False
if address not in sites: # Site not exits yet
if not isAddress(address): return False # Not address: %s % address
logging.debug("Added new site: %s" % address)
@ -50,6 +51,7 @@ def need(address, all_file=True):
if not sites[address].settings["serving"]: # Maybe it was deleted before
sites[address].settings["serving"] = True
sites[address].saveSettings()
new = True
site = sites[address]
if all_file: site.download()

View File

@ -21,7 +21,10 @@ class UiWSGIHandler(WSGIHandler):
self.ws_handler.run_application()
else: # Standard HTTP request
#print self.application.__class__.__name__
return super(UiWSGIHandler, self).run_application()
try:
return super(UiWSGIHandler, self).run_application()
except Exception, err:
logging.debug("UiWSGIHandler error: %s" % err)
class UiServer:
@ -77,5 +80,13 @@ class UiServer:
self.log.info("Web interface: http://%s:%s/" % (config.ui_ip, config.ui_port))
self.log.info("--------------------------------------")
if config.open_browser:
logging.info("Opening browser: %s...", config.open_browser)
import webbrowser
if config.open_browser == "default_browser":
browser = webbrowser.get()
else:
browser = webbrowser.get(config.open_browser)
browser.open("http://%s:%s" % (config.ui_ip, config.ui_port), new=2)
WSGIServer((self.ip, self.port), handler, handler_class=UiWSGIHandler, log=self.log).serve_forever()

View File

@ -84,6 +84,9 @@ class UiWebsocket:
cmd = req.get("cmd")
params = req.get("params")
permissions = self.site.settings["permissions"]
if req["id"] >= 1000000: # Its a wrapper command, allow admin commands
permissions = permissions[:]
permissions.append("ADMIN")
if cmd == "response": # It's a response to a command
return self.actionResponse(req["to"], req["result"])
@ -114,6 +117,8 @@ class UiWebsocket:
func = self.actionSiteDelete
elif cmd == "siteList" and "ADMIN" in permissions:
func = self.actionSiteList
elif cmd == "siteSetLimit" and "ADMIN" in permissions:
func = self.actionSiteSetLimit
elif cmd == "channelJoinAllsite" and "ADMIN" in permissions:
func = self.actionChannelJoinAllsite
# Unknown command
@ -155,16 +160,21 @@ class UiWebsocket:
if "sign" in content: del(content["sign"])
if "signs" in content: del(content["signs"])
settings = site.settings.copy()
del settings["wrapper_key"] # Dont expose wrapper key
ret = {
"auth_key": self.site.settings["auth_key"], # Obsolete, will be removed
"auth_key_sha512": hashlib.sha512(self.site.settings["auth_key"]).hexdigest()[0:64], # Obsolete, will be removed
"auth_address": self.user.getAuthAddress(site.address),
"address": site.address,
"settings": site.settings,
"settings": settings,
"content_updated": site.content_updated,
"bad_files": len(site.bad_files),
"size_limit": site.getSizeLimit(),
"next_size_limit": site.getNextSizeLimit(),
"last_downloads": len(site.last_downloads),
"peers": len(site.peers),
"peers": site.settings["peers"],
"tasks": len([task["inner_path"] for task in site.worker_manager.tasks]),
"content": content
}
@ -344,3 +354,10 @@ class UiWebsocket:
site.updateWebsocket()
else:
self.response(to, {"error": "Unknown site: %s" % address})
def actionSiteSetLimit(self, to, size_limit):
self.site.settings["size_limit"] = size_limit
self.site.saveSettings()
self.response(to, "Site size limit changed to %sMB" % size_limit)
self.site.download()

View File

@ -34,7 +34,10 @@ class Loading
if not @screen_visible then return false
$(".loadingscreen .console .cursor").remove() # Remove previous cursor
if type == "error" then text = "<span class='console-error'>#{text}</span>" else text = text+"<span class='cursor'> </span>"
$(".loadingscreen .console").append("<div class='console-line'>#{text}</div>")
line = $("<div class='console-line'>#{text}</div>").appendTo(".loadingscreen .console")
if type == "warning" then line.addClass("console-warning")
return line

View File

@ -70,23 +70,28 @@ class Wrapper
else if cmd == "wrapperSetViewport" # Set the viewport
@actionSetViewport(message)
else # Send to websocket
@ws.send(message) # Pass message to websocket
if message.id < 1000000
@ws.send(message) # Pass message to websocket
else
@log "Invalid inner message id"
# - Actions -
actionWrapperConfirm: (message) ->
actionWrapperConfirm: (message, cb=false) ->
message.params = @toHtmlSafe(message.params) # Escape html
if message.params[1] then caption = message.params[1] else caption = "ok"
body = $("<span>"+message.params[0]+"</span>")
button = $("<a href='##{caption}' class='button button-#{caption}'>#{caption}</a>") # Add confirm button
button.on "click", => # Response on button click
@wrapperConfirm message.params[0], caption, =>
@sendInner {"cmd": "response", "to": message.id, "result": "boom"} # Response to confirm
return false
wrapperConfirm: (message, caption, cb) ->
body = $("<span>"+message+"</span>")
button = $("<a href='##{caption}' class='button button-#{caption}'>#{caption}</a>") # Add confirm button
button.on "click", cb
body.append(button)
@notifications.add("notification-#{message.id}", "ask", body)
@notifications.add("notification-#{caption}", "ask", body)
@ -151,7 +156,7 @@ class Wrapper
@ws_error = @notifications.add("connection", "error", "UiServer Websocket error, please reload the page.")
else if not @ws_error
@ws_error = @notifications.add("connection", "error", "Connection with <b>UiServer Websocket</b> was lost. Reconnecting...")
), 500
), 1000
# Iframe loaded
@ -163,7 +168,7 @@ class Wrapper
if window.location.hash then $("#inner-iframe")[0].src += window.location.hash # Hash tag
if @ws.ws.readyState == 1 and not @site_info # Ws opened
@reloadSiteInfo()
else if @site_info
else if @site_info and @site_info.content?.title?
window.document.title = @site_info.content.title+" - ZeroNet"
@log "Setting title to", window.document.title
@ -198,7 +203,18 @@ class Wrapper
# File failed downloading
else if site_info.event[0] == "file_failed"
@site_error = site_info.event[1]
@loading.printLine("#{site_info.event[1]} download failed", "error")
if site_info.settings.size > site_info.size_limit*1024*1024 # Site size too large and not displaying it yet
if $(".console .button-setlimit").length == 0 # Not displaying it yet
line = @loading.printLine("Site size: <b>#{parseInt(site_info.settings.size/1024/1024)}MB</b> is larger than default allowed #{parseInt(site_info.size_limit)}MB", "warning")
button = $("<a href='#Set+limit' class='button button-setlimit'>Open site and set size limit to #{site_info.next_size_limit}MB</a>")
button.on "click", (=> return @setSizeLimit(site_info.next_size_limit) )
line.after(button)
setTimeout (=>
@loading.printLine('Ready.')
), 100
else
@loading.printLine("#{site_info.event[1]} download failed", "error")
# New peers found
else if site_info.event[0] == "peers_added"
@loading.printLine("Peers found: #{site_info.peers}")
@ -209,6 +225,13 @@ class Wrapper
else
@site_error = "No peers found"
@loading.printLine "No peers found"
if not @site_info and $("#inner-iframe").attr("src").indexOf("?") == -1 # First site info and mainpage
if site_info.size_limit < site_info.next_size_limit # Need upgrade soon
@wrapperConfirm "Running out of size limit (#{(site_info.settings.size/1024/1024).toFixed(1)}MB/#{site_info.size_limit}MB)", "Set limit to #{site_info.next_size_limit}MB", =>
@ws.cmd "siteSetLimit", [site_info.next_size_limit], (res) =>
@notifications.add("size_limit", "done", res, 5000)
return false
@site_info = site_info
@ -221,6 +244,14 @@ class Wrapper
return values
setSizeLimit: (size_limit, reload=true) =>
@ws.cmd "siteSetLimit", [size_limit], (res) =>
@loading.printLine res
if reload
$("iframe").attr "src", $("iframe").attr("src") # Reload iframe
return false
log: (args...) ->
console.log "[Wrapper]", args...

View File

@ -14,6 +14,7 @@ a { color: black }
.button-Delete { background-color: #e74c3c; border-bottom-color: #c0392b; color: white }
.button-Delete:hover { background-color: #FF5442; border-bottom-color: #8E2B21 }
/* Fixbutton */
.fixbutton {
@ -76,6 +77,9 @@ a { color: black }
display: inline-block; width: 9px; height: 19px; vertical-align: -4px;
}
.console .console-error { color: #e74c3c; font-weight: bold; animation: pulse 2s infinite linear }
.console .console-warning { color: #8e44ad; }
.console .button { margin: 20px; display: inline-block; text-transform: none; padding: 10px 20px }
/* Flipper loading anim */
.flipper-container { width: 40px; height: 40px; position: absolute; top: 0%; left: 50%; transform: translate3d(-50%, -50%, 0); perspective: 1200; opacity: 0 }

View File

@ -19,6 +19,7 @@ a { color: black }
.button-Delete { background-color: #e74c3c; border-bottom-color: #c0392b; color: white }
.button-Delete:hover { background-color: #FF5442; border-bottom-color: #8E2B21 }
/* Fixbutton */
.fixbutton {
@ -81,6 +82,9 @@ a { color: black }
display: inline-block; width: 9px; height: 19px; vertical-align: -4px;
}
.console .console-error { color: #e74c3c; font-weight: bold; -webkit-animation: pulse 2s infinite linear ; -moz-animation: pulse 2s infinite linear ; -o-animation: pulse 2s infinite linear ; -ms-animation: pulse 2s infinite linear ; animation: pulse 2s infinite linear }
.console .console-warning { color: #8e44ad; }
.console .button { margin: 20px; display: inline-block; text-transform: none; padding: 10px 20px }
/* Flipper loading anim */
.flipper-container { width: 40px; height: 40px; position: absolute; top: 0%; left: 50%; -webkit-transform: translate3d(-50%, -50%, 0); -moz-transform: translate3d(-50%, -50%, 0); -o-transform: translate3d(-50%, -50%, 0); -ms-transform: translate3d(-50%, -50%, 0); transform: translate3d(-50%, -50%, 0) ; -webkit-perspective: 1200; -moz-perspective: 1200; -o-perspective: 1200; -ms-perspective: 1200; perspective: 1200 ; opacity: 0 }

View File

@ -502,6 +502,7 @@ jQuery.extend( jQuery.easing,
};
Loading.prototype.printLine = function(text, type) {
var line;
if (type == null) {
type = "normal";
}
@ -514,7 +515,11 @@ jQuery.extend( jQuery.easing,
} else {
text = text + "<span class='cursor'> </span>";
}
return $(".loadingscreen .console").append("<div class='console-line'>" + text + "</div>");
line = $("<div class='console-line'>" + text + "</div>").appendTo(".loadingscreen .console");
if (type === "warning") {
line.addClass("console-warning");
}
return line;
};
return Loading;
@ -720,6 +725,7 @@ jQuery.extend( jQuery.easing,
Wrapper = (function() {
function Wrapper(ws_url) {
this.setSizeLimit = __bind(this.setSizeLimit, this);
this.onLoad = __bind(this.onLoad, this);
this.onCloseWebsocket = __bind(this.onCloseWebsocket, this);
this.onOpenWebsocket = __bind(this.onOpenWebsocket, this);
@ -802,21 +808,26 @@ jQuery.extend( jQuery.easing,
} else if (cmd === "wrapperSetViewport") {
return this.actionSetViewport(message);
} else {
return this.ws.send(message);
if (message.id < 1000000) {
return this.ws.send(message);
} else {
return this.log("Invalid inner message id");
}
}
};
Wrapper.prototype.actionWrapperConfirm = function(message) {
var body, button, caption;
Wrapper.prototype.actionWrapperConfirm = function(message, cb) {
var caption;
if (cb == null) {
cb = false;
}
message.params = this.toHtmlSafe(message.params);
if (message.params[1]) {
caption = message.params[1];
} else {
caption = "ok";
}
body = $("<span>" + message.params[0] + "</span>");
button = $("<a href='#" + caption + "' class='button button-" + caption + "'>" + caption + "</a>");
button.on("click", (function(_this) {
return this.wrapperConfirm(message.params[0], caption, (function(_this) {
return function() {
_this.sendInner({
"cmd": "response",
@ -826,8 +837,15 @@ jQuery.extend( jQuery.easing,
return false;
};
})(this));
};
Wrapper.prototype.wrapperConfirm = function(message, caption, cb) {
var body, button;
body = $("<span>" + message + "</span>");
button = $("<a href='#" + caption + "' class='button button-" + caption + "'>" + caption + "</a>");
button.on("click", cb);
body.append(button);
return this.notifications.add("notification-" + message.id, "ask", body);
return this.notifications.add("notification-" + caption, "ask", body);
};
Wrapper.prototype.actionWrapperPrompt = function(message) {
@ -913,10 +931,11 @@ jQuery.extend( jQuery.easing,
return _this.ws_error = _this.notifications.add("connection", "error", "Connection with <b>UiServer Websocket</b> was lost. Reconnecting...");
}
};
})(this)), 500);
})(this)), 1000);
};
Wrapper.prototype.onLoad = function(e) {
var _ref;
this.log("onLoad");
this.inner_loaded = true;
if (!this.inner_ready) {
@ -932,7 +951,7 @@ jQuery.extend( jQuery.easing,
}
if (this.ws.ws.readyState === 1 && !this.site_info) {
return this.reloadSiteInfo();
} else if (this.site_info) {
} else if (this.site_info && (((_ref = this.site_info.content) != null ? _ref.title : void 0) != null)) {
window.document.title = this.site_info.content.title + " - ZeroNet";
return this.log("Setting title to", window.document.title);
}
@ -953,6 +972,7 @@ jQuery.extend( jQuery.easing,
};
Wrapper.prototype.setSiteInfo = function(site_info) {
var button, line;
if (site_info.event != null) {
if (site_info.event[0] === "file_added" && site_info.bad_files) {
this.loading.printLine(site_info.bad_files + " files needs to be downloaded");
@ -969,7 +989,25 @@ jQuery.extend( jQuery.easing,
}
} else if (site_info.event[0] === "file_failed") {
this.site_error = site_info.event[1];
this.loading.printLine(site_info.event[1] + " download failed", "error");
if (site_info.settings.size > site_info.size_limit * 1024 * 1024) {
if ($(".console .button-setlimit").length === 0) {
line = this.loading.printLine("Site size: <b>" + (parseInt(site_info.settings.size / 1024 / 1024)) + "MB</b> is larger than default allowed " + (parseInt(site_info.size_limit)) + "MB", "warning");
button = $("<a href='#Set+limit' class='button button-setlimit'>Open site and set size limit to " + site_info.next_size_limit + "MB</a>");
button.on("click", ((function(_this) {
return function() {
return _this.setSizeLimit(site_info.next_size_limit);
};
})(this)));
line.after(button);
setTimeout(((function(_this) {
return function() {
return _this.loading.printLine('Ready.');
};
})(this)), 100);
}
} else {
this.loading.printLine(site_info.event[1] + " download failed", "error");
}
} else if (site_info.event[0] === "peers_added") {
this.loading.printLine("Peers found: " + site_info.peers);
}
@ -982,6 +1020,18 @@ jQuery.extend( jQuery.easing,
this.loading.printLine("No peers found");
}
}
if (!this.site_info && $("#inner-iframe").attr("src").indexOf("?") === -1) {
if (site_info.size_limit < site_info.next_size_limit) {
this.wrapperConfirm("Running out of size limit (" + ((site_info.settings.size / 1024 / 1024).toFixed(1)) + "MB/" + site_info.size_limit + "MB)", "Set limit to " + site_info.next_size_limit + "MB", (function(_this) {
return function() {
_this.ws.cmd("siteSetLimit", [site_info.next_size_limit], function(res) {
return _this.notifications.add("size_limit", "done", res, 5000);
});
return false;
};
})(this));
}
}
return this.site_info = site_info;
};
@ -999,6 +1049,21 @@ jQuery.extend( jQuery.easing,
return values;
};
Wrapper.prototype.setSizeLimit = function(size_limit, reload) {
if (reload == null) {
reload = true;
}
this.ws.cmd("siteSetLimit", [size_limit], (function(_this) {
return function(res) {
_this.loading.printLine(res);
if (reload) {
return $("iframe").attr("src", $("iframe").attr("src"));
}
};
})(this));
return false;
};
Wrapper.prototype.log = function() {
var args;
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];

View File

@ -1,16 +1,10 @@
#!/usr/bin/env python
from multiprocessing import Process
import sys
import webbrowser
import zeronet
def main():
browser_name = sys.argv.pop() if len(sys.argv) >= 2 else None
server = Process(target=zeronet.main)
server.start()
browser = webbrowser.get(browser_name)
url = browser.open("http://127.0.0.1:43110", new=2)
server.join()
sys.argv += ["--open_browser", "default_browser"]
zeronet.main()
if __name__ == '__main__':
main()
main()