playing with sockets. lets stick with http for now

p2p
quadrismegistus 4 years ago
parent 2b66a729fd
commit 03cc320ad2

@ -0,0 +1,316 @@
## CONFIG
# change this to your external ip address for your server
#(needs to be external to allow tor routing)
SERVER_ADDR = '128.232.229.63:5555'
# imports
from kivy.uix.screenmanager import Screen,ScreenManager
from kivymd.app import MDApp
from kivymd.uix.button import MDFillRoundFlatButton, MDIconButton
from kivymd.uix.toolbar import MDToolbar
from kivymd.uix.screen import MDScreen
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivymd.theming import ThemeManager
from kivy.properties import ObjectProperty,ListProperty
import time,os
from collections import OrderedDict
from functools import partial
from kivy.uix.screenmanager import NoTransition
from kivymd.uix.label import MDLabel
from kivy.uix.widget import Widget
from kivymd.uix.list import OneLineListItem
from kivymd.uix.card import MDCard, MDSeparator
from kivymd.uix.boxlayout import MDBoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.metrics import dp
from kivy.properties import NumericProperty
from kivymd.uix.list import * #MDList, ILeftBody, IRightBody, ThreeLineAvatarListItem, TwoLineAvatarListItem, BaseListItem, ImageLeftWidget
from kivy.uix.image import Image, AsyncImage
import requests,json
from kivy.storage.jsonstore import JsonStore
from kivy.core.window import Window
from kivy.core.text import LabelBase
import shutil
Window.size = (640, 1136) #(2.65 * 200, 5.45 * 200)
def log(x):
with open('log.txt','a+') as of:
of.write(str(x)+'\n')
class MyLayout(MDBoxLayout):
scr_mngr = ObjectProperty(None)
post_id = ObjectProperty()
def change_screen(self, screen, *args):
self.scr_mngr.current = screen
def view_post(self,post_id):
self.post_id=post_id
self.change_screen('view')
class MyBoxLayout(MDBoxLayout): pass
class MyLabel(MDLabel): pass
#### LOGIN
def get_tor_proxy_session():
session = requests.session()
# Tor uses the 9050 port as the default socks port
session.proxies = {'http': 'socks5://127.0.0.1:9050',
'https': 'socks5://127.0.0.1:9050'}
return session
def get_async_tor_proxy_session():
from requests_futures.sessions import FuturesSession
session = FuturesSession()
# Tor uses the 9050 port as the default socks port
session.proxies = {'http': 'socks5://127.0.0.1:9050',
'https': 'socks5://127.0.0.1:9050'}
return session
def get_tor_python_session():
from torpy.http.requests import TorRequests
with TorRequests() as tor_requests:
with tor_requests.get_session() as s:
return s
class MainApp(MDApp):
title = 'Komrade'
#api = 'http://localhost:5555/api'
api = 'http://%s/api' % SERVER_ADDR
#api = 'http://komrades.net:5555/api'
logged_in=False
store = JsonStore('komrade.json')
login_expiry = 60 * 60 * 24 * 7 # once a week
#login_expiry = 5 # 5 seconds
def get_session(self):
return get_async_tor_proxy_session()
# return get_tor_proxy_session()
#return get_tor_python_session()
def get_username(self):
if hasattr(self,'username'): return self.username
self.load_store()
if hasattr(self,'username'): return self.username
return ''
def build(self):
self.username=''
# bind
global app,root
app = self
#self.username = self.store.get('userd').get('username')
self.load_store()
self.root = root = Builder.load_file('root.kv')
# edit logo
logo=root.ids.toolbar.ids.label_title
logo.font_name='assets/Strengthen.ttf'
logo.font_size='58dp'
logo.pos_hint={'center_y':0.43}
# icons
icons=root.ids.toolbar.ids.right_actions.children
for icon in icons:
#log(dir(icon))
#icon.icon='android' #user_font_size='200sp'
icon.font_size='58dp'
icon.user_font_size='58dp'
icon.width='58dp'
icon.size_hint=(None,None)
icon.height='58dp'
if not self.is_logged_in():
self.root.change_screen('login')
log(self.username)
else:
# self.root.post_id=190
self.root.change_screen('feed')
return self.root
def load_store(self):
if not self.store.exists('user'): return
userd=self.store.get('user')
if not userd: userd={}
self.logged_in_when = userd.get('logged_in_when')
self.username = userd.get('username','')
def is_logged_in(self,just_check_timestamp=True, use_caching=True):
if self.logged_in: return True
if not use_caching: return False
###
if not self.store.exists('user'): return False
userd=self.store.get('user')
if not userd: userd={}
if userd.get('logged_in'):
un=userd.get('username')
timestamp=userd.get('logged_in_when')
# just a time check
if timestamp and just_check_timestamp:
if time.time() - timestamp < self.login_expiry:
self.logged_in=True
#self.username=un
return True
return False
def save_login(self,un):
self.logged_in=True
self.username=un
# self.store.put('username',un)
self.store.put('user',username=un,logged_in=True,logged_in_when=time.time())
self.root.change_screen('feed')
def login(self,un=None,pw=None):
url = self.api+'/login'
with self.get_session() as sess:
#res = requests.post(url, json={'name':un, 'passkey':pw})
res = sess.post(url, json={'name':un, 'passkey':pw})
if res.status_code==200:
data=res.json()
self.save_login(un)
return True
else:
# self.root.ids.login_status.text=res.text
return False
def register(self,un,pw):
url = self.api+'/register'
with self.get_session() as sess:
#res = requests.post(url, json={'name':un, 'passkey':pw})
res = sess.post(url, json={'name':un, 'passkey':pw})
if res.status_code==200:
self.save_login(un)
else:
pass
#self.root.ids.login_status.text=res.text
def post(self, content='', img_src=[]):
log('content: '+str(content))
log('img_src: '+str(img_src))
jsond = {'content':str(content)}
# upload?
filename=img_src[0] if img_src and os.path.exists(img_src[0]) else ''
url_upload=self.api+'/upload'
url_post = self.api+'/post'
server_filename=''
media_uid=None
with self.get_session() as sess:
if filename:
log(filename)
# copy file to cache
self.root.ids.add_post_screen.ids.post_status.text='Uploading file'
with sess.post(url_upload,files={'file':open(filename,'rb')}) as r1:
if r1.status_code==200:
rdata1 = r1.json()
server_filename = rdata1.get('filename','')
media_uid=rdata1.get('media_uid')
if server_filename:
self.root.ids.add_post_screen.ids.post_status.text='File uploaded'
# pre-cache
cache_filename = os.path.join('cache','img',server_filename)
cache_filedir = os.path.dirname(cache_filename)
if not os.path.exists(cache_filedir): os.makedirs(cache_filedir)
shutil.copyfile(filename,cache_filename)
# add post
self.root.ids.add_post_screen.ids.post_status.text='Creating post'
jsond={'img_src':server_filename, 'content':content, 'username':self.username, 'media_uid':media_uid}
# post
with sess.post(url_post, json=jsond) as r2:
log('got back from post: ' + r2.text)
rdata2 = r2.json()
post_id = rdata2.get('post_id',None)
if post_id:
self.root.ids.add_post_screen.ids.post_status.text='Post created'
self.root.view_post(post_id)
# pre-cache
with open(os.path.join('cache','json',str(post_id)+'.json'),'w') as of:
json.dump(jsond, of)
def get_post(self,post_id):
# get json from cache?
ofn_json = os.path.join('cache','json',str(post_id)+'.json')
if os.path.exists(ofn_json):
with open(ofn_json) as f:
jsond = json.load(f)
else:
with self.get_session() as sess:
with sess.get(self.api+'/post/'+str(post_id)) as r:
jsond = r.json()
# cache it!
with open(ofn_json,'w') as of:
json.dump(jsond, of)
return jsond
def get_posts(self):
with self.get_session() as sess:
with sess.get(self.api+'/posts') as r:
log(r.text)
jsond=r.json()
return jsond['posts']
return []
def get_image(self, img_src):
# is there an image?
if not img_src: return
# is it cached?
ofn_image = os.path.join('cache','img',img_src)
if not os.path.exists(ofn_image):
# create dir?
ofn_image_dir = os.path.split(ofn_image)[0]
if not os.path.exists(ofn_image_dir): os.makedirs(ofn_image_dir)
log('getting image!')
with self.get_session() as sess:
with sess.get(self.api+'/download/'+img_src,stream=True) as r:
with open(ofn_image,'wb') as of:
shutil.copyfileobj(r.raw, of)
return ofn_image
if __name__ == '__main__':
App = MainApp()
App.run()

@ -0,0 +1,102 @@
#:include screens/login/login.kv
#:include screens/feed/feed.kv
#:include screens/post/post.kv
#:include screens/messages/messages.kv
#:include screens/notifications/notifications.kv
#:import get_color_from_hex kivy.utils.get_color_from_hex
#:import images_path kivymd.images_path
#:import colors kivymd.color_definitions.colors
#:import partial functools.partial
#:import NoTransition kivy.uix.screenmanager.NoTransition
# :import MDCarousel kivymd.uix.carousel.MDCarousel
## CLASS DEFS
<MyBoxLayout>:
orientation: "vertical"
pos_hint: {'center_x':0.5, 'center_y':0.5}
size_hint:0.5,0.5
padding:'10dp'
md_bg_color:0,0,0,1
canvas:
Color:
rgb: 1,0,0,2
Line:
width: 1
rectangle: (self.x, self.y, self.width, self.height)
<MyLabel>:
theme_text_color: 'Custom'
text_color: (1,0,0,1)
pos_hint: {'center_y': 0.5}
halign: 'center'
height: self.texture_size[1]
font_family: 'Courier'
### LAYOUT
MyLayout:
scr_mngr: scr_mngr
orientation: 'vertical'
height: self.minimum_height
canvas:
Color:
rgba: 0.925,0.925,0.925,1 #get_color_from_hex(colors['Gray']['900'])
Rectangle:
pos: self.pos
size: self.size
source: 'assets/komrade2.png'
MDToolbar:
id: toolbar
title: app.title
pos_hint: {'center_x': .5, 'center_y': 0.95}
md_bg_color: 0.1,0.1,0.1,1
background_palette: 'Red'
background_hue: '500'
specific_text_color: 1,0,0,1
right_action_items: [['post-outline', partial(root.change_screen, 'feed')], ['pencil-plus-outline', partial(root.change_screen, 'post')], ['message-processing-outline', partial(root.change_screen, 'messages')], ['bell-outline', partial(root.change_screen, 'notifications')], ['account-circle-outline', partial(root.change_screen, 'notifications')]]
#left_action_items: [[f"assets/fist2.png", partial(root.change_screen, 'feed')]]
font_context: None
font_name: f'assets/Strengthen.ttf'
ScreenManager:
id: scr_mngr
# transition: NoTransition()
LoginScreen:
id: login_screen
FeedScreen:
id: feed_screen
AddPostScreen:
id: add_post_screen
ViewPostScreen:
id: view_post_screen
MessagesScreen:
id: messages_screen
NotificationsScreen:
id: notifications_screen

@ -78,6 +78,15 @@ def get_tor_proxy_session():
'https': 'socks5://127.0.0.1:9050'}
return session
def get_async_tor_proxy_session():
from requests_futures.sessions import FuturesSession
session = FuturesSession()
# Tor uses the 9050 port as the default socks port
session.proxies = {'http': 'socks5://127.0.0.1:9050',
'https': 'socks5://127.0.0.1:9050'}
return session
def get_tor_python_session():
from torpy.http.requests import TorRequests
with TorRequests() as tor_requests:
@ -96,6 +105,7 @@ class MainApp(MDApp):
#login_expiry = 5 # 5 seconds
def get_session(self):
# return get_async_tor_proxy_session()
return get_tor_proxy_session()
#return get_tor_python_session()
@ -170,7 +180,7 @@ class MainApp(MDApp):
self.logged_in=True
self.username=un
# self.store.put('username',un)
self.store.put('user',username=un,logged_in=True,logged_in_when=time.time())
# self.store.put('user',username=un,logged_in=True,logged_in_when=time.time())
self.root.change_screen('feed')
@ -180,6 +190,7 @@ class MainApp(MDApp):
with self.get_session() as sess:
#res = requests.post(url, json={'name':un, 'passkey':pw})
res = sess.post(url, json={'name':un, 'passkey':pw})
log(res.text)
if res.status_code==200:
data=res.json()
@ -198,7 +209,8 @@ class MainApp(MDApp):
if res.status_code==200:
self.save_login(un)
else:
self.root.ids.login_status.text=res.text
pass
#self.root.ids.login_status.text=res.text
def post(self, content='', img_src=[]):
log('content: '+str(content))
@ -213,6 +225,7 @@ class MainApp(MDApp):
url_post = self.api+'/post'
server_filename=''
media_uid=None
with self.get_session() as sess:
if filename:
@ -224,6 +237,7 @@ class MainApp(MDApp):
if r1.status_code==200:
rdata1 = r1.json()
server_filename = rdata1.get('filename','')
media_uid=rdata1.get('media_uid')
if server_filename:
self.root.ids.add_post_screen.ids.post_status.text='File uploaded'
@ -235,7 +249,7 @@ class MainApp(MDApp):
# add post
self.root.ids.add_post_screen.ids.post_status.text='Creating post'
jsond={'img_src':server_filename, 'content':content, 'username':self.username}
jsond={'img_src':server_filename, 'content':content, 'username':self.username, 'media_uid':media_uid}
@ -246,7 +260,7 @@ class MainApp(MDApp):
post_id = rdata2.get('post_id',None)
if post_id:
self.root.ids.add_post_screen.ids.post_status.text='Post created'
self.root.view_post(int(post_id))
self.root.view_post(post_id)
# pre-cache
with open(os.path.join('cache','json',str(post_id)+'.json'),'w') as of:
@ -279,6 +293,19 @@ class MainApp(MDApp):
jsond=r.json()
return jsond['posts']
return []
def get_posts_async(self):
result=[]
with self.get_session() as sess:
futures = [sess.get(self.api+'/posts')]
for future in as_completed(futures):
log('second?')
r=future.result()
log(r.text)
jsond=r.json()
result=jsond['posts']
log('first?')
return result
def get_image(self, img_src):
# is there an image?

@ -133,6 +133,7 @@ class FeedScreen(ProtectedScreen):
i=0
lim=25
for i,post in enumerate(reversed(self.app.get_posts())):
log('third?')
#if ln.startswith('@') or ln.startswith('RT '): continue
#i+=1
if i>lim: break

@ -0,0 +1,10 @@
Traceback (most recent call last):
File "/home/ryan/github/Komrade/venv/lib/python3.6/site-packages/twisted/application/app.py", line 312, in runReactorWithLogging
reactor.run()
File "/home/ryan/github/Komrade/venv/lib/python3.6/site-packages/twisted/internet/base.py", line 1282, in run
self.startRunning(installSignalHandlers=installSignalHandlers)
File "/home/ryan/github/Komrade/venv/lib/python3.6/site-packages/twisted/internet/base.py", line 1262, in startRunning
ReactorBase.startRunning(self)
File "/home/ryan/github/Komrade/venv/lib/python3.6/site-packages/twisted/internet/base.py", line 765, in startRunning
raise error.ReactorNotRestartable()
twisted.internet.error.ReactorNotRestartable

@ -0,0 +1,129 @@
from neomodel import *
NEO4J_USERNAME='neo4j'
NEO4J_PASSWORD='driveyourplowoverthebonesofthedead'
NEO4J_SERVER='localhost'
NEO4J_PORT=7687
config.DATABASE_URL = f'bolt://{NEO4J_USERNAME}:{NEO4J_PASSWORD}@{NEO4J_SERVER}:{NEO4J_PORT}' # default
config.ENCRYPTED_CONNECTION = False
# exit()
# G = Graph(password='drive your plow over the bones of the dead')
# Nodes = NodeMatcher(G)
# Edges = RelationshipMatcher(G)
class Media(StructuredNode):
uid=UniqueIdProperty()
ext=StringProperty()
@property
def filename(self):
return self.uid[:3]+'/'+self.uid[3:]+self.ext
@property
def data(self):
return {'uid':self.uid, 'ext':self.ext, 'filename':self.filename}
class Person(StructuredNode):
uid = UniqueIdProperty()
name = StringProperty(unique_index=True, required=True)
name_irl = StringProperty()
passkey = StringProperty()
wrote = RelationshipTo('Post','WROTE')
follows = RelationshipTo('Person','FOLLOWS')
followed_by = RelationshipFrom('Person','FOLLOWS')
located_in = RelationshipTo('Place','LOCATED_IN')
in_group = RelationshipTo('Group','IN_GROUP')
based_in = RelationshipTo('Place','LOCATED')
has_avatar = RelationshipTo('Media','HAS_MEDIA')
@property
def img_src_avatar(self):
avatar = self.has_avatar.first()
if avatar:
return avatar.filename
return ''
@property
def data(self):
img_avatar
dx={'uid':self.uid, 'name':self.name, 'name_irl':self.name_irl,
'img_src_avatar':self.img_src_avatar}
class Post(StructuredNode):
# properties
uid = UniqueIdProperty()
content = StringProperty()
has_media = RelationshipTo('Media','HAS_MEDIA')
# relations
written_by = RelationshipFrom('Person','WROTE')
located_in = RelationshipTo('Place','LOCATED')
@property
def author(self):
written_by = self.written_by
if not written_by: return None
person=written_by[0]
person.passkey=None
return person
@property
def img_src(self):
print(dir(self.has_media))
if self.has_media:
media = self.has_media[0]
return media.filename
return None
@property
def data(self):
return {'uid':self.uid, 'content':self.content, 'img_src':self.img_src}
class Place(StructuredNode):
# properties
uid = UniqueIdProperty()
name = StringProperty(unique_index=True)
posted_in = RelationshipFrom('Post','LOCATED')
based_in = RelationshipFrom('Person','LOCATED')
@property
def data(self):
return {'uid':self.uid, 'name':self.name}
class Group(StructuredNode):
uid = UniqueIdProperty()
name = StringProperty(unique_index=True)
motto = StringProperty()
mission = StringProperty()
has_members = RelationshipFrom('Person','IN_GROUP')
@property
def data(self):
return {'uid':self.uid, 'name':self.name, 'motto':self.motto, 'mission':self.mission}
def test_models():
jim = Person(name='Jim').save()
post = Post(content='woooo').save()
jim.wrote.connect(post)
print(dir(post))
print(jim.uid, post.uid)
print(list(post.written_by.all()))
print(post.author)
if __name__ == '__main__':
test_models()

@ -98,7 +98,5 @@ def test_models():
for a in [x,y,p1,p2,p3,g1,c1,c2]: G.push(a)
# test_models()
if __name__ == '__main__':
test_models()

@ -0,0 +1,233 @@
import os,time
from pathlib import Path
from models import *
from werkzeug.utils import secure_filename
from werkzeug.security import generate_password_hash,check_password_hash
from twisted.internet import defer
from twisted.internet.defer import ensureDeferred
from twisted.logger import Logger
log = Logger()
from klein import Klein
import json
jsonify = json.dumps
# Start server
app = Klein()
app.config = {}
app.config["DEBUG"] = True
app.config['SECRET_KEY'] = 'Bring out number weight & measure in a year of dearth'
# socketio = SocketIO(app)
app.config['UPLOAD_DIR'] = 'uploads/'
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
## errors
@app.route('/')
def home(request): return jsonify({'error':'404 go home friend'})
# Api Functions
def read_request_json(request):
json_str = str(request.content.read().decode('utf-8'))
# print('json_str',type(json_str),json_str)
return json.loads(json_str)
def get_person(name):
print('sleeping...')
time.sleep(10)
person = Person.nodes.get_or_none(name=name)
print('returning?')
return person
## LOGIN
@app.route('/api/login',methods=['POST'])
def login(request):
data=read_request_json(request)
print('data',data)
name=data.get('name','')
passkey=data.get('passkey','')
if not (name and passkey):
request.setResponseCode(400)
return jsonify({'error':'Login failed'})
# print('sleeping...')
# time.sleep(10)
# person = yield Person.nodes.get_or_none(name=name)
# print('sleeping...?')
person = get_person(name)
if person is None:
request.setResponseCode(401)
return jsonify({'error':'User exists'})
real_passkey = person.passkey
if not check_password_hash(real_passkey, passkey):
request.setResponseCode(401)
return jsonify({'error':'Login failed'})
request.setResponseCode(200)
return jsonify({'success':'Login success'})
@app.route('/api/register',methods=['POST'])
def register(request):
data=read_request_json(request)
name=data.get('name','')
passkey=data.get('passkey','')
if not (name and passkey):
request.setResponseCode(400)
return {'error':'Register failed'},status.HTTP_400_BAD_REQUEST
person = Person.nodes.get_or_none(name=name)
if person is not None:
request.setResponseCode(401)
return {'error':'User exists'}
passkey = generate_password_hash(passkey)
person = Person(name=name,passkey=passkey).save()
print('REGISTERED!',data)
return {'success':'Account created', 'username':name},status.HTTP_200_OK
## CREATE
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route('/api/upload',methods=['POST'])
def upload(request):
files = request.files
# check if the post request has the file part
if 'file' not in request.files:
return {'error':'No file found'},status.HTTP_204_NO_CONTENT
file = request.files['file']
# if user does not select file, browser also
# submit an empty part without filename
print('filename!',file.filename)
if file.filename == '':
return {'error':'No filename'},status.HTTP_206_PARTIAL_CONTENT
if file and allowed_file(file.filename):
print('uploading file...')
#prefix,filename = get_random_filename(file.filename) #secure_filename(file.filename)
#odir = os.path.join(app.config['UPLOAD_DIR'], os.path.dirname(filename))
#if not os.path.exists(odir):
ext = os.path.splitext(file.filename)[-1]
media = Media(ext=ext).save()
uid = media.uid
filename = media.filename
prefix,fn=filename.split('/')
folder = os.path.join(app.config['UPLOAD_DIR'], prefix)
if not os.path.exists(folder): os.makedirs(folder)
file.save(os.path.join(folder, fn))
#return redirect(url_for('uploaded_file', filename=filename))
return {'media_uid':uid, 'filename':filename}, status.HTTP_200_OK
return {'error':'Upload failed'},status.HTTP_406_NOT_ACCEPTABLE
@app.route('/api/post',methods=['POST'])
@app.route('/api/post/<post_id>',methods=['GET'])
def post(request,post_id=None):
if request.method == 'POST':
# get data
data=read_request_json(request)
print('POST /api/post:',data)
# make post
post = Post(content=data.get('content','')).save()
# attach author
name = data.get('username')
if name:
author = Person.nodes.get_or_none(name=name)
author.wrote.connect(post)
# attach media
media_uid=data.get('media_uid')
if media_uid:
media=Media.nodes.get_or_none(uid=media_uid)
post.has_media.connect(media)
# return
post_id=post.uid
print('created new post!',post_id)
return {'post_id':post_id},status.HTTP_200_OK
print('got post id!',post_id)
post = Post.nodes.get_or_none(uid=post_id)
if not post: return {},status.HTTP_204_NO_CONTENT
return post.data,status.HTTP_200_OK
@app.route('/api/download/<prefix>/<filename>',methods=['GET'])
def download(request, prefix, filename):
filedir = os.path.join(app.config['UPLOAD_DIR'], prefix)
print(filedir, filename)
return send_from_directory(filedir, filename)
### READ
@app.route('/api/followers/<name>')
def get_followers(request, name=None):
person = Person.match(G, name).first()
data = [p.data for p in person.followers]
return jsonify(data)
@app.route('/api/followers/<name>')
def get_follows(request, name=None):
person = Person.match(G, name).first()
data = [p.data for p in person.follows]
return jsonify(data)
@app.route('/api/posts')
@app.route('/api/posts/<name>')
def get_posts(request, name=None):
if name:
person = Person.nodes.get_or_none(name=name)
data = [p.data for p in person.wrote.all] if person is not None else []
else:
data = [p.data for p in Post.nodes.all()]
# print(data)
return jsonify({'posts':data})
@app.route('/api/post/<int:id>')
def get_post(request, id=None):
post = Post.match(G, int(id)).first()
data = post.data
return jsonify(data)
if __name__=='__main__':
app.run(host='0.0.0.0', port=5555)
# socketio.run(app, host='0.0.0.0', port=5000)

@ -0,0 +1,204 @@
import flask,os
from flask import request, jsonify, send_from_directory
from pathlib import Path
from models import *
from flask_api import FlaskAPI, status, exceptions
from werkzeug.security import generate_password_hash,check_password_hash
from flask import Flask, flash, request, redirect, url_for
from werkzeug.utils import secure_filename
# works better with tor?
import json
jsonify = json.dumps
# jsonify = str
# Start server
app = flask.Flask(__name__)
app.config["DEBUG"] = True
app.config['UPLOAD_DIR'] = 'uploads/'
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
@app.route('/')
def home(): return {'error':'404 go home friend'}
# Api Functions
## LOGIN
@app.route('/api/login',methods=['POST'])
def login():
data=request.json
name=data.get('name','')
passkey=data.get('passkey','')
if not (name and passkey):
return {'error':'Login failed'},status.HTTP_400_BAD_REQUEST
person = Person.nodes.get_or_none(name=name)
if person is None:
return {'error':'User exists'},status.HTTP_401_UNAUTHORIZED
real_passkey = person.passkey
if not check_password_hash(real_passkey, passkey):
return {'error':'Login failed'},status.HTTP_401_UNAUTHORIZED
return {'success':'Login success'},status.HTTP_200_OK
@app.route('/api/register',methods=['POST'])
def register():
data=request.json
name=data.get('name','')
passkey=data.get('passkey','')
if not (name and passkey):
return {'error':'Register failed'},status.HTTP_400_BAD_REQUEST
person = Person.nodes.get_or_none(name=name)
if person is not None:
return {'error':'User exists'},status.HTTP_401_UNAUTHORIZED
passkey = generate_password_hash(passkey)
person = Person(name=name,passkey=passkey).save()
print('REGISTERED!',data)
return {'success':'Account created', 'username':name},status.HTTP_200_OK
## CREATE
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
def get_random_filename(filename):
import uuid
fn=uuid.uuid4().hex
return (fn[:3],fn[3:]+os.path.splitext(filename)[-1])
@app.route('/api/upload',methods=['POST'])
def upload():
files = request.files
# check if the post request has the file part
if 'file' not in request.files:
return {'error':'No file found'},status.HTTP_204_NO_CONTENT
file = request.files['file']
# if user does not select file, browser also
# submit an empty part without filename
print('filename!',file.filename)
if file.filename == '':
return {'error':'No filename'},status.HTTP_206_PARTIAL_CONTENT
if file and allowed_file(file.filename):
print('uploading file...')
#prefix,filename = get_random_filename(file.filename) #secure_filename(file.filename)
#odir = os.path.join(app.config['UPLOAD_DIR'], os.path.dirname(filename))
#if not os.path.exists(odir):
ext = os.path.splitext(file.filename)[-1]
media = Media(ext=ext).save()
uid = media.uid
filename = media.filename
prefix,fn=filename.split('/')
folder = os.path.join(app.config['UPLOAD_DIR'], prefix)
if not os.path.exists(folder): os.makedirs(folder)
file.save(os.path.join(folder, fn))
#return redirect(url_for('uploaded_file', filename=filename))
return {'media_uid':uid, 'filename':filename}, status.HTTP_200_OK
return {'error':'Upload failed'},status.HTTP_406_NOT_ACCEPTABLE
@app.route('/api/post',methods=['POST'])
@app.route('/api/post/<post_id>',methods=['GET'])
def post(post_id=None):
if request.method == 'POST':
# get data
data=request.json
print('POST /api/post:',data)
# make post
post = Post(content=data.get('content','')).save()
# attach author
name = data.get('username')
if name:
author = Person.nodes.get_or_none(name=name)
author.wrote.connect(post)
# attach media
media_uid=data.get('media_uid')
if media_uid:
media=Media.nodes.get_or_none(uid=media_uid)
post.has_media.connect(media)
# return
post_id=post.uid
print('created new post!',post_id)
return {'post_id':post_id},status.HTTP_200_OK
print('got post id!',post_id)
post = Post.nodes.get_or_none(uid=post_id)
if not post: return {},status.HTTP_204_NO_CONTENT
return post.data,status.HTTP_200_OK
@app.route('/api/download/<prefix>/<filename>',methods=['GET'])
def download(prefix, filename):
filedir = os.path.join(app.config['UPLOAD_DIR'], prefix)
print(filedir, filename)
return send_from_directory(filedir, filename)
### READ
@app.route('/api/followers/<name>')
def get_followers(name=None):
person = Person.match(G, name).first()
data = [p.data for p in person.followers]
return jsonify(data)
@app.route('/api/followers/<name>')
def get_follows(name=None):
person = Person.match(G, name).first()
data = [p.data for p in person.follows]
return jsonify(data)
@app.route('/api/posts')
@app.route('/api/posts/<name>')
def get_posts(name=None):
if name:
person = Person.nodes.get_or_none(name=name)
data = [p.data for p in person.wrote.all] if person is not None else []
else:
data = [p.data for p in Post.nodes.all()]
# print(data)
return jsonify({'posts':data})
@app.route('/api/post/<int:id>')
def get_post(id=None):
post = Post.match(G, int(id)).first()
data = post.data
return jsonify(data)
app.run(host='0.0.0.0', port=5555)

@ -1,110 +1,129 @@
from neomodel import *
NEO4J_USERNAME='neo4j'
NEO4J_PASSWORD='drive your plow over the bones of the dead'
NEO4J_PASSWORD='driveyourplowoverthebonesofthedead'
NEO4J_SERVER='localhost'
NEO4J_PORT=7687
config.DATABASE_URL = f'bolt://{NEO4J_PASSWORD}:{NEO4J_PASSWORD}@{NEO4J_SERVER}:{NEO4J_PORT}' # default
config.DATABASE_URL = f'bolt://{NEO4J_USERNAME}:{NEO4J_PASSWORD}@{NEO4J_SERVER}:{NEO4J_PORT}' # default
config.ENCRYPTED_CONNECTION = False
exit()
G = Graph(password='drive your plow over the bones of the dead')
Nodes = NodeMatcher(G)
Edges = RelationshipMatcher(G)
# exit()
# G = Graph(password='drive your plow over the bones of the dead')
# Nodes = NodeMatcher(G)
# Edges = RelationshipMatcher(G)
class Media(StructuredNode):
uid=UniqueIdProperty()
ext=StringProperty()
class MyGraphObject(GraphObject):
@property
def data(self):
return dict(self.__ogm__.node)
def filename(self):
return self.uid[:3]+'/'+self.uid[3:]+self.ext
class Person(MyGraphObject):
__primarykey__ = 'name'
name = Property()
name_irl = Property()
passkey = Property()
posts = RelatedTo('Post','WROTE')
follows = RelatedTo('Person','FOLLOWS')
followers = RelatedFrom('Person','FOLLOWS')
based_in = RelatedTo('Place','BASED_IN')
groups = RelatedTo('Group','IN_GROUP')
avatar = RelatedTo('Media','HAS_MEDIA')
@property
def data(self):
return {'uid':self.uid, 'ext':self.ext, 'filename':self.filename}
class Post(MyGraphObject):
# properties
title = Property()
content = Property()
img_src = Property()
# relations
author = RelatedFrom('Person','WROTE')
location = RelatedTo('Place','BASED_IN')
class Person(StructuredNode):
uid = UniqueIdProperty()
name = StringProperty(unique_index=True, required=True)
name_irl = StringProperty()
passkey = StringProperty()
wrote = RelationshipTo('Post','WROTE')
follows = RelationshipTo('Person','FOLLOWS')
followed_by = RelationshipFrom('Person','FOLLOWS')
located_in = RelationshipTo('Place','LOCATED_IN')
in_group = RelationshipTo('Group','IN_GROUP')
based_in = RelationshipTo('Place','LOCATED')
has_avatar = RelationshipTo('Media','HAS_MEDIA')
@property
def img_src_avatar(self):
avatar = self.has_avatar.first()
if avatar:
return avatar.filename
return ''
@property
def data(self):
dx=dict(self.__ogm__.node)
authors=list(self.author)
locations=list(self.location)
dx['author']=authors[0].name if authors else ''
dx['location']=locations[0].name if locations else ''
img_avatar
dx={'uid':self.uid, 'name':self.name, 'name_irl':self.name_irl,
'img_src_avatar':self.img_src_avatar}
return dx
class Place(MyGraphObject):
class Post(StructuredNode):
# properties
__primarykey__ = 'name'
name = Property()
# relations
citizens = RelatedFrom('Person','BASED_IN')
class Group(MyGraphObject):
# properties
__primarykey__ = 'name'
name = Property()
motto = Property()
mission = Property()
uid = UniqueIdProperty()
content = StringProperty()
has_media = RelationshipTo('Media','HAS_MEDIA')
# relations
members = RelatedFrom('Person','IN_GROUP')
written_by = RelationshipFrom('Person','WROTE')
located_in = RelationshipTo('Place','LOCATED')
@property
def author(self):
written_by = self.written_by
if not written_by: return None
person=written_by[0]
person.passkey=None
return person
@property
def img_src(self):
print(dir(self.has_media))
if self.has_media:
media = self.has_media[0]
return media.filename
return None
def test_models():
x = Person(); x.name='MrX'
y = Person(); y.name='MrY'
p1 = Post(); p1.title='Post 1'; p1.content='Hello world!'
p2 = Post(); p2.title='Post 2'; p2.content='Hello world!!!'
p3 = Post(); p3.title='Post 3'; p3.content='Hello world!!!!!!!!!!!!'
@property
def data(self):
return {'uid':self.uid, 'content':self.content, 'img_src':self.img_src}
x.follows.add(y)
x.posts.add(p1)
y.posts.add(p2)
G.push(y)
y.posts.add(p3)
class Place(StructuredNode):
# properties
uid = UniqueIdProperty()
name = StringProperty(unique_index=True)
posted_in = RelationshipFrom('Post','LOCATED')
based_in = RelationshipFrom('Person','LOCATED')
g1=Group(); g1.name='BlocBloc'
c1=Place(); c1.name='Utopia'
c2=Place(); c2.name='Dystopia'
@property
def data(self):
return {'uid':self.uid, 'name':self.name}
class Group(StructuredNode):
uid = UniqueIdProperty()
name = StringProperty(unique_index=True)
motto = StringProperty()
mission = StringProperty()
x.based_in.add(c1)
y.based_in.add(c2)
has_members = RelationshipFrom('Person','IN_GROUP')
x.groups.add(g1)
y.groups.add(g1)
@property
def data(self):
return {'uid':self.uid, 'name':self.name, 'motto':self.motto, 'mission':self.mission}
for a in [x,y,p1,p2,p3,g1,c1,c2]: G.push(a)
def test_models():
jim = Person(name='Jim').save()
post = Post(content='woooo').save()
jim.wrote.connect(post)
print(dir(post))
print(jim.uid, post.uid)
print(list(post.written_by.all()))
# test_models()
print(post.author)
if __name__ == '__main__':
test_models()

@ -0,0 +1,5 @@
from gevent.pywsgi import WSGIServer
from server import app
http_server = WSGIServer(('', 5555), app)
http_server.serve_forever()

@ -1 +1,3 @@
python3 server.py
# gunicorn -w 4 -b 0.0.0.0:5555 server:app
# python run.py
python server.py

@ -1,4 +1,4 @@
import flask,os
import flask,os,time
from flask import request, jsonify, send_from_directory
from pathlib import Path
from models import *
@ -8,6 +8,7 @@ from flask import Flask, flash, request, redirect, url_for
from werkzeug.utils import secure_filename
# works better with tor?
import json
jsonify = json.dumps
@ -20,6 +21,7 @@ app.config["DEBUG"] = True
app.config['UPLOAD_DIR'] = 'uploads/'
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
@app.route('/')
def home(): return {'error':'404 go home friend'}
@ -30,17 +32,19 @@ def home(): return {'error':'404 go home friend'}
@app.route('/api/login',methods=['POST'])
def login():
data=request.json
print('sleeping...')
gevent.sleep(10)
name=data.get('name','')
passkey=data.get('passkey','')
if not (name and passkey):
return {'error':'Login failed'},status.HTTP_400_BAD_REQUEST
persons = list(Person.match(G,name))
if not persons:
return {'error':'Login failed'},status.HTTP_401_UNAUTHORIZED
person = Person.nodes.get_or_none(name=name)
if person is None:
return {'error':'User exists'},status.HTTP_401_UNAUTHORIZED
person = persons[0]
real_passkey = person.passkey
if not check_password_hash(real_passkey, passkey):
return {'error':'Login failed'},status.HTTP_401_UNAUTHORIZED
@ -57,20 +61,16 @@ def register():
if not (name and passkey):
return {'error':'Register failed'},status.HTTP_400_BAD_REQUEST
person = list(Person.match(G,name))
if person:
person = Person.nodes.get_or_none(name=name)
if person is not None:
return {'error':'User exists'},status.HTTP_401_UNAUTHORIZED
passkey = generate_password_hash(passkey)
person = Person()
person.name = name
person.passkey = passkey
G.push(person)
person = Person(name=name,passkey=passkey).save()
print('REGISTERED!',data)
return {'success':'Account created', 'username':name, 'passkey':passkey},status.HTTP_200_OK
return {'success':'Account created', 'username':name},status.HTTP_200_OK
@ -89,7 +89,7 @@ def get_random_filename(filename):
return (fn[:3],fn[3:]+os.path.splitext(filename)[-1])
@app.route('/api/upload',methods=['POST'])
def upload_file():
def upload():
files = request.files
# check if the post request has the file part
if 'file' not in request.files:
@ -105,20 +105,29 @@ def upload_file():
if file and allowed_file(file.filename):
print('uploading file...')
prefix,filename = get_random_filename(file.filename) #secure_filename(file.filename)
#prefix,filename = get_random_filename(file.filename) #secure_filename(file.filename)
#odir = os.path.join(app.config['UPLOAD_DIR'], os.path.dirname(filename))
#if not os.path.exists(odir):
ext = os.path.splitext(file.filename)[-1]
media = Media(ext=ext).save()
uid = media.uid
filename = media.filename
prefix,fn=filename.split('/')
folder = os.path.join(app.config['UPLOAD_DIR'], prefix)
if not os.path.exists(folder): os.mkdir(folder)
file.save(os.path.join(folder, filename))
if not os.path.exists(folder): os.makedirs(folder)
file.save(os.path.join(folder, fn))
#return redirect(url_for('uploaded_file', filename=filename))
return {'filename':prefix+'/'+filename}, status.HTTP_200_OK
return {'media_uid':uid, 'filename':filename}, status.HTTP_200_OK
return {'error':'Upload failed'},status.HTTP_406_NOT_ACCEPTABLE
@app.route('/api/post',methods=['POST'])
@app.route('/api/post/<int:post_id>',methods=['GET'])
@app.route('/api/post/<post_id>',methods=['GET'])
def post(post_id=None):
if request.method == 'POST':
@ -127,31 +136,30 @@ def post(post_id=None):
print('POST /api/post:',data)
# make post
post = Post()
post.content = data.get('content','')
post.img_src = data.get('img_src','')
G.push(post)
# attach to author
username=data.get('username','')
author = Person.match(G, username).first()
print('author?', author)
author.posts.add(post)
# post.author.add(author)
G.push(author)
post = Post(content=data.get('content','')).save()
# attach author
name = data.get('username')
if name:
author = Person.nodes.get_or_none(name=name)
author.wrote.connect(post)
# attach media
media_uid=data.get('media_uid')
if media_uid:
media=Media.nodes.get_or_none(uid=media_uid)
post.has_media.connect(media)
# return
post_id=str(post.__ogm__.node.identity)
post_id=post.uid
print('created new post!',post_id)
return {'post_id':post_id},status.HTTP_200_OK
print('got post id!',post_id)
posts = list(Post.match(G,post_id))
if not posts:
return {},status.HTTP_204_NO_CONTENT
post=posts[0]
print(post.data)
post = Post.nodes.get_or_none(uid=post_id)
if not post: return {},status.HTTP_204_NO_CONTENT
return post.data,status.HTTP_200_OK
@app.route('/api/download/<prefix>/<filename>',methods=['GET'])
@ -180,32 +188,13 @@ def get_follows(name=None):
@app.route('/api/posts/<name>')
def get_posts(name=None):
if name:
person = Person.match(G, name).first()
data = [p.data for p in person.posts]
person = Person.nodes.get_or_none(name=name)
data = [p.data for p in person.wrote.all] if person is not None else []
else:
# data=[]
# def handle_row(row):
# node = row[0]
# data+=[node.data] # do something with `node` here
# G.match
# G.cypher.execute("START z=node(*) RETURN z", row_handler=handle_row)
matcher = NodeMatcher(G)
posts = matcher.match('Post')
# posts = Post.match(G).where("_.content = '*'")
def to_data(post):
d=dict(post)
d['id']=post.identity
# authors=list(post.author)
# locations=list(post.location)
# d['author']=authors[0].name if authors else ''
# d['location']=locations[0].name if locations else ''
return d
data = {"posts":[to_data(post) for post in posts]}
data = [p.data for p in Post.nodes.all()]
# print(data)
return jsonify(data)
return jsonify({'posts':data})
@app.route('/api/post/<int:id>')
def get_post(id=None):
@ -216,5 +205,10 @@ def get_post(id=None):
app.run(host='0.0.0.0', port=5555)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5555)
# socketio.run(app,host='0.0.0.0', port=5555)
# from gevent import pywsgi
# from geventwebsocket.handler import WebSocketHandler
# server = pywsgi.WSGIServer(('', 5555), app, handler_class=WebSocketHandler)
# server.serve_forever()

@ -0,0 +1,6 @@
2020-08-12T14:13:34+0100 [twisted.logger._global#warn] Warning: primary log target selected twice at </home/ryan/github/Komrade/venv/lib/python3.6/site-packages/twisted/application/app.py:213> - previously selected at </home/ryan/github/Komrade/venv/lib/python3.6/site-packages/twisted/python/log.py:214>. Remove one of the calls to beginLoggingTo.
2020-08-12T14:13:34+0100 [twisted.scripts._twistd_unix.UnixAppLogger#info] twistd 20.3.0 (/home/ryan/github/Komrade/venv/bin/python 3.6.9) starting up.
2020-08-12T14:13:34+0100 [twisted.scripts._twistd_unix.UnixAppLogger#info] reactor class: twisted.internet.epollreactor.EPollReactor.
2020-08-12T14:13:34+0100 [-] Site starting on 8080
2020-08-12T14:13:34+0100 [twisted.web.server.Site#info] Starting factory <twisted.web.server.Site object at 0x7f91e00ba400>
2020-08-12T14:13:34+0100 [twisted.scripts._twistd_unix.UnixAppLogger#info] Server Shut Down.
Loading…
Cancel
Save