checkscanner 0.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. checkscanner-0.1.2/LICENSE +21 -0
  2. checkscanner-0.1.2/MANIFEST.in +5 -0
  3. checkscanner-0.1.2/PKG-INFO +212 -0
  4. checkscanner-0.1.2/README.md +178 -0
  5. checkscanner-0.1.2/pyproject.toml +3 -0
  6. checkscanner-0.1.2/setup.cfg +54 -0
  7. checkscanner-0.1.2/setup.py +2 -0
  8. checkscanner-0.1.2/src/checkscanner/__init__.py +5 -0
  9. checkscanner-0.1.2/src/checkscanner/active_scanners.py +53 -0
  10. checkscanner-0.1.2/src/checkscanner/admin_console.py +583 -0
  11. checkscanner-0.1.2/src/checkscanner/app.py +228 -0
  12. checkscanner-0.1.2/src/checkscanner/auth.py +51 -0
  13. checkscanner-0.1.2/src/checkscanner/bottle/__init__.py +0 -0
  14. checkscanner-0.1.2/src/checkscanner/bottle/bottle.py +4403 -0
  15. checkscanner-0.1.2/src/checkscanner/bottle/bottle_cors.py +18 -0
  16. checkscanner-0.1.2/src/checkscanner/bottle/bottle_jwt.py +116 -0
  17. checkscanner-0.1.2/src/checkscanner/bottle/bottle_rest.py +76 -0
  18. checkscanner-0.1.2/src/checkscanner/bottle/bottle_sqlite.py +161 -0
  19. checkscanner-0.1.2/src/checkscanner/check_scanner_handlers/__init__.py +3 -0
  20. checkscanner-0.1.2/src/checkscanner/check_scanner_handlers/check_scanner_handler.py +126 -0
  21. checkscanner-0.1.2/src/checkscanner/check_scanner_handlers/magtek_scanner_handler.py +185 -0
  22. checkscanner-0.1.2/src/checkscanner/check_scanner_handlers/rdm_scanner_handler.py +191 -0
  23. checkscanner-0.1.2/src/checkscanner/cli.py +221 -0
  24. checkscanner-0.1.2/src/checkscanner/data/__init__.py +0 -0
  25. checkscanner-0.1.2/src/checkscanner/data/api.json +715 -0
  26. checkscanner-0.1.2/src/checkscanner/data/arial.ttf +0 -0
  27. checkscanner-0.1.2/src/checkscanner/data/server.db.sql +37 -0
  28. checkscanner-0.1.2/src/checkscanner/debugger.py +40 -0
  29. checkscanner-0.1.2/src/checkscanner/ftp_server.py +74 -0
  30. checkscanner-0.1.2/src/checkscanner/resources/__init__.py +7 -0
  31. checkscanner-0.1.2/src/checkscanner/resources/check.py +41 -0
  32. checkscanner-0.1.2/src/checkscanner/resources/config.py +47 -0
  33. checkscanner-0.1.2/src/checkscanner/resources/connections.py +158 -0
  34. checkscanner-0.1.2/src/checkscanner/resources/profiles.py +73 -0
  35. checkscanner-0.1.2/src/checkscanner/resources/restart.py +20 -0
  36. checkscanner-0.1.2/src/checkscanner/resources/token.py +20 -0
  37. checkscanner-0.1.2/src/checkscanner/resources/users.py +72 -0
  38. checkscanner-0.1.2/src/checkscanner/static/200.html +16 -0
  39. checkscanner-0.1.2/src/checkscanner/static/404.html +16 -0
  40. checkscanner-0.1.2/src/checkscanner/static/_nuxt/-WfchzD6.js +1 -0
  41. checkscanner-0.1.2/src/checkscanner/static/_nuxt/BKuAqpd3.js +1 -0
  42. checkscanner-0.1.2/src/checkscanner/static/_nuxt/Bf__CQGB.js +34 -0
  43. checkscanner-0.1.2/src/checkscanner/static/_nuxt/CPYLmeXC.js +1 -0
  44. checkscanner-0.1.2/src/checkscanner/static/_nuxt/CZQnppu7.js +1 -0
  45. checkscanner-0.1.2/src/checkscanner/static/_nuxt/CpQhjTOX.js +1 -0
  46. checkscanner-0.1.2/src/checkscanner/static/_nuxt/CvytG1-F.js +1 -0
  47. checkscanner-0.1.2/src/checkscanner/static/_nuxt/CzsOtQj8.js +1 -0
  48. checkscanner-0.1.2/src/checkscanner/static/_nuxt/DMCEdG9B.js +1 -0
  49. checkscanner-0.1.2/src/checkscanner/static/_nuxt/DMCkEJxe.js +1 -0
  50. checkscanner-0.1.2/src/checkscanner/static/_nuxt/DRSp4aqm.js +1 -0
  51. checkscanner-0.1.2/src/checkscanner/static/_nuxt/DhGZ3OhD.js +1 -0
  52. checkscanner-0.1.2/src/checkscanner/static/_nuxt/LsS5jRfi.js +1 -0
  53. checkscanner-0.1.2/src/checkscanner/static/_nuxt/RRYXIiA5.js +1 -0
  54. checkscanner-0.1.2/src/checkscanner/static/_nuxt/builds/latest.json +1 -0
  55. checkscanner-0.1.2/src/checkscanner/static/_nuxt/builds/meta/1228cefd-1a9f-4246-a783-69aa576c153c.json +1 -0
  56. checkscanner-0.1.2/src/checkscanner/static/_nuxt/default.CKljHALx.css +1 -0
  57. checkscanner-0.1.2/src/checkscanner/static/_nuxt/entry.B9nQ-h33.css +1 -0
  58. checkscanner-0.1.2/src/checkscanner/static/_nuxt/error-404.MksMKVWr.css +1 -0
  59. checkscanner-0.1.2/src/checkscanner/static/_nuxt/error-500.DOWD7OuR.css +1 -0
  60. checkscanner-0.1.2/src/checkscanner/static/_nuxt/lZ82j3id.js +1 -0
  61. checkscanner-0.1.2/src/checkscanner/static/_nuxt/login.CepLjysV.css +1 -0
  62. checkscanner-0.1.2/src/checkscanner/static/_nuxt/odLcdTJR.js +1 -0
  63. checkscanner-0.1.2/src/checkscanner/static/_nuxt/settings.CZt2o3Ma.css +1 -0
  64. checkscanner-0.1.2/src/checkscanner/static/_nuxt/zO-zkS4P.js +1 -0
  65. checkscanner-0.1.2/src/checkscanner/static/active_scanners/index.html +16 -0
  66. checkscanner-0.1.2/src/checkscanner/static/index.html +16 -0
  67. checkscanner-0.1.2/src/checkscanner/static/login/index.html +16 -0
  68. checkscanner-0.1.2/src/checkscanner/static/scanner_profiles/index.html +16 -0
  69. checkscanner-0.1.2/src/checkscanner/static/scanner_test/index.html +16 -0
  70. checkscanner-0.1.2/src/checkscanner/static/settings/index.html +16 -0
  71. checkscanner-0.1.2/src/checkscanner/static/token/index.html +16 -0
  72. checkscanner-0.1.2/src/checkscanner/static/users/index.html +16 -0
  73. checkscanner-0.1.2/src/checkscanner/swagger/favicon-16x16.png +0 -0
  74. checkscanner-0.1.2/src/checkscanner/swagger/favicon-32x32.png +0 -0
  75. checkscanner-0.1.2/src/checkscanner/swagger/index.html +61 -0
  76. checkscanner-0.1.2/src/checkscanner/swagger/oauth2-redirect.html +75 -0
  77. checkscanner-0.1.2/src/checkscanner/swagger/swagger-ui-bundle.js +3 -0
  78. checkscanner-0.1.2/src/checkscanner/swagger/swagger-ui-bundle.js.map +1 -0
  79. checkscanner-0.1.2/src/checkscanner/swagger/swagger-ui-es-bundle-core.js +3 -0
  80. checkscanner-0.1.2/src/checkscanner/swagger/swagger-ui-es-bundle-core.js.map +1 -0
  81. checkscanner-0.1.2/src/checkscanner/swagger/swagger-ui-es-bundle.js +3 -0
  82. checkscanner-0.1.2/src/checkscanner/swagger/swagger-ui-es-bundle.js.map +1 -0
  83. checkscanner-0.1.2/src/checkscanner/swagger/swagger-ui-standalone-preset.js +3 -0
  84. checkscanner-0.1.2/src/checkscanner/swagger/swagger-ui-standalone-preset.js.map +1 -0
  85. checkscanner-0.1.2/src/checkscanner/swagger/swagger-ui.css +4 -0
  86. checkscanner-0.1.2/src/checkscanner/swagger/swagger-ui.css.map +1 -0
  87. checkscanner-0.1.2/src/checkscanner/swagger/swagger-ui.js +3 -0
  88. checkscanner-0.1.2/src/checkscanner/swagger/swagger-ui.js.map +1 -0
  89. checkscanner-0.1.2/src/checkscanner.egg-info/PKG-INFO +212 -0
  90. checkscanner-0.1.2/src/checkscanner.egg-info/SOURCES.txt +93 -0
  91. checkscanner-0.1.2/src/checkscanner.egg-info/dependency_links.txt +1 -0
  92. checkscanner-0.1.2/src/checkscanner.egg-info/entry_points.txt +3 -0
  93. checkscanner-0.1.2/src/checkscanner.egg-info/requires.txt +25 -0
  94. checkscanner-0.1.2/src/checkscanner.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DiscipleMakers
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,5 @@
1
+ include README.md LICENSE
2
+ graft src/checkscanner/static
3
+ graft src/checkscanner/swagger
4
+ recursive-include src/checkscanner/data *.ttf *.json *.sql
5
+ global-exclude *.py[cod] __pycache__ .gitkeep
@@ -0,0 +1,212 @@
1
+ Metadata-Version: 2.4
2
+ Name: checkscanner
3
+ Version: 0.1.2
4
+ Summary: Check scanner API server and admin console
5
+ Home-page: https://bitbucket.org/disciplemakers/check-reader
6
+ Author: DiscipleMakers
7
+ License: MIT
8
+ Keywords: check scanner,micr,magtek,rdm
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Framework :: Bottle
12
+ Classifier: Topic :: Office/Business :: Financial
13
+ Requires-Python: <3.13,>=3.6
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: gevent<22.0.0,>=1.5.0; python_version < "3.7"
17
+ Requires-Dist: gevent>=23.0.0; python_version >= "3.7"
18
+ Requires-Dist: Pillow<9.0.0,>=5.0.0; python_version < "3.7"
19
+ Requires-Dist: Pillow>=10.0.0; python_version >= "3.7"
20
+ Requires-Dist: pyftpdlib<2.0.0,>=1.5.0; python_version < "3.7"
21
+ Requires-Dist: pyftpdlib>=1.5.7; python_version >= "3.7"
22
+ Requires-Dist: PyJWT<2.5.0,>=2.0.0; python_version < "3.7"
23
+ Requires-Dist: PyJWT>=2.8.0; python_version >= "3.7"
24
+ Requires-Dist: requests<2.28.0,>=2.20.0; python_version < "3.7"
25
+ Requires-Dist: requests>=2.31.0; python_version >= "3.7"
26
+ Requires-Dist: toml>=0.10.0
27
+ Requires-Dist: click<8.1,>=7.1; python_version < "3.7"
28
+ Requires-Dist: click>=8.1; python_version >= "3.7"
29
+ Requires-Dist: rich<13.0,>=12.0; python_version < "3.7"
30
+ Requires-Dist: rich>=13.0; python_version >= "3.7"
31
+ Requires-Dist: questionary<2.0,>=1.10.0; python_version < "3.8"
32
+ Requires-Dist: questionary>=2.0; python_version >= "3.8"
33
+ Dynamic: license-file
34
+
35
+ # checkscanner
36
+
37
+ Python package for the check scanner backend. Manages connections to Magtek and RDM check scanning devices, extracts MICR data and check images, and serves them through a JWT-authenticated REST API.
38
+
39
+ ## Requirements
40
+
41
+ - Python 3.6+
42
+ - SQLite3
43
+ - pip 21.3.1+ (required on Python 3.6 to resolve pre-built wheels)
44
+
45
+ ## Installation
46
+
47
+ Ensure pip is up to date before installing:
48
+
49
+ ```bash
50
+ pip install --upgrade pip
51
+ ```
52
+
53
+ Then install the package from PyPI:
54
+
55
+ ```bash
56
+ pip install checkscanner
57
+ ```
58
+
59
+ Or from the source tree:
60
+
61
+ ```bash
62
+ cd server
63
+ pip install .
64
+ ```
65
+
66
+ For development (changes take effect without reinstalling):
67
+
68
+ ```bash
69
+ pip install -e .
70
+ ```
71
+
72
+ ## Building the Frontend
73
+
74
+ The frontend must be built and bundled into the package before installing. Run this once from the source tree (requires Node.js):
75
+
76
+ ```bash
77
+ checkscanner build
78
+ pip install . # reinstall to pick up the bundled frontend
79
+ ```
80
+
81
+ In development with an editable install, `checkscanner build` copies the output directly into `src/checkscanner/static/` so it takes effect immediately without reinstalling.
82
+
83
+ ## Usage
84
+
85
+ The package installs two commands. Run them from your project directory — config files and the database are created there.
86
+
87
+ ```bash
88
+ # Start the API server (serves UI at http://localhost:8000/).
89
+ # Runs first-time setup automatically if needed.
90
+ checkscanner
91
+
92
+ # Open the interactive admin console
93
+ checkscanner-cli
94
+ ```
95
+
96
+ `checkscanner` also accepts subcommands (run `checkscanner --help` or `checkscanner <command> --help` for details):
97
+
98
+ ```bash
99
+ checkscanner server [options] # Same as no arguments; see `checkscanner server --help`
100
+ checkscanner setup [-i] # Create anything missing: config/, server.db, admin user (-i: edit config first)
101
+ checkscanner status # Show config, database and connection status
102
+ checkscanner admin # Same as checkscanner-cli
103
+ checkscanner build # Build the frontend and bundle it into the package (source tree only)
104
+
105
+ checkscanner user list|add|passwd|delete
106
+ checkscanner profile list|add|delete
107
+ checkscanner config show|edit|rotate-keys
108
+ checkscanner token <username>[:<password>] # or -u <username> [-p <password>]
109
+ ```
110
+
111
+ Server options can also be passed directly, e.g. `checkscanner --port 9000`.
112
+
113
+ ## First-Time Setup
114
+
115
+ `checkscanner setup` (also runs automatically before the server and admin console start) creates:
116
+
117
+ | File | Description |
118
+ |---|---|
119
+ | `config/config.toml` | Server host, port, debug mode |
120
+ | `config/auth.toml` | JWT signing key, FTP credentials |
121
+ | `server.db` | SQLite database |
122
+ | `admin_credentials.txt` | Generated admin username and password — delete after noting |
123
+
124
+ ## Admin Console
125
+
126
+ `checkscanner-cli` opens an arrow-key menu for managing the server:
127
+
128
+ - View server status
129
+ - List, add and delete users; reset passwords
130
+ - List, add and delete check scanner profiles
131
+ - Edit config and regenerate auth keys
132
+ - Get a bearer token for API testing
133
+ - Start the server, optionally restarting it automatically when it stops
134
+
135
+ Every menu action is also available as a `checkscanner` subcommand for scripting.
136
+
137
+ ## API
138
+
139
+ Swagger UI is available at `http://localhost:8000/docs` when the server is running.
140
+
141
+ | Method | Endpoint | Description |
142
+ |---|---|---|
143
+ | POST | `/token` | Authenticate, receive JWT |
144
+ | GET/POST | `/connections` | List or create reader connections |
145
+ | DELETE | `/connections/<id>` | Disconnect from reader |
146
+ | GET/PUT | `/connections/<id>/check` | Get check data or return check to queue |
147
+ | GET/POST/PATCH/DELETE | `/profiles` | Manage reader device profiles |
148
+ | GET/POST/PATCH/DELETE | `/users` | Manage users (admin only) |
149
+
150
+ ## Supported Devices
151
+
152
+ | Type | Protocol |
153
+ |---|---|
154
+ | Magtek | Telnet + FTP |
155
+ | RDM | HTTPS (XML) |
156
+
157
+ ## Project Structure
158
+
159
+ ```
160
+ src/
161
+ └── checkscanner/
162
+ ├── app.py # Bottle API server
163
+ ├── admin_console.py # Interactive admin CLI
164
+ ├── active_scanners.py # In-memory scanner connection registry
165
+ ├── auth.py # Password hashing and JWT verification
166
+ ├── ftp_server.py # FTP server for Magtek image uploads
167
+ ├── debugger.py # Colored terminal logging
168
+ ├── bottle/ # Vendored Bottle framework + plugins
169
+ ├── resources/ # REST resource handlers
170
+ ├── check_scanner_handlers/ # Device-specific connection handlers
171
+ ├── static/ # Bundled frontend (populated by `checkscanner build`)
172
+ ├── swagger/ # Bundled Swagger UI
173
+ └── data/ # Bundled assets (schema, font, API spec)
174
+ ```
175
+
176
+ ## Releasing to PyPI
177
+
178
+ Releases are published by the Bitbucket pipeline (`bitbucket-pipelines.yml`) when a `v*` tag is pushed. The pipeline builds the frontend, bundles it into the package, builds the sdist and wheel, uploads them to PyPI, and uploads the wheel to Bitbucket Downloads.
179
+
180
+ 1. Bump `version` in `setup.cfg` (PyPI rejects re-uploads of an existing version).
181
+ 2. Commit, then tag with the matching version and push the tag:
182
+ ```bash
183
+ git tag v0.1.0
184
+ git push origin v0.1.0
185
+ ```
186
+
187
+ The pipeline fails if the tag does not match `setup.cfg`. It needs these secured repository variables:
188
+
189
+ | Variable | Purpose |
190
+ |---|---|
191
+ | `PYPI_TOKEN` | PyPI API token (`pypi-...`) used by twine |
192
+ | `BB_TOKEN` | Bitbucket token for the Downloads upload |
193
+
194
+ To build locally instead:
195
+
196
+ ```bash
197
+ checkscanner build # bundle the frontend first
198
+ python -m build # creates dist/*.tar.gz and dist/*.whl
199
+ twine check dist/*
200
+ twine upload dist/*
201
+ ```
202
+
203
+ ## Dependencies
204
+
205
+ | Package | Purpose |
206
+ |---|---|
207
+ | `gevent` | Async concurrency for the server |
208
+ | `Pillow` | Check image processing |
209
+ | `pyftpdlib` | FTP server for Magtek readers |
210
+ | `PyJWT` | JWT signing and verification |
211
+ | `requests` | HTTP client for RDM readers |
212
+ | `toml` | Config file parsing |
@@ -0,0 +1,178 @@
1
+ # checkscanner
2
+
3
+ Python package for the check scanner backend. Manages connections to Magtek and RDM check scanning devices, extracts MICR data and check images, and serves them through a JWT-authenticated REST API.
4
+
5
+ ## Requirements
6
+
7
+ - Python 3.6+
8
+ - SQLite3
9
+ - pip 21.3.1+ (required on Python 3.6 to resolve pre-built wheels)
10
+
11
+ ## Installation
12
+
13
+ Ensure pip is up to date before installing:
14
+
15
+ ```bash
16
+ pip install --upgrade pip
17
+ ```
18
+
19
+ Then install the package from PyPI:
20
+
21
+ ```bash
22
+ pip install checkscanner
23
+ ```
24
+
25
+ Or from the source tree:
26
+
27
+ ```bash
28
+ cd server
29
+ pip install .
30
+ ```
31
+
32
+ For development (changes take effect without reinstalling):
33
+
34
+ ```bash
35
+ pip install -e .
36
+ ```
37
+
38
+ ## Building the Frontend
39
+
40
+ The frontend must be built and bundled into the package before installing. Run this once from the source tree (requires Node.js):
41
+
42
+ ```bash
43
+ checkscanner build
44
+ pip install . # reinstall to pick up the bundled frontend
45
+ ```
46
+
47
+ In development with an editable install, `checkscanner build` copies the output directly into `src/checkscanner/static/` so it takes effect immediately without reinstalling.
48
+
49
+ ## Usage
50
+
51
+ The package installs two commands. Run them from your project directory — config files and the database are created there.
52
+
53
+ ```bash
54
+ # Start the API server (serves UI at http://localhost:8000/).
55
+ # Runs first-time setup automatically if needed.
56
+ checkscanner
57
+
58
+ # Open the interactive admin console
59
+ checkscanner-cli
60
+ ```
61
+
62
+ `checkscanner` also accepts subcommands (run `checkscanner --help` or `checkscanner <command> --help` for details):
63
+
64
+ ```bash
65
+ checkscanner server [options] # Same as no arguments; see `checkscanner server --help`
66
+ checkscanner setup [-i] # Create anything missing: config/, server.db, admin user (-i: edit config first)
67
+ checkscanner status # Show config, database and connection status
68
+ checkscanner admin # Same as checkscanner-cli
69
+ checkscanner build # Build the frontend and bundle it into the package (source tree only)
70
+
71
+ checkscanner user list|add|passwd|delete
72
+ checkscanner profile list|add|delete
73
+ checkscanner config show|edit|rotate-keys
74
+ checkscanner token <username>[:<password>] # or -u <username> [-p <password>]
75
+ ```
76
+
77
+ Server options can also be passed directly, e.g. `checkscanner --port 9000`.
78
+
79
+ ## First-Time Setup
80
+
81
+ `checkscanner setup` (also runs automatically before the server and admin console start) creates:
82
+
83
+ | File | Description |
84
+ |---|---|
85
+ | `config/config.toml` | Server host, port, debug mode |
86
+ | `config/auth.toml` | JWT signing key, FTP credentials |
87
+ | `server.db` | SQLite database |
88
+ | `admin_credentials.txt` | Generated admin username and password — delete after noting |
89
+
90
+ ## Admin Console
91
+
92
+ `checkscanner-cli` opens an arrow-key menu for managing the server:
93
+
94
+ - View server status
95
+ - List, add and delete users; reset passwords
96
+ - List, add and delete check scanner profiles
97
+ - Edit config and regenerate auth keys
98
+ - Get a bearer token for API testing
99
+ - Start the server, optionally restarting it automatically when it stops
100
+
101
+ Every menu action is also available as a `checkscanner` subcommand for scripting.
102
+
103
+ ## API
104
+
105
+ Swagger UI is available at `http://localhost:8000/docs` when the server is running.
106
+
107
+ | Method | Endpoint | Description |
108
+ |---|---|---|
109
+ | POST | `/token` | Authenticate, receive JWT |
110
+ | GET/POST | `/connections` | List or create reader connections |
111
+ | DELETE | `/connections/<id>` | Disconnect from reader |
112
+ | GET/PUT | `/connections/<id>/check` | Get check data or return check to queue |
113
+ | GET/POST/PATCH/DELETE | `/profiles` | Manage reader device profiles |
114
+ | GET/POST/PATCH/DELETE | `/users` | Manage users (admin only) |
115
+
116
+ ## Supported Devices
117
+
118
+ | Type | Protocol |
119
+ |---|---|
120
+ | Magtek | Telnet + FTP |
121
+ | RDM | HTTPS (XML) |
122
+
123
+ ## Project Structure
124
+
125
+ ```
126
+ src/
127
+ └── checkscanner/
128
+ ├── app.py # Bottle API server
129
+ ├── admin_console.py # Interactive admin CLI
130
+ ├── active_scanners.py # In-memory scanner connection registry
131
+ ├── auth.py # Password hashing and JWT verification
132
+ ├── ftp_server.py # FTP server for Magtek image uploads
133
+ ├── debugger.py # Colored terminal logging
134
+ ├── bottle/ # Vendored Bottle framework + plugins
135
+ ├── resources/ # REST resource handlers
136
+ ├── check_scanner_handlers/ # Device-specific connection handlers
137
+ ├── static/ # Bundled frontend (populated by `checkscanner build`)
138
+ ├── swagger/ # Bundled Swagger UI
139
+ └── data/ # Bundled assets (schema, font, API spec)
140
+ ```
141
+
142
+ ## Releasing to PyPI
143
+
144
+ Releases are published by the Bitbucket pipeline (`bitbucket-pipelines.yml`) when a `v*` tag is pushed. The pipeline builds the frontend, bundles it into the package, builds the sdist and wheel, uploads them to PyPI, and uploads the wheel to Bitbucket Downloads.
145
+
146
+ 1. Bump `version` in `setup.cfg` (PyPI rejects re-uploads of an existing version).
147
+ 2. Commit, then tag with the matching version and push the tag:
148
+ ```bash
149
+ git tag v0.1.0
150
+ git push origin v0.1.0
151
+ ```
152
+
153
+ The pipeline fails if the tag does not match `setup.cfg`. It needs these secured repository variables:
154
+
155
+ | Variable | Purpose |
156
+ |---|---|
157
+ | `PYPI_TOKEN` | PyPI API token (`pypi-...`) used by twine |
158
+ | `BB_TOKEN` | Bitbucket token for the Downloads upload |
159
+
160
+ To build locally instead:
161
+
162
+ ```bash
163
+ checkscanner build # bundle the frontend first
164
+ python -m build # creates dist/*.tar.gz and dist/*.whl
165
+ twine check dist/*
166
+ twine upload dist/*
167
+ ```
168
+
169
+ ## Dependencies
170
+
171
+ | Package | Purpose |
172
+ |---|---|
173
+ | `gevent` | Async concurrency for the server |
174
+ | `Pillow` | Check image processing |
175
+ | `pyftpdlib` | FTP server for Magtek readers |
176
+ | `PyJWT` | JWT signing and verification |
177
+ | `requests` | HTTP client for RDM readers |
178
+ | `toml` | Config file parsing |
@@ -0,0 +1,3 @@
1
+ [build-system]
2
+ requires = ["setuptools>=42"]
3
+ build-backend = "setuptools.build_meta"
@@ -0,0 +1,54 @@
1
+ [metadata]
2
+ name = checkscanner
3
+ version = 0.1.2
4
+ description = Check scanner API server and admin console
5
+ long_description = file: README.md
6
+ long_description_content_type = text/markdown
7
+ author = DiscipleMakers
8
+ url = https://bitbucket.org/disciplemakers/check-reader
9
+ license = MIT
10
+ license_files = LICENSE
11
+ keywords = check scanner, micr, magtek, rdm
12
+ classifiers =
13
+ Programming Language :: Python :: 3
14
+ Operating System :: OS Independent
15
+ Framework :: Bottle
16
+ Topic :: Office/Business :: Financial
17
+
18
+ [options]
19
+ package_dir =
20
+ = src
21
+ packages = find_namespace:
22
+ python_requires = >=3.6, <3.13
23
+ include_package_data = True
24
+ install_requires =
25
+ gevent>=1.5.0,<22.0.0; python_version < "3.7"
26
+ gevent>=23.0.0; python_version >= "3.7"
27
+ Pillow>=5.0.0,<9.0.0; python_version < "3.7"
28
+ Pillow>=10.0.0; python_version >= "3.7"
29
+ pyftpdlib>=1.5.0,<2.0.0; python_version < "3.7"
30
+ pyftpdlib>=1.5.7; python_version >= "3.7"
31
+ PyJWT>=2.0.0,<2.5.0; python_version < "3.7"
32
+ PyJWT>=2.8.0; python_version >= "3.7"
33
+ requests>=2.20.0,<2.28.0; python_version < "3.7"
34
+ requests>=2.31.0; python_version >= "3.7"
35
+ toml>=0.10.0
36
+ click>=7.1,<8.1; python_version < "3.7"
37
+ click>=8.1; python_version >= "3.7"
38
+ rich>=12.0,<13.0; python_version < "3.7"
39
+ rich>=13.0; python_version >= "3.7"
40
+ questionary>=1.10.0,<2.0; python_version < "3.8"
41
+ questionary>=2.0; python_version >= "3.8"
42
+
43
+ [options.packages.find]
44
+ where = src
45
+
46
+ [options.entry_points]
47
+ console_scripts =
48
+ checkscanner = checkscanner.cli:main
49
+ checkscanner-cli = checkscanner.cli:admin_main
50
+
51
+ [egg_info]
52
+ tag_build =
53
+ tag_date = 0
54
+
@@ -0,0 +1,2 @@
1
+ from setuptools import setup
2
+ setup()
@@ -0,0 +1,5 @@
1
+ import os
2
+
3
+ DATA_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data")
4
+ STATIC_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "static")
5
+ SWAGGER_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "swagger")
@@ -0,0 +1,53 @@
1
+ from threading import Thread
2
+ from . import debugger
3
+ from .check_scanner_handlers import MagtekScannerHandler, RDMScannerHandler, CheckScannerHandler
4
+ from .ftp_server import DummyFTP
5
+
6
+ class Singleton (type):
7
+ _instances = {}
8
+ def __call__(cls, *args, **kwargs):
9
+ if cls not in cls._instances:
10
+ cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
11
+ return cls._instances[cls]
12
+
13
+ class ActiveScanners(dict, metaclass=Singleton):
14
+
15
+ handlers = {
16
+ "magtek": MagtekScannerHandler,
17
+ "rdm": RDMScannerHandler
18
+ }
19
+
20
+ def __init__(self, sleep_time = 5, handlers: dict = None) -> None:
21
+ debugger.print("Initializing active scanners handler")
22
+ self.sleep_time = sleep_time
23
+ self.ftp = None
24
+ if handlers:
25
+ self.handlers = handlers
26
+ self.started = False
27
+
28
+ def newReaderHandler(self, reader_type) -> CheckScannerHandler:
29
+ handler = self.handlers[reader_type](self)
30
+ if reader_type == "magtek":
31
+ if not self.ftp:
32
+ self.ftp = DummyFTP()
33
+ self.ftp_thread = Thread(target=self.ftp.start, daemon=True).start()
34
+ handler.ftp = self.ftp
35
+ return handler
36
+
37
+ def isReaderActive(self, reader_host):
38
+ for crh in self.values():
39
+ if reader_host == crh.host:
40
+ return True
41
+ return False
42
+
43
+ def jsonSerialize(self):
44
+ return {
45
+ "active_scanners": [{"conn_id": conn_id, **conn} for conn_id, conn in self.items()]
46
+ }
47
+
48
+ def start(self):
49
+ if not self.started:
50
+ self.started = True
51
+
52
+ def stop(self):
53
+ self.started = False