version 0.1.5, install as user to readme, more debug on filerequests, if upnpc fail try without -e, announce interval from 10 to 20 min, detect computer wakeup and acts as startup, delete sites files websocket command support, pause stop all current downloads, wrapper confirmation dialog support
This commit is contained in:
parent
3bec738595
commit
a977feec33
13 changed files with 197 additions and 16 deletions
|
@ -40,6 +40,11 @@ Linux (Debian):
|
||||||
- `pip install msgpack-python`
|
- `pip install msgpack-python`
|
||||||
- start using `python zeronet.py`
|
- start using `python zeronet.py`
|
||||||
|
|
||||||
|
Linux (Without root access):
|
||||||
|
- `wget https://bootstrap.pypa.io/get-pip.py`
|
||||||
|
- `python get-pip.py --user pyzmq gevent msgpack-python`
|
||||||
|
- start using `python zeronet.py`
|
||||||
|
|
||||||
|
|
||||||
## Current limitations
|
## Current limitations
|
||||||
- No torrent-like, file splitting big file support
|
- No torrent-like, file splitting big file support
|
||||||
|
@ -91,4 +96,4 @@ Bitcoin: 1QDhxQ6PraUZa21ET5fYUCPgdrwBomnFgX
|
||||||
#### Thank you!
|
#### Thank you!
|
||||||
|
|
||||||
- More info, help, changelog, zeronet sites: http://www.reddit.com/r/zeronet/
|
- More info, help, changelog, zeronet sites: http://www.reddit.com/r/zeronet/
|
||||||
- Come, chat with us: [#zeronet @ FreeNode](https://kiwiirc.com/client/irc.freenode.net/zeronet)
|
- Come, chat with us: [#zeronet @ FreeNode](https://kiwiirc.com/client/irc.freenode.net/zeronet)
|
|
@ -3,7 +3,7 @@ import ConfigParser
|
||||||
|
|
||||||
class Config(object):
|
class Config(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.version = "0.1.4"
|
self.version = "0.1.5"
|
||||||
self.parser = self.createArguments()
|
self.parser = self.createArguments()
|
||||||
argv = sys.argv[:] # Copy command line arguments
|
argv = sys.argv[:] # Copy command line arguments
|
||||||
argv = self.parseConfig(argv) # Add arguments from config file
|
argv = self.parseConfig(argv) # Add arguments from config file
|
||||||
|
|
|
@ -2,6 +2,7 @@ import os, msgpack, shutil
|
||||||
from Site import SiteManager
|
from Site import SiteManager
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
from Debug import Debug
|
from Debug import Debug
|
||||||
|
from Config import config
|
||||||
|
|
||||||
FILE_BUFF = 1024*512
|
FILE_BUFF = 1024*512
|
||||||
|
|
||||||
|
@ -80,13 +81,17 @@ class FileRequest:
|
||||||
self.send({"error": "Unknown site"})
|
self.send({"error": "Unknown site"})
|
||||||
return False
|
return False
|
||||||
try:
|
try:
|
||||||
file = open(site.getPath(params["inner_path"]), "rb")
|
file_path = site.getPath(params["inner_path"])
|
||||||
|
if config.debug_socket: self.log.debug("Opening file: %s" % file_path)
|
||||||
|
file = open(file_path, "rb")
|
||||||
file.seek(params["location"])
|
file.seek(params["location"])
|
||||||
back = {}
|
back = {}
|
||||||
back["body"] = file.read(FILE_BUFF)
|
back["body"] = file.read(FILE_BUFF)
|
||||||
back["location"] = file.tell()
|
back["location"] = file.tell()
|
||||||
back["size"] = os.fstat(file.fileno()).st_size
|
back["size"] = os.fstat(file.fileno()).st_size
|
||||||
|
if config.debug_socket: self.log.debug("Sending file %s from position %s to %s" % (file_path, params["location"], back["location"]))
|
||||||
self.send(back)
|
self.send(back)
|
||||||
|
if config.debug_socket: self.log.debug("File %s sent" % file_path)
|
||||||
except Exception, err:
|
except Exception, err:
|
||||||
self.send({"error": "File read error: %s" % Debug.formatException(err)})
|
self.send({"error": "File read error: %s" % Debug.formatException(err)})
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -49,10 +49,14 @@ class FileServer:
|
||||||
self.log.info("Try to open port using upnpc...")
|
self.log.info("Try to open port using upnpc...")
|
||||||
try:
|
try:
|
||||||
exit = os.system("%s -e ZeroNet -r %s tcp" % (config.upnpc, self.port))
|
exit = os.system("%s -e ZeroNet -r %s tcp" % (config.upnpc, self.port))
|
||||||
if exit == 0:
|
if exit == 0: # Success
|
||||||
upnpc_success = True
|
upnpc_success = True
|
||||||
else:
|
else: # Failed
|
||||||
upnpc_success = False
|
exit = os.system("%s -r %s tcp" % (config.upnpc, self.port)) # Try without -e option
|
||||||
|
if exit == 0:
|
||||||
|
upnpc_success = True
|
||||||
|
else:
|
||||||
|
upnpc_success = False
|
||||||
except Exception, err:
|
except Exception, err:
|
||||||
self.log.error("Upnpc run error: %s" % Debug.formatException(err))
|
self.log.error("Upnpc run error: %s" % Debug.formatException(err))
|
||||||
upnpc_success = False
|
upnpc_success = False
|
||||||
|
@ -122,13 +126,25 @@ class FileServer:
|
||||||
# Announce sites every 10 min
|
# Announce sites every 10 min
|
||||||
def announceSites(self):
|
def announceSites(self):
|
||||||
while 1:
|
while 1:
|
||||||
time.sleep(10*60) # Announce sites every 10 min
|
time.sleep(20*60) # Announce sites every 20 min
|
||||||
for address, site in self.sites.items():
|
for address, site in self.sites.items():
|
||||||
if site.settings["serving"]:
|
if site.settings["serving"]:
|
||||||
site.announce() # Announce site to tracker
|
site.announce() # Announce site to tracker
|
||||||
time.sleep(2) # Prevent too quick request
|
time.sleep(2) # Prevent too quick request
|
||||||
|
|
||||||
|
|
||||||
|
# Detects if computer back from wakeup
|
||||||
|
def wakeupWatcher(self):
|
||||||
|
last_time = time.time()
|
||||||
|
while 1:
|
||||||
|
time.sleep(30)
|
||||||
|
if time.time()-last_time > 60: # If taken more than 60 second then the computer was in sleep mode
|
||||||
|
self.log.info("Wakeup detected: time wrap from %s to %s (%s sleep seconds), acting like startup..." % (last_time, time.time(), time.time()-last_time))
|
||||||
|
self.port_opened = None # Check if we still has the open port on router
|
||||||
|
self.checkSites()
|
||||||
|
last_time = time.time()
|
||||||
|
|
||||||
|
|
||||||
# Bind and start serving sites
|
# Bind and start serving sites
|
||||||
def start(self, check_sites = True):
|
def start(self, check_sites = True):
|
||||||
self.log = logging.getLogger(__name__)
|
self.log = logging.getLogger(__name__)
|
||||||
|
@ -152,6 +168,7 @@ class FileServer:
|
||||||
gevent.spawn(self.checkSites)
|
gevent.spawn(self.checkSites)
|
||||||
|
|
||||||
gevent.spawn(self.announceSites)
|
gevent.spawn(self.announceSites)
|
||||||
|
gevent.spawn(self.wakeupWatcher)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -267,6 +267,32 @@ class Site:
|
||||||
self.bad_files[bad_file] = True
|
self.bad_files[bad_file] = True
|
||||||
|
|
||||||
|
|
||||||
|
def deleteFiles(self):
|
||||||
|
self.log.debug("Deleting files from content.json...")
|
||||||
|
files = self.content["files"].keys() # Make a copy
|
||||||
|
files.append("content.json")
|
||||||
|
for inner_path in files:
|
||||||
|
path = self.getPath(inner_path)
|
||||||
|
if os.path.isfile(path): os.unlink(path)
|
||||||
|
|
||||||
|
self.log.debug("Deleting empty dirs...")
|
||||||
|
for root, dirs, files in os.walk(self.directory, topdown=False):
|
||||||
|
for dir in dirs:
|
||||||
|
path = os.path.join(root,dir)
|
||||||
|
if os.path.isdir(path) and os.listdir(path) == []:
|
||||||
|
os.removedirs(path)
|
||||||
|
self.log.debug("Removing %s" % path)
|
||||||
|
if os.path.isdir(self.directory) and os.listdir(self.directory) == []: os.removedirs(self.directory) # Remove sites directory if empty
|
||||||
|
|
||||||
|
if os.path.isdir(self.directory):
|
||||||
|
self.log.debug("Some unknown file remained in site data dir: %s..." % self.directory)
|
||||||
|
return False # Some files not deleted
|
||||||
|
else:
|
||||||
|
self.log.debug("Site data directory deleted: %s..." % self.directory)
|
||||||
|
return True # All clean
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# - Events -
|
# - Events -
|
||||||
|
|
||||||
# Add event listeners
|
# Add event listeners
|
||||||
|
@ -380,11 +406,10 @@ class Site:
|
||||||
else:
|
else:
|
||||||
ok = self.verifyFile(inner_path, open(file_path, "rb"))
|
ok = self.verifyFile(inner_path, open(file_path, "rb"))
|
||||||
|
|
||||||
if ok:
|
if not ok:
|
||||||
self.log.debug("[OK] %s" % inner_path)
|
|
||||||
else:
|
|
||||||
self.log.error("[ERROR] %s" % inner_path)
|
self.log.error("[ERROR] %s" % inner_path)
|
||||||
bad_files.append(inner_path)
|
bad_files.append(inner_path)
|
||||||
|
self.log.debug("Site verified: %s files, quick_check: %s, bad files: %s" % (len(self.content["files"]), quick_check, bad_files))
|
||||||
|
|
||||||
return bad_files
|
return bad_files
|
||||||
|
|
||||||
|
|
|
@ -45,12 +45,20 @@ def need(address, all_file=True):
|
||||||
from Site import Site
|
from Site import Site
|
||||||
if address not in sites: # Site not exits yet
|
if address not in sites: # Site not exits yet
|
||||||
if not isAddress(address): raise Exception("Not address: %s" % address)
|
if not isAddress(address): raise Exception("Not address: %s" % address)
|
||||||
|
logging.debug("Added new site: %s" % address)
|
||||||
sites[address] = Site(address)
|
sites[address] = Site(address)
|
||||||
|
sites[address].settings["serving"] = True # Maybe it was deleted before
|
||||||
site = sites[address]
|
site = sites[address]
|
||||||
if all_file: site.download()
|
if all_file: site.download()
|
||||||
return site
|
return site
|
||||||
|
|
||||||
|
|
||||||
|
def delete(address):
|
||||||
|
global sites
|
||||||
|
logging.debug("SiteManager deleted site: %s" % address)
|
||||||
|
del(sites[address])
|
||||||
|
|
||||||
|
|
||||||
# Lazy load sites
|
# Lazy load sites
|
||||||
def list():
|
def list():
|
||||||
if sites == None: # Not loaded yet
|
if sites == None: # Not loaded yet
|
||||||
|
|
|
@ -96,6 +96,8 @@ class UiWebsocket:
|
||||||
self.actionSitePause(req["id"], req["params"])
|
self.actionSitePause(req["id"], req["params"])
|
||||||
elif cmd == "siteResume" and "ADMIN" in permissions:
|
elif cmd == "siteResume" and "ADMIN" in permissions:
|
||||||
self.actionSiteResume(req["id"], req["params"])
|
self.actionSiteResume(req["id"], req["params"])
|
||||||
|
elif cmd == "siteDelete" and "ADMIN" in permissions:
|
||||||
|
self.actionSiteDelete(req["id"], req["params"])
|
||||||
elif cmd == "siteList" and "ADMIN" in permissions:
|
elif cmd == "siteList" and "ADMIN" in permissions:
|
||||||
self.actionSiteList(req["id"], req["params"])
|
self.actionSiteList(req["id"], req["params"])
|
||||||
elif cmd == "channelJoinAllsite" and "ADMIN" in permissions:
|
elif cmd == "channelJoinAllsite" and "ADMIN" in permissions:
|
||||||
|
@ -211,6 +213,7 @@ class UiWebsocket:
|
||||||
site.settings["serving"] = False
|
site.settings["serving"] = False
|
||||||
site.saveSettings()
|
site.saveSettings()
|
||||||
site.updateWebsocket()
|
site.updateWebsocket()
|
||||||
|
site.worker_manager.stopWorkers()
|
||||||
else:
|
else:
|
||||||
self.response(to, {"error": "Unknown site: %s" % address})
|
self.response(to, {"error": "Unknown site: %s" % address})
|
||||||
|
|
||||||
|
@ -227,3 +230,18 @@ class UiWebsocket:
|
||||||
site.updateWebsocket()
|
site.updateWebsocket()
|
||||||
else:
|
else:
|
||||||
self.response(to, {"error": "Unknown site: %s" % address})
|
self.response(to, {"error": "Unknown site: %s" % address})
|
||||||
|
|
||||||
|
|
||||||
|
def actionSiteDelete(self, to, params):
|
||||||
|
address = params.get("address")
|
||||||
|
site = self.server.sites.get(address)
|
||||||
|
if site:
|
||||||
|
site.settings["serving"] = False
|
||||||
|
site.saveSettings()
|
||||||
|
site.worker_manager.running = False
|
||||||
|
site.worker_manager.stopWorkers()
|
||||||
|
site.deleteFiles()
|
||||||
|
SiteManager.delete(address)
|
||||||
|
site.updateWebsocket()
|
||||||
|
else:
|
||||||
|
self.response(to, {"error": "Unknown site: %s" % address})
|
||||||
|
|
|
@ -27,10 +27,15 @@ class Notifications
|
||||||
$(".notification-icon", elem).html("!")
|
$(".notification-icon", elem).html("!")
|
||||||
else if type == "done"
|
else if type == "done"
|
||||||
$(".notification-icon", elem).html("<div class='icon-success'></div>")
|
$(".notification-icon", elem).html("<div class='icon-success'></div>")
|
||||||
|
else if type == "ask"
|
||||||
|
$(".notification-icon", elem).html("?")
|
||||||
else
|
else
|
||||||
$(".notification-icon", elem).html("i")
|
$(".notification-icon", elem).html("i")
|
||||||
|
|
||||||
$(".body", elem).html(body)
|
if typeof(body) == "string"
|
||||||
|
$(".body", elem).html(body)
|
||||||
|
else
|
||||||
|
$(".body", elem).html("").append(body)
|
||||||
|
|
||||||
elem.appendTo(@elem)
|
elem.appendTo(@elem)
|
||||||
|
|
||||||
|
@ -53,7 +58,10 @@ class Notifications
|
||||||
@close elem
|
@close elem
|
||||||
return false
|
return false
|
||||||
|
|
||||||
@
|
# Close on button click within body (confirm dialog)
|
||||||
|
$(".button", elem).on "click", =>
|
||||||
|
@close elem
|
||||||
|
return false
|
||||||
|
|
||||||
|
|
||||||
close: (elem) ->
|
close: (elem) ->
|
||||||
|
|
|
@ -55,12 +55,31 @@ class Wrapper
|
||||||
if @ws.ws.readyState == 1 and not @wrapperWsInited # If ws already opened
|
if @ws.ws.readyState == 1 and not @wrapperWsInited # If ws already opened
|
||||||
@sendInner {"cmd": "wrapperOpenedWebsocket"}
|
@sendInner {"cmd": "wrapperOpenedWebsocket"}
|
||||||
@wrapperWsInited = true
|
@wrapperWsInited = true
|
||||||
else if cmd == "wrapperNotification"
|
else if cmd == "wrapperNotification" # Display notification
|
||||||
|
message.params = @toHtmlSafe(message.params) # Escape html
|
||||||
@notifications.add("notification-#{message.id}", message.params[0], message.params[1], message.params[2])
|
@notifications.add("notification-#{message.id}", message.params[0], message.params[1], message.params[2])
|
||||||
|
else if cmd == "wrapperConfirm" # Display confirm message
|
||||||
|
@actionWrapperConfirm(message)
|
||||||
else # Send to websocket
|
else # Send to websocket
|
||||||
@ws.send(message) # Pass message to websocket
|
@ws.send(message) # Pass message to websocket
|
||||||
|
|
||||||
|
|
||||||
|
# - Actions -
|
||||||
|
|
||||||
|
actionWrapperConfirm: (message) ->
|
||||||
|
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
|
||||||
|
@sendInner {"cmd": "response", "to": message.id, "result": "boom"} # Response to confirm
|
||||||
|
return false
|
||||||
|
body.append(button)
|
||||||
|
|
||||||
|
@notifications.add("notification-#{message.id}", "ask", body)
|
||||||
|
|
||||||
|
|
||||||
onOpenWebsocket: (e) =>
|
onOpenWebsocket: (e) =>
|
||||||
@ws.cmd "channelJoin", {"channel": "siteChanged"} # Get info on modifications
|
@ws.cmd "channelJoin", {"channel": "siteChanged"} # Get info on modifications
|
||||||
@log "onOpenWebsocket", @inner_ready, @wrapperWsInited
|
@log "onOpenWebsocket", @inner_ready, @wrapperWsInited
|
||||||
|
@ -125,6 +144,7 @@ class Wrapper
|
||||||
@loading.printLine("#{site_info.event[1]} downloaded")
|
@loading.printLine("#{site_info.event[1]} downloaded")
|
||||||
if site_info.event[1] == window.inner_path # File downloaded we currently on
|
if site_info.event[1] == window.inner_path # File downloaded we currently on
|
||||||
@loading.hideScreen()
|
@loading.hideScreen()
|
||||||
|
if not @site_info then @reloadSiteInfo()
|
||||||
if not $(".loadingscreen").length # Loading screen already removed (loaded +2sec)
|
if not $(".loadingscreen").length # Loading screen already removed (loaded +2sec)
|
||||||
@notifications.add("modified", "info", "New version of this page has just released.<br>Reload to see the modified content.")
|
@notifications.add("modified", "info", "New version of this page has just released.<br>Reload to see the modified content.")
|
||||||
# File failed downloading
|
# File failed downloading
|
||||||
|
@ -144,6 +164,10 @@ class Wrapper
|
||||||
@site_info = site_info
|
@site_info = site_info
|
||||||
|
|
||||||
|
|
||||||
|
toHtmlSafe: (unsafe) ->
|
||||||
|
return unsafe
|
||||||
|
|
||||||
|
|
||||||
log: (args...) ->
|
log: (args...) ->
|
||||||
console.log "[Wrapper]", args...
|
console.log "[Wrapper]", args...
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,12 @@ a { color: black }
|
||||||
#inner-iframe { width: 100%; height: 100%; position: absolute; border: 0px; transition: all 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55), opacity 0.8s ease-in-out }
|
#inner-iframe { width: 100%; height: 100%; position: absolute; border: 0px; transition: all 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55), opacity 0.8s ease-in-out }
|
||||||
#inner-iframe.back { transform: scale(0.95) translate(-300px, 0px); opacity: 0.4 }
|
#inner-iframe.back { transform: scale(0.95) translate(-300px, 0px); opacity: 0.4 }
|
||||||
|
|
||||||
|
.button { padding: 5px 10px; margin-left: 10px; background-color: #FFF85F; border-bottom: 2px solid #CDBD1E; border-radius: 2px; text-decoration: none; transition: all 0.5s; }
|
||||||
|
.button:hover { background-color: #FFF400; border-bottom: 2px solid #4D4D4C; transition: none }
|
||||||
|
.button:active { position: relative; top: 1px }
|
||||||
|
|
||||||
|
.button-Delete { background-color: #e74c3c; border-bottom-color: #c0392b; color: white }
|
||||||
|
.button-Delete:hover { background-color: #FF5442; border-bottom-color: #8E2B21 }
|
||||||
|
|
||||||
/* Fixbutton */
|
/* Fixbutton */
|
||||||
|
|
||||||
|
@ -43,6 +49,7 @@ a { color: black }
|
||||||
.notification .close:active, .notification .close:focus { color: #AF3BFF }
|
.notification .close:active, .notification .close:focus { color: #AF3BFF }
|
||||||
|
|
||||||
/* Notification types */
|
/* Notification types */
|
||||||
|
.notification-ask .notification-icon { background-color: #f39c12; }
|
||||||
.notification-info .notification-icon { font-size: 22px; font-weight: bold; background-color: #2980b9; line-height: 48px }
|
.notification-info .notification-icon { font-size: 22px; font-weight: bold; background-color: #2980b9; line-height: 48px }
|
||||||
.notification-done .notification-icon { font-size: 22px; background-color: #27ae60 }
|
.notification-done .notification-icon { font-size: 22px; background-color: #27ae60 }
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,12 @@ a { color: black }
|
||||||
#inner-iframe { width: 100%; height: 100%; position: absolute; border: 0px; -webkit-transition: all 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55), opacity 0.8s ease-in-out ; -moz-transition: all 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55), opacity 0.8s ease-in-out ; -o-transition: all 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55), opacity 0.8s ease-in-out ; -ms-transition: all 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55), opacity 0.8s ease-in-out ; transition: all 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55), opacity 0.8s ease-in-out }
|
#inner-iframe { width: 100%; height: 100%; position: absolute; border: 0px; -webkit-transition: all 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55), opacity 0.8s ease-in-out ; -moz-transition: all 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55), opacity 0.8s ease-in-out ; -o-transition: all 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55), opacity 0.8s ease-in-out ; -ms-transition: all 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55), opacity 0.8s ease-in-out ; transition: all 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55), opacity 0.8s ease-in-out }
|
||||||
#inner-iframe.back { -webkit-transform: scale(0.95) translate(-300px, 0px); -moz-transform: scale(0.95) translate(-300px, 0px); -o-transform: scale(0.95) translate(-300px, 0px); -ms-transform: scale(0.95) translate(-300px, 0px); transform: scale(0.95) translate(-300px, 0px) ; opacity: 0.4 }
|
#inner-iframe.back { -webkit-transform: scale(0.95) translate(-300px, 0px); -moz-transform: scale(0.95) translate(-300px, 0px); -o-transform: scale(0.95) translate(-300px, 0px); -ms-transform: scale(0.95) translate(-300px, 0px); transform: scale(0.95) translate(-300px, 0px) ; opacity: 0.4 }
|
||||||
|
|
||||||
|
.button { padding: 5px 10px; margin-left: 10px; background-color: #FFF85F; border-bottom: 2px solid #CDBD1E; -webkit-border-radius: 2px; -moz-border-radius: 2px; -o-border-radius: 2px; -ms-border-radius: 2px; border-radius: 2px ; text-decoration: none; -webkit-transition: all 0.5s; -moz-transition: all 0.5s; -o-transition: all 0.5s; -ms-transition: all 0.5s; transition: all 0.5s ; }
|
||||||
|
.button:hover { background-color: #FFF400; border-bottom: 2px solid #4D4D4C; -webkit-transition: none ; -moz-transition: none ; -o-transition: none ; -ms-transition: none ; transition: none }
|
||||||
|
.button:active { position: relative; top: 1px }
|
||||||
|
|
||||||
|
.button-Delete { background-color: #e74c3c; border-bottom-color: #c0392b; color: white }
|
||||||
|
.button-Delete:hover { background-color: #FF5442; border-bottom-color: #8E2B21 }
|
||||||
|
|
||||||
/* Fixbutton */
|
/* Fixbutton */
|
||||||
|
|
||||||
|
@ -48,6 +54,7 @@ a { color: black }
|
||||||
.notification .close:active, .notification .close:focus { color: #AF3BFF }
|
.notification .close:active, .notification .close:focus { color: #AF3BFF }
|
||||||
|
|
||||||
/* Notification types */
|
/* Notification types */
|
||||||
|
.notification-ask .notification-icon { background-color: #f39c12; }
|
||||||
.notification-info .notification-icon { font-size: 22px; font-weight: bold; background-color: #2980b9; line-height: 48px }
|
.notification-info .notification-icon { font-size: 22px; font-weight: bold; background-color: #2980b9; line-height: 48px }
|
||||||
.notification-done .notification-icon { font-size: 22px; background-color: #27ae60 }
|
.notification-done .notification-icon { font-size: 22px; background-color: #27ae60 }
|
||||||
|
|
||||||
|
|
|
@ -571,10 +571,16 @@ jQuery.extend( jQuery.easing,
|
||||||
$(".notification-icon", elem).html("!");
|
$(".notification-icon", elem).html("!");
|
||||||
} else if (type === "done") {
|
} else if (type === "done") {
|
||||||
$(".notification-icon", elem).html("<div class='icon-success'></div>");
|
$(".notification-icon", elem).html("<div class='icon-success'></div>");
|
||||||
|
} else if (type === "ask") {
|
||||||
|
$(".notification-icon", elem).html("?");
|
||||||
} else {
|
} else {
|
||||||
$(".notification-icon", elem).html("i");
|
$(".notification-icon", elem).html("i");
|
||||||
}
|
}
|
||||||
$(".body", elem).html(body);
|
if (typeof body === "string") {
|
||||||
|
$(".body", elem).html(body);
|
||||||
|
} else {
|
||||||
|
$(".body", elem).html("").append(body);
|
||||||
|
}
|
||||||
elem.appendTo(this.elem);
|
elem.appendTo(this.elem);
|
||||||
if (timeout) {
|
if (timeout) {
|
||||||
$(".close", elem).remove();
|
$(".close", elem).remove();
|
||||||
|
@ -604,7 +610,12 @@ jQuery.extend( jQuery.easing,
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
})(this));
|
})(this));
|
||||||
return this;
|
return $(".button", elem).on("click", (function(_this) {
|
||||||
|
return function() {
|
||||||
|
_this.close(elem);
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
})(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
Notifications.prototype.close = function(elem) {
|
Notifications.prototype.close = function(elem) {
|
||||||
|
@ -771,12 +782,39 @@ jQuery.extend( jQuery.easing,
|
||||||
return this.wrapperWsInited = true;
|
return this.wrapperWsInited = true;
|
||||||
}
|
}
|
||||||
} else if (cmd === "wrapperNotification") {
|
} else if (cmd === "wrapperNotification") {
|
||||||
|
message.params = this.toHtmlSafe(message.params);
|
||||||
return this.notifications.add("notification-" + message.id, message.params[0], message.params[1], message.params[2]);
|
return this.notifications.add("notification-" + message.id, message.params[0], message.params[1], message.params[2]);
|
||||||
|
} else if (cmd === "wrapperConfirm") {
|
||||||
|
return this.actionWrapperConfirm(message);
|
||||||
} else {
|
} else {
|
||||||
return this.ws.send(message);
|
return this.ws.send(message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Wrapper.prototype.actionWrapperConfirm = function(message) {
|
||||||
|
var body, button, caption;
|
||||||
|
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 function() {
|
||||||
|
_this.sendInner({
|
||||||
|
"cmd": "response",
|
||||||
|
"to": message.id,
|
||||||
|
"result": "boom"
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
})(this));
|
||||||
|
body.append(button);
|
||||||
|
return this.notifications.add("notification-" + message.id, "ask", body);
|
||||||
|
};
|
||||||
|
|
||||||
Wrapper.prototype.onOpenWebsocket = function(e) {
|
Wrapper.prototype.onOpenWebsocket = function(e) {
|
||||||
this.ws.cmd("channelJoin", {
|
this.ws.cmd("channelJoin", {
|
||||||
"channel": "siteChanged"
|
"channel": "siteChanged"
|
||||||
|
@ -858,6 +896,9 @@ jQuery.extend( jQuery.easing,
|
||||||
this.loading.printLine("" + site_info.event[1] + " downloaded");
|
this.loading.printLine("" + site_info.event[1] + " downloaded");
|
||||||
if (site_info.event[1] === window.inner_path) {
|
if (site_info.event[1] === window.inner_path) {
|
||||||
this.loading.hideScreen();
|
this.loading.hideScreen();
|
||||||
|
if (!this.site_info) {
|
||||||
|
this.reloadSiteInfo();
|
||||||
|
}
|
||||||
if (!$(".loadingscreen").length) {
|
if (!$(".loadingscreen").length) {
|
||||||
this.notifications.add("modified", "info", "New version of this page has just released.<br>Reload to see the modified content.");
|
this.notifications.add("modified", "info", "New version of this page has just released.<br>Reload to see the modified content.");
|
||||||
}
|
}
|
||||||
|
@ -880,6 +921,10 @@ jQuery.extend( jQuery.easing,
|
||||||
return this.site_info = site_info;
|
return this.site_info = site_info;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Wrapper.prototype.toHtmlSafe = function(unsafe) {
|
||||||
|
return unsafe;
|
||||||
|
};
|
||||||
|
|
||||||
Wrapper.prototype.log = function() {
|
Wrapper.prototype.log = function() {
|
||||||
var args;
|
var args;
|
||||||
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
|
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
|
||||||
|
|
|
@ -9,13 +9,14 @@ class WorkerManager:
|
||||||
self.site = site
|
self.site = site
|
||||||
self.workers = {} # Key: ip:port, Value: Worker.Worker
|
self.workers = {} # Key: ip:port, Value: Worker.Worker
|
||||||
self.tasks = [] # {"evt": evt, "workers_num": 0, "site": self.site, "inner_path": inner_path, "done": False, "time_started": None, "time_added": time.time(), "peers": peers, "priority": 0}
|
self.tasks = [] # {"evt": evt, "workers_num": 0, "site": self.site, "inner_path": inner_path, "done": False, "time_started": None, "time_added": time.time(), "peers": peers, "priority": 0}
|
||||||
|
self.running = True
|
||||||
self.log = logging.getLogger("WorkerManager:%s" % self.site.address_short)
|
self.log = logging.getLogger("WorkerManager:%s" % self.site.address_short)
|
||||||
self.process_taskchecker = gevent.spawn(self.checkTasks)
|
self.process_taskchecker = gevent.spawn(self.checkTasks)
|
||||||
|
|
||||||
|
|
||||||
# Check expired tasks
|
# Check expired tasks
|
||||||
def checkTasks(self):
|
def checkTasks(self):
|
||||||
while 1:
|
while self.running:
|
||||||
time.sleep(15) # Check every 15 sec
|
time.sleep(15) # Check every 15 sec
|
||||||
|
|
||||||
# Clean up workers
|
# Clean up workers
|
||||||
|
@ -42,6 +43,7 @@ class WorkerManager:
|
||||||
task["peers"] = []
|
task["peers"] = []
|
||||||
self.startWorkers()
|
self.startWorkers()
|
||||||
break # One reannounce per loop
|
break # One reannounce per loop
|
||||||
|
self.log.debug("checkTasks stopped running")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -91,6 +93,16 @@ class WorkerManager:
|
||||||
if worker: self.log.debug("Added worker: %s, workers: %s/%s" % (key, len(self.workers), MAX_WORKERS))
|
if worker: self.log.debug("Added worker: %s, workers: %s/%s" % (key, len(self.workers), MAX_WORKERS))
|
||||||
|
|
||||||
|
|
||||||
|
# Stop all worker
|
||||||
|
def stopWorkers(self):
|
||||||
|
for worker in self.workers.values():
|
||||||
|
worker.stop()
|
||||||
|
tasks = self.tasks[:] # Copy
|
||||||
|
for task in tasks: # Mark all current task as failed
|
||||||
|
self.failTask(task)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Find workers by task
|
# Find workers by task
|
||||||
def findWorkers(self, task):
|
def findWorkers(self, task):
|
||||||
workers = []
|
workers = []
|
||||||
|
|
Loading…
Reference in a new issue