ZeroNet/src/Site/SiteManager.py

227 lines
7.9 KiB
Python
Raw Normal View History

import json
import logging
import re
import os
import time
2016-09-04 17:51:20 +02:00
import atexit
2016-04-20 23:28:58 +02:00
import gevent
import util
from Plugin import PluginManager
from Content import ContentDb
from Config import config
from util import helper
2019-08-02 14:06:25 +02:00
from util import RateLimit
from util import Cached
2016-04-20 23:28:58 +02:00
@PluginManager.acceptPlugins
class SiteManager(object):
def __init__(self):
2016-08-10 12:32:28 +02:00
self.log = logging.getLogger("SiteManager")
2016-08-16 18:14:02 +02:00
self.log.debug("SiteManager created.")
2018-02-08 18:05:50 +01:00
self.sites = {}
self.sites_changed = int(time.time())
self.loaded = False
2016-04-20 23:28:58 +02:00
gevent.spawn(self.saveTimer)
2017-08-09 14:21:44 +02:00
atexit.register(lambda: self.save(recalculate_size=True))
# Load all sites from data/sites.json
@util.Noparallel()
def load(self, cleanup=True, startup=False):
from Debug import Debug
self.log.info("Loading sites... (cleanup: %s, startup: %s)" % (cleanup, startup))
self.loaded = False
2019-03-15 21:06:59 +01:00
from .Site import Site
address_found = []
added = 0
load_s = time.time()
# Load new adresses
try:
json_path = "%s/sites.json" % config.data_dir
data = json.load(open(json_path))
except Exception as err:
raise Exception("Unable to load %s: %s" % (json_path, err))
sites_need = []
for address, settings in data.items():
if address not in self.sites:
if os.path.isfile("%s/%s/content.json" % (config.data_dir, address)):
# Root content.json exists, try load site
s = time.time()
try:
site = Site(address, settings=settings)
site.content_manager.contents.get("content.json")
2019-03-15 21:06:59 +01:00
except Exception as err:
self.log.debug("Error loading site %s: %s" % (address, err))
continue
self.sites[address] = site
self.log.debug("Loaded site %s in %.3fs" % (address, time.time() - s))
added += 1
elif startup:
# No site directory, start download
self.log.debug("Found new site in sites.json: %s" % address)
sites_need.append([address, settings])
added += 1
address_found.append(address)
# Remove deleted adresses
if cleanup:
2019-03-15 21:06:59 +01:00
for address in list(self.sites.keys()):
if address not in address_found:
del(self.sites[address])
self.log.debug("Removed site: %s" % address)
# Remove orpan sites from contentdb
content_db = ContentDb.getContentDb()
2017-07-31 14:35:01 +02:00
for row in content_db.execute("SELECT * FROM site").fetchall():
address = row["address"]
if address not in self.sites and address not in address_found:
self.log.info("Deleting orphan site from content.db: %s" % address)
2017-07-31 14:35:48 +02:00
try:
content_db.execute("DELETE FROM site WHERE ?", {"address": address})
except Exception as err:
self.log.error("Can't delete site %s from content_db: %s" % (address, err))
if address in content_db.site_ids:
del content_db.site_ids[address]
if address in content_db.sites:
del content_db.sites[address]
2016-09-04 17:52:14 +02:00
self.loaded = True
for address, settings in sites_need:
gevent.spawn(self.need, address, settings=settings)
if added:
self.log.info("Added %s sites in %.3fs" % (added, time.time() - load_s))
2019-08-02 14:06:25 +02:00
def saveDelayed(self):
RateLimit.callAsync("Save sites.json", allowed_again=5, func=self.save)
2017-08-09 14:21:44 +02:00
def save(self, recalculate_size=False):
2016-04-20 23:28:58 +02:00
if not self.sites:
self.log.debug("Save skipped: No sites found")
return
if not self.loaded:
self.log.debug("Save skipped: Not loaded")
return
2016-04-20 23:28:58 +02:00
s = time.time()
2016-08-10 12:33:34 +02:00
data = {}
# Generate data file
2017-08-09 14:21:44 +02:00
s = time.time()
2019-12-17 14:31:55 +01:00
for address, site in list(self.list().items()):
2017-08-09 14:21:44 +02:00
if recalculate_size:
2017-10-03 15:29:52 +02:00
site.settings["size"], site.settings["size_optional"] = site.content_manager.getTotalSize() # Update site size
2016-04-20 23:28:58 +02:00
data[address] = site.settings
data[address]["cache"] = site.getSettingsCache()
2017-08-09 14:21:44 +02:00
time_generate = time.time() - s
2017-08-09 14:21:44 +02:00
s = time.time()
if data:
helper.atomicWrite("%s/sites.json" % config.data_dir, helper.jsonDumps(data).encode("utf8"))
else:
self.log.debug("Save error: No data")
2017-08-09 14:21:44 +02:00
time_write = time.time() - s
# Remove cache from site settings
2019-03-15 21:06:59 +01:00
for address, site in self.list().items():
site.settings["cache"] = {}
2017-08-09 14:21:44 +02:00
self.log.debug("Saved sites in %.2fs (generate: %.2fs, write: %.2fs)" % (time.time() - s, time_generate, time_write))
2016-04-20 23:28:58 +02:00
def saveTimer(self):
while 1:
time.sleep(60 * 10)
2017-08-09 14:21:44 +02:00
self.save(recalculate_size=True)
2016-04-20 23:28:58 +02:00
# Checks if its a valid address
def isAddress(self, address):
return re.match("^[A-Za-z0-9]{26,35}$", address)
2017-05-11 18:02:05 +02:00
def isDomain(self, address):
return False
@Cached(timeout=10)
def isDomainCached(self, address):
return self.isDomain(address)
def resolveDomain(self, domain):
return False
@Cached(timeout=10)
def resolveDomainCached(self, domain):
return self.resolveDomain(domain)
# Return: Site object or None if not found
def get(self, address):
if self.isDomainCached(address):
address_resolved = self.resolveDomainCached(address)
if address_resolved:
address = address_resolved
if not self.loaded: # Not loaded yet
2018-04-28 21:48:36 +02:00
self.log.debug("Loading site: %s)..." % address)
self.load()
site = self.sites.get(address)
return site
def add(self, address, all_file=True, settings=None, **kwargs):
2019-09-28 17:01:37 +02:00
from .Site import Site
self.sites_changed = int(time.time())
# Try to find site with differect case
for recover_address, recover_site in list(self.sites.items()):
if recover_address.lower() == address.lower():
return recover_site
if not self.isAddress(address):
return False # Not address: %s % address
self.log.debug("Added new site: %s" % address)
config.loadTrackersFile()
site = Site(address, settings=settings)
self.sites[address] = site
if not site.settings["serving"]: # Maybe it was deleted before
site.settings["serving"] = True
site.saveSettings()
if all_file: # Also download user files on first sync
site.download(check_size=True, blind_includes=True)
return site
# Return or create site and start download site files
2020-02-13 17:23:37 +01:00
def need(self, address, *args, **kwargs):
if self.isDomainCached(address):
address_resolved = self.resolveDomainCached(address)
if address_resolved:
address = address_resolved
site = self.get(address)
if not site: # Site not exist yet
2020-02-13 17:23:37 +01:00
site = self.add(address, *args, **kwargs)
return site
def delete(self, address):
2018-02-08 18:05:50 +01:00
self.sites_changed = int(time.time())
self.log.debug("Deleted site: %s" % address)
del(self.sites[address])
# Delete from sites.json
2016-08-10 12:34:28 +02:00
self.save()
# Lazy load sites
def list(self):
2018-02-08 18:06:19 +01:00
if not self.loaded: # Not loaded yet
2016-08-10 12:32:28 +02:00
self.log.debug("Sites not loaded yet...")
self.load(startup=True)
return self.sites
site_manager = SiteManager() # Singletone
if config.action == "main": # Don't connect / add myself to peerlist
2019-01-20 03:26:12 +01:00
peer_blacklist = [("127.0.0.1", config.fileserver_port), ("::1", config.fileserver_port)]
else:
peer_blacklist = []