js api started

master
Anton Lydike 4 years ago
parent f2e8e074b6
commit 36885409b8

@ -0,0 +1,100 @@
import server.models.models
from server.models.models import Jsonifiable
from neomodel import StructuredNode, StringProperty, RelationshipManager, \
UniqueIdProperty, DateProperty, StructuredRel, DateTimeProperty, StructuredRel, \
Property, RelationshipDefinition
import json
from typing import Type, Tuple, Dict, List, Optional, Generator
def constructor(cls: Type[StructuredNode]) -> str:
return 'constructor(json) {\n' + \
f' super(json)\n' + \
' }'
def get_set_attr(name, attr) -> str:
if RelationshipDefinition in attr.__class__.__mro__:
return f"""
get_{name}() {{
return this.json.{name}
}}
link_{name}(ref) {{
return this._link_property('{name}', ref)
}}
unlink_{name}(ref) {{
return this._unlink_property('{name}', ref)
}}
"""
else:
return f"""
get_{name}() {{
return this.json.{name}
}}
""" + f"""
set_{name}(new_val) {{
if (this.check('{name}', new_val)) {{
this.json.{name} = new_val
this.changes.{name} = new_val
}}
}}
""" if name != 'uid' else ''
def all_attrs(cls):
return '\n'.join([ get_set_attr(name, val) for name, val in get_relevant_attributes(cls)])
def js_representation(cls: Type[StructuredNode]) -> str:
props, rels = get_properties_and_relations(cls)
return f"""class {cls.__name__} extends StructuredNode {{
{constructor(cls)}
{all_attrs(cls)}
}}
StructuredNode.registerClassDefinition({cls.__name__}, {json.dumps(props)}, {json.dumps(rels)})
"""
def get_relevant_attributes(cls: Type[StructuredNode]) -> Generator[None, Tuple[str, Property], None]:
for name, value in vars(cls).items():
if name[0] == '_' or name[0].isupper() and not isinstance(value, (RelationshipDefinition, Property)):
continue
yield name, value
def get_properties_and_relations(cls) -> Tuple[Dict[str, dict], Dict[str, dict]]:
props = dict()
rels = dict()
for name, value in get_relevant_attributes(cls):
if isinstance(value, RelationshipDefinition):
target_cls = value._raw_class.__name__ if not isinstance(value._raw_class, str) else value._raw_class
rels[name] = {
'field': name,
'target': target_cls,
'cardinality': value.manager.__name__
}
elif isinstance(value, Property):
props[name] = {
'field': name,
'type': value.__class__.__name__,
'required': value.required
}
return props, rels
if __name__ == '__main__':
reps = list()
reps.append("// This file was generated by running js_conversion.py\n" +
"// It converts the neomodel declaration of this element into js classes.\n")
for name, cls in vars(server.models.models).items():
if hasattr(cls, '__mro__') and StructuredNode in cls.__mro__ and StructuredNode != cls.__mro__[0]:
reps.append(js_representation(cls))
print('\n\n\n'.join(reps))

@ -0,0 +1,221 @@
import sys
from server.models.models import Jsonifiable
from flask import Flask, request, Response, jsonify
from typing import Type, Final, List, Optional
from datetime import datetime
from neomodel import StructuredNode, RelationshipManager, RelationshipDefinition
from neomodel.cardinality import ZeroOrOne, ZeroOrMore, One, OneOrMore
import json
"""
This document declares some abstract api functions that will help you implement a REST api
for you StructuredNodes fast and efficiently.
The simplest way to achieve this is by using the register_api_object(cls: Type[StructuredNode])
method.
Example:
given the node type: (note that you need to add the mixin Jsonifiable)
class Person(StructuredNode, Jsonifiable):
uid = UniqueIdProperty()
name = StringProperty(required=True)
children = RelationshipTo('Person', 'CHILDREN')
# you can register the endpoints /api/person and /api/person/<uid> by calling
register_api_object(Person)
# if you want to preotect some attributes from being set this way, simply include them in the
BLOCKED_PROPERTIES global variable in this module
"""
BLOCKED_PROPERTIES: Final[List[str]] = ['id', 'uid', 'password']
class ApiError(Exception):
"""Defines an error that occurred during the handling of an API request"""
def __init__(self, msg, status=400, data=None):
super().__init__()
self.msg = msg
self.status = status
self.data = data
def __str__(self):
return json.dumps(dict(msg=self.msg, status=self.status))
def to_response(self) -> Response:
"""Converts the error into a response"""
resp: Response = jsonify(msg=self.msg, data=self.data)
resp.status_code = self.status
return resp
@staticmethod
def not_found(cls: Type[StructuredNode], uid=None):
return ApiError(f"Cannot find {cls.__name__}" + (f" with uid {uid}" if uid else "") + "!", status=404)
@staticmethod
def bad_request(msg: str, data=None):
return ApiError(msg, status=400, data=data)
def _connect_callback(attr, target):
"""returns a callback to connect attr with target, this is wrapped
because we need to get attr and target into a separate closure"""
return lambda: attr.connect(target)
def _get_target_class(rel: RelationshipDefinition) -> Type[StructuredNode]:
target_cls = rel._raw_class
return getattr(sys.modules[rel.module_name], target_cls) if isinstance(target_cls, str) else target_cls
def construct_object_from_request(cls: Type[StructuredNode], uid=None):
"""Construct an obect of class cls from request data"""
# properties that you do not want to initialize or change this way
if uid:
obj: Final = cls.nodes.get_or_none(uid=uid)
if not obj:
raise ApiError.not_found(cls, uid)
else:
obj: Final = cls()
relationship_attach_callbacks = list()
for name, value in cls.__dict__.items():
if name[0] == '_' or name in BLOCKED_PROPERTIES or not name[0].islower():
continue
cls_name = value.__class__.__name__
convert_with = None
if cls_name == 'StringProperty':
convert_with = str
elif cls_name == 'DateProperty':
convert_with = datetime.fromisoformat
elif cls_name == 'IntegerProperty':
convert_with = int
elif cls_name == 'FloatProperty':
convert_with = float
elif cls_name == 'RelationshipDefinition':
if name not in request.form:
continue
target_cls: Type[StructuredNode] = _get_target_class(value)
for val in request.form.getlist(name):
target = target_cls.nodes.first_or_none(uid=val)
if not target:
raise ApiError(f'Cannot find referenced object uid={val} of type {target_cls.__name__}!')
relationship_attach_callbacks.append(_connect_callback(getattr(obj, name), target))
if not convert_with:
# skip this property
continue
if name in request.form:
setattr(obj, name, convert_with(request.form.get(name)))
return obj, lambda: [c() for c in relationship_attach_callbacks]
def handle_object_api_request(cls: Type[StructuredNode]):
"""basic method for handling (GET|POST) /api/type calls"""
if request.method == 'GET':
return jsonify([obj.json(include_relations=False) for obj in cls.nodes.all()])
if request.method == 'POST':
obj, attach_relationships = construct_object_from_request(cls)
obj.save()
attach_relationships()
return jsonify(obj.json())
def handle_object_api_request_id(cls: Type[StructuredNode], uid: str):
"""basic method for handling (GET|PUT|DELETE) /api/type/uid calls"""
if request.method == 'GET':
obj = cls.nodes.get_or_none(uid=uid)
if not obj:
raise ApiError.not_found(cls, uid)
return jsonify(obj.json())
if request.method == 'PUT':
obj, attach_relationships = construct_object_from_request(cls, uid=uid)
obj.save()
attach_relationships()
return jsonify(obj.json())
if request.method == 'DELETE':
obj = cls.nodes.get_or_none(uid=uid)
if not obj:
raise ApiError.not_found(cls, uid)
def handle_object_api_request_for_relation(cls: Type[StructuredNode], uid, relation, reluid):
obj = cls.nodes.get_or_none(uid=uid)
if not obj:
raise ApiError.not_found(cls, uid)
rel = getattr(cls, relation, None)
if not (rel and isinstance(rel, RelationshipDefinition)):
raise ApiError.bad_request(f'Object of type {cls.__name__} has no relation {relation}!')
target_cls = _get_target_class(rel)
target_obj = target_cls.nodes.get_or_none(uid=reluid)
if not target_obj:
raise ApiError.not_found(target_cls, reluid)
if request.method == 'DELETE':
getattr(obj, relation).disconnect(target_obj)
elif request.method == 'POST':
if isinstance(rel.manager, (ZeroOrOne, One)):
getattr(obj, relation).disconnect_all()
getattr(obj, relation).connect(target_obj)
return jsonify(obj)
def register_api_object(app: Flask, cls: Type[StructuredNode], name: Optional[str] = None):
"""Register api paths for the object type cls
routes registered:
GET /api/<name>
POST /api/<name>
GET /api/<name>/<uid>
PUT /api/<name>/<uid>
DELETE /api/<name>/<uid>
POST /api/<name>/<uid>/<relation>/<reluid> connect relationships
DELETE /api/<name>/<uid>/<relation>/<reluid> disconnect relationships
where <name> defaults to the lowercase class name of cls
internally it registers the three view functions api.<name>, api.<name>.with-id and api.<name>.rel
"""
name = name or cls.__name__.lower()
print(app.view_functions)
def func_handle():
return handle_object_api_request(cls)
def func_handle_id(uid):
return handle_object_api_request_id(cls, uid)
def func_handle_rel(uid, rel, reluid):
return handle_object_api_request_for_relation(cls, uid, rel, reluid)
app.view_functions[f'api.{name}'] = func_handle
app.view_functions[f'api.{name}.wth-id'] = func_handle_id
app.view_functions[f'api.{name}.rel'] = func_handle_rel
app.add_url_rule(f'/api/{name}',
f'api.{name}', func_handle,
methods=['GET', 'POST'])
app.add_url_rule(f'/api/{name}/<uid>',
f'api.{name}.with-id', func_handle_id,
methods=['GET', 'PUT', 'DELETE'])
app.add_url_rule(f'/api/{name}/<uid>/<rel>/<reluid>',
f'api.{name}.rel', func_handle_rel,
methods=['POST', 'DELETE'])

@ -1,120 +1,18 @@
import sys
from .models import *
from flask import Flask, request, jsonify
from typing import Dict, Type, List, Final
from datetime import datetime
class ApiError(BaseException):
def __init__(self, msg, status=400):
super().__init__()
self.msg = msg
self.status = status
def connect_callback(attr, target):
"""returns a callback to connect attr with target"""
return lambda: attr.connect(target)
def construct_object_from_request(cls, uid=None):
if uid:
obj: Final = cls.nodes.get_or_none(uid=uid)
if not obj:
raise ApiError(f"No {cls.__name__} found with uid {request.form.get('uid')}!", status=404)
else:
obj: Final = cls()
relationship_attach_callbacks = list()
for name, value in cls.__dict__.items():
if name[0] == '_' or not name[0].islower():
continue
cls_name = value.__class__.__name__
convert_with = None
if cls_name == 'StringProperty':
convert_with = str
elif cls_name == 'DateProperty':
convert_with = datetime.fromisoformat
elif cls_name == 'IntegerProperty':
convert_with = int
elif cls_name == 'FloatProperty':
convert_with = float
elif cls_name == 'RelationshipDefinition':
if name not in request.form:
continue
target_cls = value._raw_class
# convert class string to class by loading it from module object
if isinstance(target_cls, str):
target_cls = getattr(cls.__module__, target_cls)
for val in request.form.getlist(name):
target = target_cls.nodes.first_or_none(uid=val)
if not target:
raise ApiError(f'Cannot find referenced object uid={val} of type {target_cls.__name__}!')
relationship_attach_callbacks.append(connect_callback(getattr(obj, name), target))
if not convert_with:
# skip this property
continue
if name in request.form:
setattr(obj, name, convert_with(request.form.get(name)))
return obj, lambda: [c() for c in relationship_attach_callbacks]
def func_api_obj(cls: Type):
"""basic method for handling /api/type calls"""
if request.method == 'GET':
return jsonify([p.json(include_relations=False) for p in cls.nodes.all()])
if request.method == 'POST':
p, attach_relationships = construct_object_from_request(cls)
p.save()
attach_relationships()
return jsonify(p.json())
def func_api_obj_id(cls: Type, uid: str):
if request.method == 'GET':
person = Person.nodes.get_or_none(uid=uid)
if not person:
return "Not found", 404
return jsonify(person.json())
if request.method == 'PUT':
p, attach_relationships = construct_object_from_request(cls, uid=uid)
p.save()
attach_relationships()
return jsonify(p.json())
from server.abstract_api import ApiError, register_api_object
from flask import Flask
def attach_to_flask(app: Flask):
@app.route('/api/person', methods=['POST', 'GET'])
def func_api_person():
return func_api_obj(Person)
@app.route('/api/person/<uid>', methods=['PUT', 'GET'])
def func_api_person_id(uid):
return func_api_obj_id(Person, uid)
@app.route('/api/event', methods=['POST', 'GET'])
def func_api_event():
return func_api_obj(Event)
@app.route('/api/event/<uid>', methods=['PUT', 'GET'])
def func_api_event_id(uid):
return func_api_obj_id(Event, uid)
@app.route('/api/place', methods=['POST', 'GET'])
def func_api_place():
return func_api_obj(Place)
@app.route('/api/place/<uid>', methods=['PUT', 'GET'])
def func_api_place_id(uid):
return func_api_obj_id(Place, uid)
@app.errorhandler(ApiError)
def handle_api_error(err: ApiError):
return err.to_response()
# register these as api accessible
register_api_object(app, Person)
register_api_object(app, Event)
register_api_object(app, Place)
register_api_object(app, Image)
register_api_object(app, VisitRel)

@ -66,6 +66,3 @@ class Image(StructuredNode, Jsonifiable):
place = RelationshipTo(Place, 'TAKEN_AT')
date = DateProperty()

@ -0,0 +1,194 @@
class StructuredNode {
constructor(json) {
if (this.constructor === StructuredNode) {
throw new TypeError('Cannot instantiate object of abstract type StructuredNode!')
}
this.json = json
this.changes = {}
this.definition = StructuredNode.classes[this.constructor.name]
this.hooks = []
this.deleted = false
}
check(name, type, new_value) {
}
_link_property(name, elm) {
const relation = this.definition.rels[name]
if (!relation) {
throw new ApiError(`Cannot find realation ${name} of object ${this.constructor.name}!`, 404, {obj: this, elm: elm})
}
const type = StructuredNode.classes[relation.target]
if (typeof elm == "string") {
elm = type.by_id(elm)
}
if (elm instanceof type) {
if (!elm.exists() || !this.exists()) {
throw new ApiError("Cannot connect object before it exists on the backend!")
}
// make results available immediately
if (relation.cardinality.endsWith('OrMore')) {
if (!this.json[name].contains(elm)) {
this.json[name].push(elm)
}
} else {
this.json[name] = elm
}
return Api.post(`/${this.constructor.name}/${this.json.uid}/${name}/${elm.json.uid}`).then(json => {
this.json = json
this.update('link')
return this
})
}
}
_unlink_property(name, type, elm) {
this.json[name] = this.json[name].filter(elm => elm.json.uid === (elm.uid || elm))
}
static by_id(uid) {
return Api.get(this.constructor.name + '/' + uid).then(x => x.map(elm => new this.constructor(elm)))
}
static find(attributes, settings) {
}
save() {
if (this.deleted) {
throw new ApiError("Cannot save deleted object!", {obj: this})
}
if (this.json.uid) {
return Api.put(`/${this.constructor.name}/${this.json.uid}`, this.changes).then(json => {
this.json = json
this.changes = {}
this.update('save')
return this
})
} else {
return Api.post('/' + this.constructor.name, this.json).then(json => {
this.json = json
this.changes = {}
this.update('created')
return this
})
}
}
delete() {
if (!this.exists()) {
throw new ApiError("Cannot delete object that does not exist yet!", {obj: this})
}
this.deleted = true
Api.delete(`/${this.constructor.name}/${this.json.uid}`).then(r => {
this.update('deleted')
return this
})
}
exists() {
return !this.deleted && !!this.json.id
}
onupdate(callback) {
this.hooks.push(callback)
}
update(event) {
this.hooks.forEach(cb => {
try {
cb(this, event)
} catch (e) {
console.error(`Error while running update on ${this.constructor.name} update function ${cb}: ${e}`)
console.error(cb, event, e)
}
})
}
static registerClassDefinition(klass, props, rels) {
StructuredNode.classes[klass.constructor.name] = {klass, props, rels}
}
}
StructuredNode.classes = {}
class Api {
static set_auth(token) {
Api.token = token;
}
static get(url) {
fetch('/api' + url, {headers: Api.headers()}).then(r => {
if (r.ok) {
return r.json
} else {
// evaluate response and raise error
return ApiError.fromResponse(r)
}
})
}
static post(url, body="") {
fetch('/api' + url, {headers: Api.headers(), body: JSON.stringify(body), method: 'POST'}).then(r => {
if (r.ok) {
return r.json
} else {
// evaluate response and raise error
return ApiError.fromResponse(r)
}
})
}
static put(url, body) {
fetch('/api' + url, {headers: Api.headers(), body: JSON.stringify(body), method: 'PUT'}).then(r => {
if (r.ok) {
return r.json
} else {
// evaluate response and raise error
return ApiError.fromResponse(r)
}
})
}
static delete(url) {
fetch('/api' + url, {headers: Api.headers(), method: 'DELETE'}).then(r => {
if (r.ok) {
return r
} else {
// evaluate response and raise error
return ApiError.fromResponse(r)
}
})
}
static headers() {
return {
'Content-Type': 'application/json'
}
}
}
class ApiError {
constructor(msg, code, data) {
this.msg = msg
this.code = code
this.data = data
}
static fromResponse(r) {
if (r.headers.get('Content-Type') === 'application/json') {
return r.json().then(json => {
throw new ApiError(json.msg, r.status, json.data)
})
} else {
return r.text().then(text => {
throw new ApiError(text, r.status)
})
}
}
}

@ -0,0 +1,312 @@
// This file was generated by running js_conversion.py
// It converts the neomodel declaration of this element into js classes.
class Person extends StructuredNode {
constructor(json) {
super(json)
}
get_name() {
return this.json.name
}
set_name(new_val) {
if (this.check('name', new_val)) {
this.json.name = new_val
this.changes.name = new_val
}
}
get_father() {
return this.json.father
}
link_father(ref) {
return this._link_property('father', ref)
}
unlink_father(ref) {
return this._unlink_property('father', ref)
}
get_mother() {
return this.json.mother
}
link_mother(ref) {
return this._link_property('mother', ref)
}
unlink_mother(ref) {
return this._unlink_property('mother', ref)
}
get_birthdate() {
return this.json.birthdate
}
set_birthdate(new_val) {
if (this.check('birthdate', new_val)) {
this.json.birthdate = new_val
this.changes.birthdate = new_val
}
}
get_deceased() {
return this.json.deceased
}
set_deceased(new_val) {
if (this.check('deceased', new_val)) {
this.json.deceased = new_val
this.changes.deceased = new_val
}
}
get_description() {
return this.json.description
}
set_description(new_val) {
if (this.check('description', new_val)) {
this.json.description = new_val
this.changes.description = new_val
}
}
get_picture() {
return this.json.picture
}
set_picture(new_val) {
if (this.check('picture', new_val)) {
this.json.picture = new_val
this.changes.picture = new_val
}
}
get_places() {
return this.json.places
}
link_places(ref) {
return this._link_property('places', ref)
}
unlink_places(ref) {
return this._unlink_property('places', ref)
}
}
StructuredNode.registerClassDefinition(Person, {"uid": {"field": "uid", "type": "UniqueIdProperty", "required": false}, "name": {"field": "name", "type": "StringProperty", "required": true}, "birthdate": {"field": "birthdate", "type": "DateProperty", "required": false}, "deceased": {"field": "deceased", "type": "DateProperty", "required": false}, "description": {"field": "description", "type": "StringProperty", "required": false}, "picture": {"field": "picture", "type": "StringProperty", "required": false}}, {"father": {"field": "father", "target": "Person", "cardinality": "ZeroOrOne"}, "mother": {"field": "mother", "target": "Person", "cardinality": "ZeroOrOne"}, "places": {"field": "places", "target": "Place", "cardinality": "ZeroOrMore"}})
class Event extends StructuredNode {
constructor(json) {
super(json)
}
get_name() {
return this.json.name
}
set_name(new_val) {
if (this.check('name', new_val)) {
this.json.name = new_val
this.changes.name = new_val
}
}
get_participants() {
return this.json.participants
}
link_participants(ref) {
return this._link_property('participants', ref)
}
unlink_participants(ref) {
return this._unlink_property('participants', ref)
}
get_places() {
return this.json.places
}
link_places(ref) {
return this._link_property('places', ref)
}
unlink_places(ref) {
return this._unlink_property('places', ref)
}
get_date() {
return this.json.date
}
set_date(new_val) {
if (this.check('date', new_val)) {
this.json.date = new_val
this.changes.date = new_val
}
}
get_end_date() {
return this.json.end_date
}
set_end_date(new_val) {
if (this.check('end_date', new_val)) {
this.json.end_date = new_val
this.changes.end_date = new_val
}
}
get_related_events() {
return this.json.related_events
}
link_related_events(ref) {
return this._link_property('related_events', ref)
}
unlink_related_events(ref) {
return this._unlink_property('related_events', ref)
}
}
StructuredNode.registerClassDefinition(Event, {"uid": {"field": "uid", "type": "UniqueIdProperty", "required": false}, "name": {"field": "name", "type": "StringProperty", "required": true}, "date": {"field": "date", "type": "DateProperty", "required": false}, "end_date": {"field": "end_date", "type": "DateProperty", "required": false}}, {"participants": {"field": "participants", "target": "Person", "cardinality": "ZeroOrMore"}, "places": {"field": "places", "target": "Place", "cardinality": "ZeroOrMore"}, "related_events": {"field": "related_events", "target": "Event", "cardinality": "ZeroOrMore"}})
class Place extends StructuredNode {
constructor(json) {
super(json)
}
get_name() {
return this.json.name
}
set_name(new_val) {
if (this.check('name', new_val)) {
this.json.name = new_val
this.changes.name = new_val
}
}
get_description() {
return this.json.description
}
set_description(new_val) {
if (this.check('description', new_val)) {
this.json.description = new_val
this.changes.description = new_val
}
}
get_visitors() {
return this.json.visitors
}
link_visitors(ref) {
return this._link_property('visitors', ref)
}
unlink_visitors(ref) {
return this._unlink_property('visitors', ref)
}
}
StructuredNode.registerClassDefinition(Place, {"uid": {"field": "uid", "type": "UniqueIdProperty", "required": false}, "name": {"field": "name", "type": "StringProperty", "required": true}, "description": {"field": "description", "type": "StringProperty", "required": false}}, {"visitors": {"field": "visitors", "target": "Person", "cardinality": "ZeroOrMore"}})
class Image extends StructuredNode {
constructor(json) {
super(json)
}
get_description() {
return this.json.description
}
set_description(new_val) {
if (this.check('description', new_val)) {
this.json.description = new_val
this.changes.description = new_val
}
}
get_pictured() {
return this.json.pictured
}
link_pictured(ref) {
return this._link_property('pictured', ref)
}
unlink_pictured(ref) {
return this._unlink_property('pictured', ref)
}
get_place() {
return this.json.place
}
link_place(ref) {
return this._link_property('place', ref)
}
unlink_place(ref) {
return this._unlink_property('place', ref)
}
get_date() {
return this.json.date
}
set_date(new_val) {
if (this.check('date', new_val)) {
this.json.date = new_val
this.changes.date = new_val
}
}
}
StructuredNode.registerClassDefinition(Image, {"uid": {"field": "uid", "type": "StringProperty", "required": true}, "description": {"field": "description", "type": "StringProperty", "required": false}, "date": {"field": "date", "type": "DateProperty", "required": false}}, {"pictured": {"field": "pictured", "target": "Person", "cardinality": "ZeroOrMore"}, "place": {"field": "place", "target": "Place", "cardinality": "ZeroOrMore"}})

@ -1,20 +0,0 @@
const $ = sel => document.querySelector(sel)
const $$ = sel => Array.from(document.querySelectorAll(sel))
const create_elm = str => {
const div = document.createElement('div')
div.innerHTML = str
return Array.from(div.childNodes)
}
const flash_msg = (message) => {
const elm = create_elm('<div class="flash-msg flex"><span class="flex-grow">' + message + '</span> <span class="close-btn">×</span></div>')[0]
$('main .container').prepend(elm)
elm.querySelector('.close-btn').addEventListener('click', e => elm.remove())
}
onload(() => {
// remove flash messages when close button is clicked
$$('.flash-msg .close-btn').forEach(elm => elm.addEventListener('click', e => {
elm.parentElement.remove()
}))
})

@ -1,32 +0,0 @@
onload(() => {
const socket = io.connect(document.location.host);
socket.on('flash_msg', flash_msg);
socket.on('player_state', (data) => {
if (!data.secret) {
// our secret seems to be to old
localStorage.removeItem('secret');
return register();
}
//flash_msg("Logged in!");
localStorage.setItem('secret', data.secret);
localStorage.setItem('name', data.username);
document.cookie = `secret=${data.secret}`;
window.username = data.username;
$('.username').innerText = 'playing as ' + window.username;
});
function register() {
const name = localStorage.getItem('name') || prompt("Whats your username?");
socket.emit('signup', name)
}
if (!localStorage.getItem('secret')) {
register()
} else {
socket.emit('resume', localStorage.getItem('secret'))
}
window.socket = socket;
});

@ -68,16 +68,3 @@ a:hover {
text-decoration: underline;
}
.flash-msg {
border: 1px solid;
padding: 8px 16px;
border-radius: 3px;
margin: 16px;
margin-top: -16px;
margin-bottom: 32px;
background: rgb(48, 50, 51)
}
.flash-msg .close-btn {
cursor: pointer;
}
Loading…
Cancel
Save