web store sessions in the database

This commit is contained in:
dave 2019-07-06 13:49:16 -07:00
parent 3aa2264be4
commit 2de4a5d5ae
5 changed files with 86 additions and 21 deletions

View File

@ -1,4 +1,4 @@
FROM ubuntu:bionic
FROM ubuntu:bionic AS frontend
RUN apt-get update && \
apt-get install -y wget software-properties-common && \
@ -13,20 +13,21 @@ RUN cd /tmp/code && \
npm install && \
./node_modules/.bin/grunt
FROM ubuntu:disco
FROM ubuntu:disco AS app
ADD . /tmp/code/
COPY --from=0 /tmp/code/styles/dist/style.css /tmp/code/styles/dist/style.css
RUN apt-get update && \
apt-get install -y python3-pip
COPY --from=frontend /tmp/code/styles/dist/style.css /tmp/code/styles/dist/style.css
RUN pip3 install -U pip && \
cd /tmp/code && \
pip3 install -r requirements.txt && \
python3 setup.py install && \
useradd --uid 1000 app
useradd --uid 1000 app && \
rm -rf /tmp/code
VOLUME /srv/library
VOLUME /srv/cache

View File

@ -115,31 +115,19 @@ This would ingest all the files listed in `shas.txt` that aren't already in the
Roadmap
-------
- Stateless aka docker support
- ~~Photo storage~~ done
- ~~Abstract the storage api~~ done
- ~~Standardize on API ingest~~ done
- ~~Display and retrieval of images from the abstracted image store~~ done
- ~~Thumbnail gen~~ done
- ~~Database support~~
- ~~Get the web UI code (daemon.py) using the same db access method as the api~~
- ~~Support any connection URI sqlalchemy is happy with~~
- ~~Tune certain databases if their uri is detected (sqlite and threads lol)~~
- ~~Cache~~ done
- ~~Using the local fs seems fine?~~ done
- Migration path
- open database
- copy files table to memory
- recreate files table
- insert into the new table, with replaced paths, generating a list of files moves at the same time
- migrate files to the new storage according to the list
- Storage option for cherrypy sessions - Redis?
- Flesh out CLI:
- Config that is saved somewhere
- Support additional fields on upload like title description tags etc
- delete features
- tag features
- modify features (tags & images)
- Longer term ideas:
- "fast ingest" method that touches the db/storage directly. This would scale better than the API ingest.
- Dynamic svg placeholder for images we can't open
- Proactive thumbnail generation

View File

@ -6,6 +6,7 @@ from urllib.parse import urlparse
from datetime import datetime, timedelta
from photoapp.thumb import ThumbGenerator
from photoapp.types import Photo, PhotoSet, Tag, TagItem, PhotoStatus, User
from photoapp.dbsession import DatabaseSession
from photoapp.common import pwhash
from photoapp.api import PhotosApi, LibraryManager
from photoapp.dbutils import SAEnginePlugin, SATool, db, get_db_engine
@ -473,7 +474,8 @@ def main():
# Setup and mount API
api = PhotosApi(library_manager)
cherrypy.tree.mount(api, '/api', {'/': {'tools.trailing_slash.on': False,
cherrypy.tree.mount(api, '/api', {'/': {'tools.sessions.on': False,
'tools.trailing_slash.on': False,
'tools.auth_basic.on': True,
'tools.auth_basic.realm': 'photolib',
'tools.auth_basic.checkpassword': validate_password,
@ -481,6 +483,7 @@ def main():
# General config options
cherrypy.config.update({
'tools.sessions.storage_class': DatabaseSession,
'tools.sessions.on': True,
'tools.sessions.locking': 'explicit',
'tools.sessions.timeout': 525600,

73
photoapp/dbsession.py Normal file
View File

@ -0,0 +1,73 @@
import cherrypy
from photoapp.types import Base
from sqlalchemy import Column, String, DateTime, Boolean, BLOB
try:
import cPickle as pickle
except ImportError:
import pickle
class Session(Base):
__tablename__ = 'sessions'
session_id = Column(String(128), nullable=False, primary_key=True)
# user_id = Column(Integer, ForeignKey('users.user_id'))
expiration = Column(DateTime, nullable=False)
data = Column(BLOB)
is_valid = Column(Boolean, default=True, nullable=False)
class DatabaseSession(cherrypy.lib.sessions.Session):
"""
Sqlalchemy-backed backed for session storage. Note that we don't implement any of the locking methods because the
underlying database connection is being uses transaction anyway, which would prevent concurrent updates.
"""
@classmethod
def setup(cls, **kwargs):
for k, v in kwargs.items():
setattr(cls, k, v)
cls.cached = None
def _exists(self):
if not self.cached:
self._load()
return bool(self.cached)
def _load(self):
if not self.cached:
self.cached = cherrypy.request.db.query(Session).filter(Session.session_id == self.id). \
with_for_update().first()
if self.cached:
return pickle.loads(self.cached.data)
return None
def _save(self, expiration_time):
data = pickle.dumps(
(self._data, expiration_time),
pickle.HIGHEST_PROTOCOL)
if not self.cached:
self.cached = Session(session_id=self.id,
expiration=expiration_time)
cherrypy.request.db.add(self.cached)
self.cached.data = data
def _delete(self):
if not self.cached:
self._load()
if self.cached:
cherrypy.request.db.delete(self.cached)
def acquire_lock(self):
pass
def release_lock(self):
pass
def clean_up(self):
pass #TODO

View File

@ -67,7 +67,7 @@ class SATool(cherrypy.Tool):
def __init__(self):
cherrypy.Tool.__init__(self, 'before_request_body',
self.bind_session,
priority=100)
priority=49) # slightly earlier than Sessions tool, which is 50 or 60
self.session = sqlalchemy.orm.scoped_session(
sqlalchemy.orm.sessionmaker(autoflush=True, autocommit=False))
@ -88,4 +88,4 @@ class SATool(cherrypy.Tool):
self.session.rollback()
raise
finally:
self.session.remove()
self.session.remove()