From 6da881edfcba6fd09c66b7e3c3c15fbdeae824fd Mon Sep 17 00:00:00 2001
From: Adrian Schlatter <10478149+adrianschlatter@users.noreply.github.com>
Date: Sun, 17 Dec 2023 21:48:39 +0100
Subject: [PATCH] drop docker, make python package
* anything docker-related belongs in docker.webref, not in ppf.webref
=> dropped
* organize code as a python package
---
.gitignore | 9 --
Dockerfile | 19 ----
LICENSE | 2 +-
MANIFEST.in | 6 ++
config/httpd.conf | 50 ----------
config/webref.wsgi | 4 -
docker-compose_templ.yml | 22 -----
docs/README.md | 61 +++++++-----
docs/README_pypi.md | 99 +++++++++++++++++++
requirements.txt | 21 ----
setup.cfg | 86 ++++++++++++++++
setup.py | 6 ++
webref.py => src/ppf/webref/__init__.py | 38 +++++--
{static => src/ppf/webref/static}/script.js | 0
{static => src/ppf/webref/static}/style.css | 0
.../ppf/webref/templates}/entry_table.tmpl | 0
.../ppf/webref/templates}/index.php | 4 +-
.../ppf/webref/templates}/login.php | 0
18 files changed, 264 insertions(+), 163 deletions(-)
delete mode 100644 Dockerfile
create mode 100644 MANIFEST.in
delete mode 100644 config/httpd.conf
delete mode 100644 config/webref.wsgi
delete mode 100644 docker-compose_templ.yml
create mode 100644 docs/README_pypi.md
delete mode 100644 requirements.txt
create mode 100644 setup.cfg
create mode 100644 setup.py
rename webref.py => src/ppf/webref/__init__.py (79%)
rename {static => src/ppf/webref/static}/script.js (100%)
rename {static => src/ppf/webref/static}/style.css (100%)
rename {templates => src/ppf/webref/templates}/entry_table.tmpl (100%)
rename {templates => src/ppf/webref/templates}/index.php (89%)
rename {templates => src/ppf/webref/templates}/login.php (100%)
diff --git a/.gitignore b/.gitignore
index 48c3721..9c1ba27 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,19 +1,10 @@
# to avoid leaking credentials to github:
-sqldatabasename
-sqlpassword
-sqlserver
-sqlusername
-secrets/
-docker-compose.yml
hash_password.py
# to avoid leaking references to github:
references
references/
-# because this relates to my personal config:
-config/www.webref.conf
-
# general things to ignore:
build/
dist/
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index 5b32b4d..0000000
--- a/Dockerfile
+++ /dev/null
@@ -1,19 +0,0 @@
-FROM httpd:2.4
-
-COPY static/ /webapp/static
-COPY templates/ /webapp/templates
-COPY webref.py /webapp
-COPY config/webref.wsgi /webapp
-COPY config/httpd.conf /usr/local/apache2/conf
-COPY requirements.txt /webapp
-RUN mkdir /var/run/apache2 # needed to store files for WSGI
-RUN mkdir /var/www # home of www-data user
-WORKDIR /webapp
-
-RUN apt update
-RUN apt install -y python3
-RUN apt install -y python3-pip
-RUN apt install -y libapache2-mod-wsgi-py3
-RUN pip3 install -r requirements.txt --break-system-packages
-
-CMD ["/usr/local/apache2/bin/httpd", "-D", "FOREGROUND"]
diff --git a/LICENSE b/LICENSE
index 6e966b8..30bcce9 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2021 Adrian Schlatter
+Copyright (c) 2021-2023 Adrian Schlatter
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..7e3eb77
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,6 @@
+exclude .gitignore
+exclude MANIFEST.in
+exclude tox.ini
+
+recursive-exclude .github *
+recursive-exclude tests *
diff --git a/config/httpd.conf b/config/httpd.conf
deleted file mode 100644
index e095a03..0000000
--- a/config/httpd.conf
+++ /dev/null
@@ -1,50 +0,0 @@
-ServerName localhost
-ServerAdmin root@localhost
-ServerRoot /usr/local/apache2
-User www-data
-Group www-data
-PidFile logs/httpd.pid
-
-ServerTokens Prod
-UseCanonicalName On
-TraceEnable Off
-
-Timeout 10
-MaxRequestWorkers 100
-
-Listen 0.0.0.0:80
-
-LoadModule mpm_event_module modules/mod_mpm_event.so
-LoadModule unixd_module modules/mod_unixd.so
-
-LoadModule log_config_module modules/mod_log_config.so
-
-LoadModule authn_core_module modules/mod_authn_core.so
-LoadModule authz_core_module modules/mod_authz_core.so
-
-# apt installs mod_wsgi.so in a different folder:
-LoadModule wsgi_module /usr/lib/apache2/modules/mod_wsgi.so
-
-ErrorLogFormat "[%{cu}t] [%-m:%-l] %-a %-L %M"
-LogFormat "%h %l %u [%{%Y-%m-%d %H:%M:%S}t.%{usec_frac}t] \"%r\" %>s %b \
-\"%{Referer}i\" \"%{User-Agent}i\"" combined
-
-LogLevel debug
-ErrorLog logs/error.log
-CustomLog logs/access.log combined
-
-
- Require all denied
- Options SymLinksIfOwnerMatch
-
-
-
- WSGIDaemonProcess webref user=www-data group=www-data threads=5
- WSGIScriptAlias / /webapp/webref.wsgi
-
-
- WSGIProcessGroup webref
- WSGIApplicationGroup %{GLOBAL}
- Require all granted
-
-
diff --git a/config/webref.wsgi b/config/webref.wsgi
deleted file mode 100644
index 32ffd43..0000000
--- a/config/webref.wsgi
+++ /dev/null
@@ -1,4 +0,0 @@
-import sys
-sys.path.insert(0, '/webapp')
-
-from webref import app as application
diff --git a/docker-compose_templ.yml b/docker-compose_templ.yml
deleted file mode 100644
index 664a695..0000000
--- a/docker-compose_templ.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-services:
- webref:
- image: webref
- build: .
- ports:
- - "7000:80"
- volumes:
- - :/webapp/references
- secrets:
- - sqlusername
- - sqlpassword
- - sqlserver
- - sqldatabasename
-secrets:
- sqlusername:
- file: ./secrets/sqlusername
- sqlpassword:
- file: ./secrets/sqlpassword
- sqlserver:
- file: ./secrets/sqlserver
- sqldatabasename:
- file: ./secrets/sqldatabasename
diff --git a/docs/README.md b/docs/README.md
index af2755c..1d70bf9 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,16 +1,17 @@
-# WebRef
+# ppf.webref
-WebRef is a web interface to a [JabRef SQL database](https://docs.jabref.org/collaborative-work/sqldatabase).
+ppf.webref is a web app providing an interface to a [JabRef SQL
+database](https://docs.jabref.org/collaborative-work/sqldatabase).
It allows you to access your references from anywhere in the world and from
any device with a web browser. You do not need to install Java, you
do not need to install an app. Any non-archaic phone, tablet, PC, Mac, or
Raspberry Pi will do.
-Create a JabRef database (using your normal JabRef) and configure WebRef
+Create a JabRef database (using your normal JabRef) and configure ppf.webref
to point to this database. Voila: Your references just became accessible
worldwide.
-Note: WebRef provides *read-only* access to your library. To add, edit, or
+Note: ppf.webref provides *read-only* access to your library. To add, edit, or
delete entries from your library, you still need a standard JabRef installation
somewhere.
@@ -21,33 +22,39 @@ somewhere.
## Installation
-You need:
+Prerequisite: You need JabRef to create, edit, and extend your library.
-* JabRef: To create, edit, and extend your library
-* Docker: To create and run docker images
+Install ppf.webref:
+```shell
+pip install ppf.webref
+```
+
+Then, tell ppf.webref about your database by adding a section as follows to
+`~/.config/ppf.webref/ppf.webref.conf` (create it if it does not exist):
-Steps:
+```
+[database]
+server = :
+databasename =
+username =
+password =
+```
+
+Finally, run
+
+```shell
+flask --app ppf.webref run
+```
-* Clone this repo
-* Create a suitable docker-compose.yml (use
- [docker-compose_templ.yml](../docker-compose_templ.yml) as a starting point)
-* Create the following text files (assuming you did not change the paths
- from the template docker-compose.yml):
- - ./secrets/sqlusername: Username used to access your JabRef database
- - ./secrets/sqlpassword: Password for that username
- - ./secrets/sqlserver: The sql server holding your JabRef database
- - ./secrets/sqldatabasename: The name of your JabRef database
-* Run ```docker-compose up```
-* Point your webbrowser to localhost:7000 (or where you configured your
- WebRef to be)
+and point your webbrowser to http://localhost:5000.
-This will start WebRef on your local machine which is nice for testing.
-To get the most out of WebRef, you will probably want to
-run this docker image on a web server.
+This will start ppf.webref on your local machine which is nice for testing.
+To get the most out of ppf.webref, you will probably want to run ppf.webref on
+a web server.
As we have not created any users yet, we can't login. To create
-users, open your JabRef database (the one named in ./secrets/sqldatabasename)
+users, open your JabRef database (the one named in the config file above)
and run this sql-code (make sure you don't have a table with this name
already):
@@ -61,9 +68,9 @@ create table user (
)
```
-Now we have a user table but no users in it, yet. Let's find a password and hash
-it with the following python code (of course, we replace the dummy password
-with your own password beforehand):
+Now we have a user table but no users in it, yet. Let's find a password and
+hash it with the following python code (of course, we replace the dummy
+password with your own password beforehand):
```
import bcrypt
diff --git a/docs/README_pypi.md b/docs/README_pypi.md
new file mode 100644
index 0000000..be8e2cc
--- /dev/null
+++ b/docs/README_pypi.md
@@ -0,0 +1,99 @@
+# ppf.webref
+
+ppf.webref is a web app providing an interface to a [JabRef SQL
+database](https://docs.jabref.org/collaborative-work/sqldatabase).
+It allows you to access your references from anywhere in the world and from
+any device with a web browser. You do not need to install Java, you
+do not need to install an app. Any non-archaic phone, tablet, PC, Mac, or
+Raspberry Pi will do.
+
+Create a JabRef database (using your normal JabRef) and configure ppf.webref
+to point to this database. Voila: Your references just became accessible
+worldwide.
+
+Note: ppf.webref provides *read-only* access to your library. To add, edit, or
+delete entries from your library, you still need a standard JabRef installation
+somewhere.
+
+
+## Installation
+
+Prerequisite: You need JabRef to create, edit, and extend your library.
+
+Install ppf.webref:
+
+```shell
+pip install ppf.webref
+```
+
+Then, tell ppf.webref about your database by adding a section as follows to
+`~/.config/ppf.webref/ppf.webref.conf` (create it if it does not exist):
+
+```
+[database]
+server = :
+databasename =
+username =
+password =
+```
+
+Finally, run
+
+```shell
+flask --app ppf.webref run
+```
+
+and point your webbrowser to http://localhost:5000.
+
+This will start ppf.webref on your local machine which is nice for testing.
+To get the most out of ppf.webref, you will probably want to run ppf.webref on
+a web server.
+
+As we have not created any users yet, we can't login. To create
+users, open your JabRef database (the one named in the config file above)
+and run this sql-code (make sure you don't have a table with this name
+already):
+
+```
+create table user (
+ id INT auto_increment,
+ username varchar(20) character set utf8 not null,
+ password char(80) character set ascii not null,
+ primary key (id),
+ unique(username)
+)
+```
+
+Now we have a user table but no users in it, yet. Let's find a password and
+hash it with the following python code (of course, we replace the dummy
+password with your own password beforehand):
+
+```
+import bcrypt
+
+password = 'This is my password'
+
+bytes = password.encode('utf-8')
+salt = bcrypt.gensalt()
+print(bcrypt.hashpw(bytes, salt))
+```
+
+The output looks something like this:
+
+```
+b'$2b$12$1royHRBq6o/mbDdO7LjR8eaThWYErI6HLLdn7MBfajtpRLlwWSJ8m'
+```
+
+Now add your user to the user table in you JabRef database using this sql-code
+(again, replace "webref" with your username and the password hash with the
+hash you generated above):
+
+```
+insert into user (username, password)
+values (
+ "webref",
+ "$2b$12$1royHRBq6o/mbDdO7LjR8eaThWYErI6HLLdn7MBfajtpRLlwWSJ8m"
+);
+```
+
+Now we are ready to go.
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index a51cd93..0000000
--- a/requirements.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-bcrypt==4.0.1
-blinker==1.7.0
-click==8.1.3
-Flask==2.3.3
-Flask-Bcrypt==1.0.1
-Flask-Login==0.6.2
-Flask-SQLAlchemy==3.0.3
-flask-talisman==1.0.0
-Flask-WTF==1.1.1
-greenlet==2.0.2
-importlib-metadata==6.1.0
-itsdangerous==2.1.2
-Jinja2==3.1.2
-MarkupSafe==2.1.2
-ppf-jabref==0.1.0
-PyMySQL==1.0.3
-SQLAlchemy==2.0.7
-typing-extensions==4.5.0
-Werkzeug==2.3.8
-WTForms==3.0.1
-zipp==3.15.0
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..53de360
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,86 @@
+[metadata]
+name = ppf.webref
+description = Flask app providing a web-interface to a JabRef database
+long_description = file: docs/README_pypi.md
+long_description_content_type = text/markdown
+url = https://github.com/adrianschlatter/ppf.webref/tree/master
+project_urls =
+ Bug Reports = https://github.com/adrianschlatter/ppf.webref/issues
+ Source = https://github.com/adrianschlatter/ppf.webref
+author = Adrian Schlatter
+
+license = MIT
+license_files = LICENSE
+
+# See https://pypi.python.org/pypi?%3Aaction=list_classifiers
+classifiers =
+ Development Status :: 3 - Alpha
+ Intended Audience :: Developers
+ Intended Audience :: Information Technology
+ License :: OSI Approved :: MIT License
+ Programming Language :: Python :: 3
+ Operating System :: OS Independent
+keywords = jabref, references, web, server, app
+
+[options]
+package_dir =
+ = src
+packages = find_namespace:
+python_requires = >=3.5, <4
+install_requires =
+ flask
+ flask-sqlalchemy
+ flask_talisman
+ flask_login
+ flask_wtf
+ flask_bcrypt
+ pymysql
+ ppf.jabref
+ plumbum
+ importlib_metadata;python_version<'3.8'
+
+[options.packages.find]
+where = src
+
+[options.extras_require]
+# List additional groups of dependencies here. You can install these using:
+# pip install -e .[dev,test]
+test =
+ check-manifest
+ setuptools>=40.5.0
+ flake8
+ pytest
+ coverage
+dev =
+ build
+ tox
+ twine
+
+[tool:pytest]
+testpaths =
+ tests
+
+[flake8]
+per-file-ignores =
+ # imported but unused, import *, undefined name:
+ __init__.py: F401, F403, F821
+filename =
+ */src/*.py
+ */docs/*.py
+ */tests/*.py
+ setup.py
+
+[check-manifest]
+ignore =
+ tox.ini
+ tests
+ tests/**
+ docs/**
+
+[coverage:run]
+command_line = -m pytest
+branch = True
+
+[coverage:report]
+omit = tests/*
+
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..7c7f94d
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,6 @@
+# -*- coding: utf-8 -*-
+# required for backwards compatibility
+
+from setuptools import setup
+
+setup()
diff --git a/webref.py b/src/ppf/webref/__init__.py
similarity index 79%
rename from webref.py
rename to src/ppf/webref/__init__.py
index 632f51e..b285b80 100644
--- a/webref.py
+++ b/src/ppf/webref/__init__.py
@@ -21,6 +21,8 @@
from flask_talisman import Talisman
from ppf.jabref import Entry, Field, split_by_unescaped_sep
from pathlib import Path
+from plumbum import cli
+from urllib.parse import quote_plus
# Credential management
#
@@ -31,16 +33,36 @@
# Inside the container, the named secret is available in the text file
# /run/secrets/.
-if __name__ == '__main__':
- secrets_path = Path('./secrets')
-else:
- secrets_path = Path('/run/secrets')
-sqlusername = open(secrets_path / 'sqlusername').readline().strip()
-sqlpassword = open(secrets_path / 'sqlpassword').readline().strip()
-sqlserver = open(secrets_path / 'sqlserver').readline().strip()
-sqldatabasename = open(secrets_path / 'sqldatabasename').readline().strip()
+def get_secrets():
+ # read config:
+ config_path = Path('~/.config/ppf.webref/ppf.webref.conf').expanduser()
+ if __name__ == '__main__':
+ secrets_path = Path('./secrets')
+ else:
+ secrets_path = Path('/run/secrets')
+
+ if config_path.exists():
+ with cli.Config(config_path) as config:
+ sqlusername = config.get('database.username', None)
+ sqlpassword = config.get('database.password', None)
+ sqlserver = config.get('database.server', None)
+ sqldatabasename = config.get('database.databasename', None)
+ elif secrets_path.exists():
+ sqlusername = open(secrets_path / 'sqlusername').readline().strip()
+ sqlpassword = open(secrets_path / 'sqlpassword').readline().strip()
+ sqlserver = open(secrets_path / 'sqlserver').readline().strip()
+ sqldatabasename = (open(secrets_path / 'sqldatabasename')
+ .readline().strip())
+ else:
+ raise RuntimeError('No config file found')
+
+ sqlpassword = quote_plus(sqlpassword)
+
+ return sqlusername, sqlpassword, sqlserver, sqldatabasename
+
+sqlusername, sqlpassword, sqlserver, sqldatabasename = get_secrets()
app = Flask(__name__,
static_url_path='',
diff --git a/static/script.js b/src/ppf/webref/static/script.js
similarity index 100%
rename from static/script.js
rename to src/ppf/webref/static/script.js
diff --git a/static/style.css b/src/ppf/webref/static/style.css
similarity index 100%
rename from static/style.css
rename to src/ppf/webref/static/style.css
diff --git a/templates/entry_table.tmpl b/src/ppf/webref/templates/entry_table.tmpl
similarity index 100%
rename from templates/entry_table.tmpl
rename to src/ppf/webref/templates/entry_table.tmpl
diff --git a/templates/index.php b/src/ppf/webref/templates/index.php
similarity index 89%
rename from templates/index.php
rename to src/ppf/webref/templates/index.php
index 15d6aba..dc1bf96 100644
--- a/templates/index.php
+++ b/src/ppf/webref/templates/index.php
@@ -3,7 +3,7 @@
- WebRef: A Web Interface to JabRef
+ ppf.webref: A Web Interface to JabRef