This commit is contained in:
j 2014-05-12 14:57:47 +02:00
commit 10d2f35b7b
20 changed files with 1334 additions and 629 deletions

View file

@ -20,8 +20,8 @@ class Changelog(db.Model):
editlist name {name: newname}
orderlists [name, name, name]
removelist name
additemtolist listname itemid
removeitemfromlist listname itemid
addlistitems listname [ids]
removelistitems listname [ids]
editusername username
editcontact string
addpeer peerid peername
@ -182,24 +182,17 @@ class Changelog(db.Model):
l.remove()
return True
def action_addlistitem(self, user, timestamp, name, itemid):
from item.models import Item
def action_addlistitems(self, user, timestamp, name, ids):
from user.models import List
l = List.get(user.id, name)
i = Item.get(itemid)
if l and i:
i.lists.append(l)
i.update()
l = List.get_or_create(user.id, name)
l.add_items(ids)
return True
def action_removelistitem(self, user, timestamp, name, itemid):
from item.models import Item
def action_removelistitem(self, user, timestamp, name, ids):
from user.models import List
l = List.get(user.id, name)
i = Item.get(itemid)
if l and i:
i.lists.remove(l)
i.update()
if l:
l.remove_items(ids)
return True
def action_editusername(self, user, timestamp, username):

View file

@ -6,7 +6,6 @@ from flask import json
from oxflask.api import actions
from oxflask.shortcuts import returns_json
from oml import utils
import query
import models
@ -118,6 +117,8 @@ def edit(request):
if item and keys and item.json()['mediastate'] == 'available':
key = keys[0]
print 'update mainid', key, data[key]
if key in ('isbn10', 'isbn13'):
data[key] = utils.normalize_isbn(data[key])
item.update_mainid(key, data[key])
response = item.json()
else:

View file

@ -233,6 +233,12 @@ class Item(db.Model):
else:
f.value = '%s:' % p.id
db.session.add(f)
for l in self.lists:
f = Find()
f.item_id = self.id
f.key = 'list'
f.value = l.find_id
db.session.add(f)
def update(self):
users = map(str, list(self.users))

104
oml/localnodes.py Normal file
View file

@ -0,0 +1,104 @@
# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
import socket
import thread
import json
import struct
from threading import Thread
from settings import preferences, server, USER_ID
from node.utils import get_public_ipv6
def can_connect(data):
try:
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
s.settimeout(1)
s.connect((data['host'], data['port']))
s.close()
return True
except:
pass
return False
class LocalNodes(Thread):
_active = True
_nodes = {}
_BROADCAST = "ff02::1"
_PORT = 9851
TTL = 2
def __init__(self, app):
self._app = app
Thread.__init__(self)
self.daemon = True
self.start()
self.host = get_public_ipv6()
self.send()
def send(self):
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
ttl = struct.pack('@i', self.TTL)
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_HOPS, ttl)
message = json.dumps({
'id': USER_ID,
'username': preferences.get('username', 'anonymous'),
'host': self.host,
'port': server['node_port'],
})
s.sendto(message + '\0', (self._BROADCAST, self._PORT))
def receive(self):
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('', self._PORT))
group_bin = socket.inet_pton(socket.AF_INET6, self._BROADCAST) + '\0'*4
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, group_bin)
while self._active:
data, addr = s.recvfrom(1024)
while data[-1] == '\0':
data = data[:-1] # Strip trailing \0's
data = self.validate(data)
if data:
if data['id'] not in self._nodes:
thread.start_new_thread(self.new_node, (data, ))
else:
print 'UPDATE NODE', data
self._nodes[data['id']] = data
def validate(self, data):
try:
data = json.loads(data)
except:
return None
for key in ['id', 'username', 'host', 'port']:
if key not in data:
return None
return data
def get(self, user_id):
if user_id in self._nodes:
if can_connect(self._nodes[user_id]):
return self._nodes[user_id]
def new_node(self, data):
print 'NEW NODE', data
if can_connect(data):
self._nodes[data['id']] = data
with self._app.app_context():
import user.models
u = user.models.User.get_or_create(data['id'])
u.info['username'] = data['username']
u.info['local'] = data
u.save()
self.send()
def run(self):
self.receive()
def join(self):
self._active = False
return Thread.join(self)

View file

@ -107,11 +107,8 @@ def start(app):
(r"/get/(.*)", ShareHandler, dict(app=app)),
(r".*", NodeHandler, dict(app=app)),
])
#tr = WSGIContainer(node_app)
#http_server= HTTPServer(tr)
http_server.listen(settings.server['node_port'], settings.server['node_address'])
host = utils.get_public_ipv4()
host = utils.get_public_ipv6()
state.online = directory.put(settings.sk, {
'host': host,
'port': settings.server['node_port']

View file

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
import socket
import requests
from urlparse import urlparse
def get_public_ipv6():
host = ('2a01:4f8:120:3201::3', 25519)

View file

@ -19,6 +19,7 @@ from changelog import Changelog
import directory
from websocket import trigger_event
from localnodes import LocalNodes
ENCODING='base64'
@ -26,8 +27,9 @@ class Node(object):
online = False
download_speed = 0
def __init__(self, app, user):
self._app = app
def __init__(self, nodes, user):
self._nodes = nodes
self._app = nodes._app
self.user_id = user.id
key = str(user.id)
self.vk = ed25519.VerifyingKey(key, encoding=ENCODING)
@ -35,10 +37,15 @@ class Node(object):
@property
def url(self):
if ':' in self.host:
url = 'http://[%s]:%s' % (self.host, self.port)
local = self.get_local()
if local:
url = 'http://[%s]:%s' % (local['host'], local['port'])
print 'using local peer discovery to access node', url
else:
url = 'http://%s:%s' % (self.host, self.port)
if ':' in self.host:
url = 'http://[%s]:%s' % (self.host, self.port)
else:
url = 'http://%s:%s' % (self.host, self.port)
return url
def resolve_host(self):
@ -51,6 +58,11 @@ class Node(object):
self.host = None
self.port = 9851
def get_local(self):
if self._nodes and self._nodes._local:
return self._nodes._local.get(self.user_id)
return None
def request(self, action, *args):
if not self.host:
self.resolve_host()
@ -211,6 +223,7 @@ class Nodes(Thread):
self._app = app
self._q = Queue()
self._running = True
self._local = LocalNodes(app)
Thread.__init__(self)
self.daemon = True
self.start()
@ -238,7 +251,7 @@ class Nodes(Thread):
def _add_node(self, user_id):
if user_id not in self._nodes:
from user.models import User
self._nodes[user_id] = Node(self._app, User.get_or_create(user_id))
self._nodes[user_id] = Node(self, User.get_or_create(user_id))
else:
self._nodes[user_id].online = True
trigger_event('status', {

View file

@ -144,6 +144,8 @@ class Parser(object):
l = self._list.query.filter_by(user_id=p.id, name=name).first()
else:
l = None
if l:
v = l.find_id
if l and l._query:
data = l._query
q = self.parse_conditions(data.get('conditions', []),

View file

@ -38,7 +38,7 @@ server_defaults = {
'port': 9842,
'address': '127.0.0.1',
'node_port': 9851,
'node_address': '::',
'node_address': '',
'extract_text': True,
'directory_service': 'http://[2a01:4f8:120:3201::3]:25519',
'lookup_service': 'http://data.openmedialibrary.com',

View file

@ -84,9 +84,9 @@ actions.register(getUsers)
@returns_json
def getLists(request):
lists = {}
lists = []
for u in models.User.query.filter((models.User.peered==True)|(models.User.id==settings.USER_ID)):
lists[u.id] = u.lists_json()
lists += u.lists_json()
return {
'lists': lists
}
@ -132,28 +132,24 @@ def editList(request):
actions.register(editList, cache=False)
@returns_json
def addListItem(request):
def addListItems(request):
data = json.loads(request.form['data']) if 'data' in request.form else {}
l = models.List.get_or_create(data['id'])
i = Item.get(data['item'])
if l and i:
l.items.append(i)
models.db.session.add(l)
i.update()
l = models.List.get_or_create(data['list'])
if l:
l.add_items(data['items'])
return l.json()
return {}
actions.register(addListItem, cache=False)
actions.register(addListItems, cache=False)
@returns_json
def removeListItem(request):
def removeListItems(request):
data = json.loads(request.form['data']) if 'data' in request.form else {}
l = models.List.get(data['id'])
i = Item.get(data['item'])
if l and i:
l.items.remove(i)
models.db.session.add(l)
i.update()
l = models.List.get(data['list'])
if l:
l.remove_items(data['items'])
return l.json()
return {}
actions.register(removeListItem, cache=False)
actions.register(removeListItems, cache=False)
@returns_json
def sortLists(request):

View file

@ -59,7 +59,7 @@ class User(db.Model):
return j
def check_online(self):
return state.nodes.check_online(self.id)
return state.nodes and state.nodes.check_online(self.id)
def lists_json(self):
return [l.json() for l in self.lists.order_by('position')]
@ -158,25 +158,39 @@ class List(db.Model):
from item.models import Item
for item_id in items:
i = Item.get(item_id)
self.items.add(i)
self.items.append(i)
i.update()
db.session.add(self)
db.session.commit()
for item_id in items:
i = Item.get(item_id)
i.update_lists()
db.session.commit()
if self.user_id == settings.USER_ID:
Changelog.record(self.user, 'addlistitems', self.name, items)
def remove_items(self, items):
from item.models import Item
for item_id in items:
i = Item.get(item_id)
self.items.remove(i)
i.update()
db.session.add(self)
db.session.commit()
for item_id in items:
i = Item.get(item_id)
i.update_lists()
db.session.commit()
if self.user_id == settings.USER_ID:
Changelog.record(self.user, 'removelistitems', self.name, items)
def remove(self):
if not self._query:
for i in self.items:
self.items.remove(i)
if not self._query:
print 'record change: removelist', self.user, self.name
Changelog.record(self.user, 'removelist', self.name)
if self.user_id == settings.USER_ID:
Changelog.record(self.user, 'removelist', self.name)
db.session.delete(self)
db.session.commit()
@ -184,10 +198,21 @@ class List(db.Model):
def public_id(self):
id = ''
if self.user_id != settings.USER_ID:
id += self.user_id
id = '%s:%s' % (id, self.name)
id += self.user.nickname
id = u'%s:%s' % (id, self.name)
return id
@property
def find_id(self):
id = ''
if self.user_id != settings.USER_ID:
id += self.user_id
id = u'%s:%s' % (id, self.id)
return id
def __repr__(self):
return self.public_id.encode('utf-8')
def items_count(self):
from item.models import Item
if self._query:
@ -199,6 +224,7 @@ class List(db.Model):
def json(self):
r = {
'id': self.public_id,
'user': self.user.nickname if self.user_id != settings.USER_ID else settings.preferences['username'],
'name': self.name,
'index': self.position,
'items': self.items_count(),