Forking what is left of ZeroNet and hopefully adding an AT Proto Frontend/Proxy
1import time
2import sqlite3
3import random
4import atexit
5
6import gevent
7from Plugin import PluginManager
8
9
10@PluginManager.registerTo("ContentDb")
11class ContentDbPlugin(object):
12 def __init__(self, *args, **kwargs):
13 atexit.register(self.saveAllPeers)
14 super(ContentDbPlugin, self).__init__(*args, **kwargs)
15
16 def getSchema(self):
17 schema = super(ContentDbPlugin, self).getSchema()
18
19 schema["tables"]["peer"] = {
20 "cols": [
21 ["site_id", "INTEGER REFERENCES site (site_id) ON DELETE CASCADE"],
22 ["address", "TEXT NOT NULL"],
23 ["port", "INTEGER NOT NULL"],
24 ["hashfield", "BLOB"],
25 ["reputation", "INTEGER NOT NULL"],
26 ["time_added", "INTEGER NOT NULL"],
27 ["time_found", "INTEGER NOT NULL"]
28 ],
29 "indexes": [
30 "CREATE UNIQUE INDEX peer_key ON peer (site_id, address, port)"
31 ],
32 "schema_changed": 2
33 }
34
35 return schema
36
37 def loadPeers(self, site):
38 s = time.time()
39 site_id = self.site_ids.get(site.address)
40 res = self.execute("SELECT * FROM peer WHERE site_id = :site_id", {"site_id": site_id})
41 num = 0
42 num_hashfield = 0
43 for row in res:
44 peer = site.addPeer(str(row["address"]), row["port"])
45 if not peer: # Already exist
46 continue
47 if row["hashfield"]:
48 peer.hashfield.replaceFromBytes(row["hashfield"])
49 num_hashfield += 1
50 peer.time_added = row["time_added"]
51 peer.time_found = row["time_found"]
52 peer.reputation = row["reputation"]
53 if row["address"].endswith(".onion"):
54 peer.reputation = peer.reputation / 2 - 1 # Onion peers less likely working
55 num += 1
56 if num_hashfield:
57 site.content_manager.has_optional_files = True
58 site.log.debug("%s peers (%s with hashfield) loaded in %.3fs" % (num, num_hashfield, time.time() - s))
59
60 def iteratePeers(self, site):
61 site_id = self.site_ids.get(site.address)
62 for key, peer in list(site.peers.items()):
63 address, port = key.rsplit(":", 1)
64 if peer.has_hashfield:
65 hashfield = sqlite3.Binary(peer.hashfield.tobytes())
66 else:
67 hashfield = ""
68 yield (site_id, address, port, hashfield, peer.reputation, int(peer.time_added), int(peer.time_found))
69
70 def savePeers(self, site, spawn=False):
71 if spawn:
72 # Save peers every hour (+random some secs to not update very site at same time)
73 site.greenlet_manager.spawnLater(60 * 60 + random.randint(0, 60), self.savePeers, site, spawn=True)
74 if not site.peers:
75 site.log.debug("Peers not saved: No peers found")
76 return
77 s = time.time()
78 site_id = self.site_ids.get(site.address)
79 cur = self.getCursor()
80 try:
81 cur.execute("DELETE FROM peer WHERE site_id = :site_id", {"site_id": site_id})
82 cur.executemany(
83 "INSERT INTO peer (site_id, address, port, hashfield, reputation, time_added, time_found) VALUES (?, ?, ?, ?, ?, ?, ?)",
84 self.iteratePeers(site)
85 )
86 except Exception as err:
87 site.log.error("Save peer error: %s" % err)
88 site.log.debug("Peers saved in %.3fs" % (time.time() - s))
89
90 def initSite(self, site):
91 super(ContentDbPlugin, self).initSite(site)
92 site.greenlet_manager.spawnLater(0.5, self.loadPeers, site)
93 site.greenlet_manager.spawnLater(60*60, self.savePeers, site, spawn=True)
94
95 def saveAllPeers(self):
96 for site in list(self.sites.values()):
97 try:
98 self.savePeers(site)
99 except Exception as err:
100 site.log.error("Save peer error: %s" % err)