leafdocs 0.1.0__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.
leafdocs-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Anshul Raj
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,310 @@
1
+ Metadata-Version: 2.4
2
+ Name: leafdocs
3
+ Version: 0.1.0
4
+ Summary: Turn a directory of Markdown files into a self-hosted, searchable web reader.
5
+ Author-email: Anshul <you@example.com>
6
+ License: MIT
7
+ Keywords: markdown,docs,flask,self-hosted
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Framework :: Flask
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Requires-Python: >=3.10
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: flask>=3.0
16
+ Requires-Dist: markdown>=3.5
17
+ Requires-Dist: python-frontmatter>=1.1
18
+ Requires-Dist: bcrypt>=4.1
19
+ Requires-Dist: python-dotenv>=1.0
20
+ Provides-Extra: dev
21
+ Requires-Dist: pytest; extra == "dev"
22
+ Requires-Dist: pytest-flask; extra == "dev"
23
+ Dynamic: license-file
24
+
25
+ # leafdocs
26
+
27
+ A lightweight Python library that turns a directory of Markdown files into a self-hosted, searchable web reader.
28
+
29
+ Install it, point it at a folder, get a running Flask app you can deploy anywhere.
30
+
31
+ ```python
32
+ from leafdocs import LeafDocs
33
+
34
+ ld = LeafDocs(docs_dir="./docs")
35
+ ld.run()
36
+ ```
37
+
38
+ ---
39
+
40
+ ## Installation
41
+
42
+ ```bash
43
+ pip install leafdocs
44
+ ```
45
+
46
+ ---
47
+
48
+ ## Usage
49
+
50
+ ### Minimal setup
51
+
52
+ ```python
53
+ from leafdocs import LeafDocs
54
+
55
+ ld = LeafDocs(docs_dir="./docs")
56
+ ld.run()
57
+ ```
58
+
59
+ Visit `http://127.0.0.1:5000` — you'll see a searchable index of all `.md` files in your `docs/` directory.
60
+
61
+ ### Constructor arguments
62
+
63
+ | Argument | Type | Default | Description |
64
+ |--------------|-----------------|-----------------|--------------------------------------------------|
65
+ | `docs_dir` | `str` | `"./docs"` | Path to the directory containing `.md` files |
66
+ | `secret_key` | `str` or `None` | `None` | Flask session secret key (see Auth section) |
67
+
68
+ ### Accessing the Flask app
69
+
70
+ The underlying Flask app is exposed as `ld.flask_app`. Use it to add routes, middleware, or blueprints:
71
+
72
+ ```python
73
+ from leafdocs import LeafDocs
74
+
75
+ ld = LeafDocs(docs_dir="./docs")
76
+
77
+ @ld.flask_app.route("/health")
78
+ def health():
79
+ return {"status": "ok"}
80
+
81
+ ld.run()
82
+ ```
83
+
84
+ ---
85
+
86
+ ## Frontmatter
87
+
88
+ Each `.md` file can optionally include YAML frontmatter. Supported fields:
89
+
90
+ ```yaml
91
+ ---
92
+ title: My Document Title
93
+ tags: [python, tutorial]
94
+ ---
95
+ ```
96
+
97
+ | Field | Type | Fallback |
98
+ |---------|--------------------------------|----------------------|
99
+ | `title` | string | Filename, titlecased |
100
+ | `tags` | list or comma-separated string | None |
101
+
102
+ Frontmatter is optional — files without it are discovered and served normally.
103
+
104
+ ---
105
+
106
+ ## Authentication
107
+
108
+ By default the server runs open with no login required.
109
+
110
+ To enable pin-based auth, add pins to a `.env` file in your working directory:
111
+
112
+ ```
113
+ LEAFDOCS_PINS=mypin123,anotherpin
114
+ ```
115
+
116
+ Restart the server. All routes will now require a valid pin.
117
+
118
+ **How it works:**
119
+ - Pins are hashed with bcrypt at startup — raw values are never stored in memory
120
+ - A successful login issues an `httponly` session cookie
121
+ - Multiple pins are supported — useful for sharing access without a shared secret
122
+ - To invalidate all sessions, rotate or remove the pin and restart
123
+
124
+ ### Session secret key
125
+
126
+ By default a random secret key is generated at startup, which means sessions are invalidated on every restart. For persistent sessions across restarts, set a stable key:
127
+
128
+ ```
129
+ LEAFDOCS_SECRET_KEY=your-long-random-secret-here
130
+ ```
131
+
132
+ Or pass it directly in code:
133
+
134
+ ```python
135
+ ld = LeafDocs(docs_dir="./docs", secret_key="your-long-random-secret-here")
136
+ ```
137
+
138
+ Generate a good key with:
139
+
140
+ ```bash
141
+ python -c "import secrets; print(secrets.token_hex(32))"
142
+ ```
143
+
144
+ ---
145
+
146
+ ## Configuration reference
147
+
148
+ All pins and the secret key are configured via `.env` only. Copy `.env.example` to `.env` to get started:
149
+
150
+ ```
151
+ LEAFDOCS_PINS=pin1,pin2
152
+ LEAFDOCS_SECRET_KEY=your-secret-here
153
+ ```
154
+
155
+ ---
156
+
157
+ ## Known limitations
158
+
159
+ - **No caching** — Markdown is rendered on every request. Fine for personal or low-traffic use; not suitable for high-traffic production serving.
160
+ - **No per-device session revocation** — rotating the pin invalidates all active sessions across all devices.
161
+ - **No plugin system** — the primary extension hook is `ld.flask_app`. Add routes and middleware directly on the Flask app.
162
+
163
+ ---
164
+
165
+ ## Deployment
166
+
167
+ > **Disclaimer:** leafdocs is a Flask application. Running it with `ld.run()` uses Flask's built-in development server, which is not suitable for production. For production use, you are responsible for:
168
+ > - Running behind a production WSGI server (Gunicorn recommended)
169
+ > - Terminating HTTPS at a reverse proxy (Nginx recommended)
170
+ > - Managing the process with a supervisor (systemd recommended)
171
+ >
172
+ > The instructions below cover a standard Nginx + Gunicorn + systemd setup.
173
+
174
+ ### AWS EC2 / GCP Compute Engine
175
+
176
+ The steps are identical for both — the only difference is how you provision the VM.
177
+
178
+ #### 1. Provision a VM
179
+
180
+ - **AWS:** Launch an EC2 instance (Ubuntu 24.04 LTS, `t3.micro` or larger). Open ports 80 and 443 in the security group.
181
+ - **GCP:** Create a Compute Engine instance (Ubuntu 24.04 LTS, `e2-micro` or larger). Open ports 80 and 443 in the firewall rules.
182
+
183
+ SSH into the instance.
184
+
185
+ #### 2. Install dependencies
186
+
187
+ ```bash
188
+ sudo apt update && sudo apt install -y python3-pip python3-venv nginx
189
+ ```
190
+
191
+ #### 3. Set up the app
192
+
193
+ ```bash
194
+ mkdir ~/leafdocs-app && cd ~/leafdocs-app
195
+ python3 -m venv .venv
196
+ source .venv/bin/activate
197
+ pip install leafdocs gunicorn
198
+ ```
199
+
200
+ Create your `docs/` directory and add your `.md` files:
201
+
202
+ ```bash
203
+ mkdir docs
204
+ ```
205
+
206
+ Create `main.py`:
207
+
208
+ ```python
209
+ from leafdocs import LeafDocs
210
+
211
+ ld = LeafDocs(docs_dir="./docs")
212
+ app = ld.flask_app
213
+ ```
214
+
215
+ Create `.env`:
216
+
217
+ ```
218
+ LEAFDOCS_PINS=yourpin
219
+ LEAFDOCS_SECRET_KEY=your-long-random-secret-here
220
+ ```
221
+
222
+ Test it runs:
223
+
224
+ ```bash
225
+ gunicorn --bind 127.0.0.1:8000 main:app
226
+ ```
227
+
228
+ #### 4. Configure systemd
229
+
230
+ Create `/etc/systemd/system/leafdocs.service`:
231
+
232
+ ```ini
233
+ [Unit]
234
+ Description=LeafDocs
235
+ After=network.target
236
+
237
+ [Service]
238
+ User=ubuntu
239
+ WorkingDirectory=/home/ubuntu/leafdocs-app
240
+ Environment="PATH=/home/ubuntu/leafdocs-app/.venv/bin"
241
+ ExecStart=/home/ubuntu/leafdocs-app/.venv/bin/gunicorn --workers 2 --bind 127.0.0.1:8000 main:app
242
+ Restart=always
243
+
244
+ [Install]
245
+ WantedBy=multi-user.target
246
+ ```
247
+
248
+ Enable and start:
249
+
250
+ ```bash
251
+ sudo systemctl daemon-reload
252
+ sudo systemctl enable leafdocs
253
+ sudo systemctl start leafdocs
254
+ sudo systemctl status leafdocs
255
+ ```
256
+
257
+ #### 5. Configure Nginx
258
+
259
+ Create `/etc/nginx/sites-available/leafdocs`:
260
+
261
+ ```nginx
262
+ server {
263
+ listen 80;
264
+ server_name your-domain.com;
265
+
266
+ location / {
267
+ proxy_pass http://127.0.0.1:8000;
268
+ proxy_set_header Host $host;
269
+ proxy_set_header X-Real-IP $remote_addr;
270
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
271
+ proxy_set_header X-Forwarded-Proto $scheme;
272
+ }
273
+ }
274
+ ```
275
+
276
+ Enable and reload:
277
+
278
+ ```bash
279
+ sudo ln -s /etc/nginx/sites-available/leafdocs /etc/nginx/sites-enabled/
280
+ sudo nginx -t
281
+ sudo systemctl reload nginx
282
+ ```
283
+
284
+ #### 6. Enable HTTPS
285
+
286
+ ```bash
287
+ sudo apt install -y certbot python3-certbot-nginx
288
+ sudo certbot --nginx -d your-domain.com
289
+ ```
290
+
291
+ Certbot will automatically update your Nginx config and set up auto-renewal.
292
+
293
+ ---
294
+
295
+ ## Development
296
+
297
+ ```bash
298
+ git clone https://github.com/anshulraj10/leafdocs
299
+ cd leafdocs
300
+ python -m venv .venv
301
+ source .venv/bin/activate
302
+ pip install -e ".[dev]"
303
+ pytest tests/ -v
304
+ ```
305
+
306
+ ---
307
+
308
+ ## License
309
+
310
+ MIT
@@ -0,0 +1,286 @@
1
+ # leafdocs
2
+
3
+ A lightweight Python library that turns a directory of Markdown files into a self-hosted, searchable web reader.
4
+
5
+ Install it, point it at a folder, get a running Flask app you can deploy anywhere.
6
+
7
+ ```python
8
+ from leafdocs import LeafDocs
9
+
10
+ ld = LeafDocs(docs_dir="./docs")
11
+ ld.run()
12
+ ```
13
+
14
+ ---
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ pip install leafdocs
20
+ ```
21
+
22
+ ---
23
+
24
+ ## Usage
25
+
26
+ ### Minimal setup
27
+
28
+ ```python
29
+ from leafdocs import LeafDocs
30
+
31
+ ld = LeafDocs(docs_dir="./docs")
32
+ ld.run()
33
+ ```
34
+
35
+ Visit `http://127.0.0.1:5000` — you'll see a searchable index of all `.md` files in your `docs/` directory.
36
+
37
+ ### Constructor arguments
38
+
39
+ | Argument | Type | Default | Description |
40
+ |--------------|-----------------|-----------------|--------------------------------------------------|
41
+ | `docs_dir` | `str` | `"./docs"` | Path to the directory containing `.md` files |
42
+ | `secret_key` | `str` or `None` | `None` | Flask session secret key (see Auth section) |
43
+
44
+ ### Accessing the Flask app
45
+
46
+ The underlying Flask app is exposed as `ld.flask_app`. Use it to add routes, middleware, or blueprints:
47
+
48
+ ```python
49
+ from leafdocs import LeafDocs
50
+
51
+ ld = LeafDocs(docs_dir="./docs")
52
+
53
+ @ld.flask_app.route("/health")
54
+ def health():
55
+ return {"status": "ok"}
56
+
57
+ ld.run()
58
+ ```
59
+
60
+ ---
61
+
62
+ ## Frontmatter
63
+
64
+ Each `.md` file can optionally include YAML frontmatter. Supported fields:
65
+
66
+ ```yaml
67
+ ---
68
+ title: My Document Title
69
+ tags: [python, tutorial]
70
+ ---
71
+ ```
72
+
73
+ | Field | Type | Fallback |
74
+ |---------|--------------------------------|----------------------|
75
+ | `title` | string | Filename, titlecased |
76
+ | `tags` | list or comma-separated string | None |
77
+
78
+ Frontmatter is optional — files without it are discovered and served normally.
79
+
80
+ ---
81
+
82
+ ## Authentication
83
+
84
+ By default the server runs open with no login required.
85
+
86
+ To enable pin-based auth, add pins to a `.env` file in your working directory:
87
+
88
+ ```
89
+ LEAFDOCS_PINS=mypin123,anotherpin
90
+ ```
91
+
92
+ Restart the server. All routes will now require a valid pin.
93
+
94
+ **How it works:**
95
+ - Pins are hashed with bcrypt at startup — raw values are never stored in memory
96
+ - A successful login issues an `httponly` session cookie
97
+ - Multiple pins are supported — useful for sharing access without a shared secret
98
+ - To invalidate all sessions, rotate or remove the pin and restart
99
+
100
+ ### Session secret key
101
+
102
+ By default a random secret key is generated at startup, which means sessions are invalidated on every restart. For persistent sessions across restarts, set a stable key:
103
+
104
+ ```
105
+ LEAFDOCS_SECRET_KEY=your-long-random-secret-here
106
+ ```
107
+
108
+ Or pass it directly in code:
109
+
110
+ ```python
111
+ ld = LeafDocs(docs_dir="./docs", secret_key="your-long-random-secret-here")
112
+ ```
113
+
114
+ Generate a good key with:
115
+
116
+ ```bash
117
+ python -c "import secrets; print(secrets.token_hex(32))"
118
+ ```
119
+
120
+ ---
121
+
122
+ ## Configuration reference
123
+
124
+ All pins and the secret key are configured via `.env` only. Copy `.env.example` to `.env` to get started:
125
+
126
+ ```
127
+ LEAFDOCS_PINS=pin1,pin2
128
+ LEAFDOCS_SECRET_KEY=your-secret-here
129
+ ```
130
+
131
+ ---
132
+
133
+ ## Known limitations
134
+
135
+ - **No caching** — Markdown is rendered on every request. Fine for personal or low-traffic use; not suitable for high-traffic production serving.
136
+ - **No per-device session revocation** — rotating the pin invalidates all active sessions across all devices.
137
+ - **No plugin system** — the primary extension hook is `ld.flask_app`. Add routes and middleware directly on the Flask app.
138
+
139
+ ---
140
+
141
+ ## Deployment
142
+
143
+ > **Disclaimer:** leafdocs is a Flask application. Running it with `ld.run()` uses Flask's built-in development server, which is not suitable for production. For production use, you are responsible for:
144
+ > - Running behind a production WSGI server (Gunicorn recommended)
145
+ > - Terminating HTTPS at a reverse proxy (Nginx recommended)
146
+ > - Managing the process with a supervisor (systemd recommended)
147
+ >
148
+ > The instructions below cover a standard Nginx + Gunicorn + systemd setup.
149
+
150
+ ### AWS EC2 / GCP Compute Engine
151
+
152
+ The steps are identical for both — the only difference is how you provision the VM.
153
+
154
+ #### 1. Provision a VM
155
+
156
+ - **AWS:** Launch an EC2 instance (Ubuntu 24.04 LTS, `t3.micro` or larger). Open ports 80 and 443 in the security group.
157
+ - **GCP:** Create a Compute Engine instance (Ubuntu 24.04 LTS, `e2-micro` or larger). Open ports 80 and 443 in the firewall rules.
158
+
159
+ SSH into the instance.
160
+
161
+ #### 2. Install dependencies
162
+
163
+ ```bash
164
+ sudo apt update && sudo apt install -y python3-pip python3-venv nginx
165
+ ```
166
+
167
+ #### 3. Set up the app
168
+
169
+ ```bash
170
+ mkdir ~/leafdocs-app && cd ~/leafdocs-app
171
+ python3 -m venv .venv
172
+ source .venv/bin/activate
173
+ pip install leafdocs gunicorn
174
+ ```
175
+
176
+ Create your `docs/` directory and add your `.md` files:
177
+
178
+ ```bash
179
+ mkdir docs
180
+ ```
181
+
182
+ Create `main.py`:
183
+
184
+ ```python
185
+ from leafdocs import LeafDocs
186
+
187
+ ld = LeafDocs(docs_dir="./docs")
188
+ app = ld.flask_app
189
+ ```
190
+
191
+ Create `.env`:
192
+
193
+ ```
194
+ LEAFDOCS_PINS=yourpin
195
+ LEAFDOCS_SECRET_KEY=your-long-random-secret-here
196
+ ```
197
+
198
+ Test it runs:
199
+
200
+ ```bash
201
+ gunicorn --bind 127.0.0.1:8000 main:app
202
+ ```
203
+
204
+ #### 4. Configure systemd
205
+
206
+ Create `/etc/systemd/system/leafdocs.service`:
207
+
208
+ ```ini
209
+ [Unit]
210
+ Description=LeafDocs
211
+ After=network.target
212
+
213
+ [Service]
214
+ User=ubuntu
215
+ WorkingDirectory=/home/ubuntu/leafdocs-app
216
+ Environment="PATH=/home/ubuntu/leafdocs-app/.venv/bin"
217
+ ExecStart=/home/ubuntu/leafdocs-app/.venv/bin/gunicorn --workers 2 --bind 127.0.0.1:8000 main:app
218
+ Restart=always
219
+
220
+ [Install]
221
+ WantedBy=multi-user.target
222
+ ```
223
+
224
+ Enable and start:
225
+
226
+ ```bash
227
+ sudo systemctl daemon-reload
228
+ sudo systemctl enable leafdocs
229
+ sudo systemctl start leafdocs
230
+ sudo systemctl status leafdocs
231
+ ```
232
+
233
+ #### 5. Configure Nginx
234
+
235
+ Create `/etc/nginx/sites-available/leafdocs`:
236
+
237
+ ```nginx
238
+ server {
239
+ listen 80;
240
+ server_name your-domain.com;
241
+
242
+ location / {
243
+ proxy_pass http://127.0.0.1:8000;
244
+ proxy_set_header Host $host;
245
+ proxy_set_header X-Real-IP $remote_addr;
246
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
247
+ proxy_set_header X-Forwarded-Proto $scheme;
248
+ }
249
+ }
250
+ ```
251
+
252
+ Enable and reload:
253
+
254
+ ```bash
255
+ sudo ln -s /etc/nginx/sites-available/leafdocs /etc/nginx/sites-enabled/
256
+ sudo nginx -t
257
+ sudo systemctl reload nginx
258
+ ```
259
+
260
+ #### 6. Enable HTTPS
261
+
262
+ ```bash
263
+ sudo apt install -y certbot python3-certbot-nginx
264
+ sudo certbot --nginx -d your-domain.com
265
+ ```
266
+
267
+ Certbot will automatically update your Nginx config and set up auto-renewal.
268
+
269
+ ---
270
+
271
+ ## Development
272
+
273
+ ```bash
274
+ git clone https://github.com/anshulraj10/leafdocs
275
+ cd leafdocs
276
+ python -m venv .venv
277
+ source .venv/bin/activate
278
+ pip install -e ".[dev]"
279
+ pytest tests/ -v
280
+ ```
281
+
282
+ ---
283
+
284
+ ## License
285
+
286
+ MIT
@@ -0,0 +1,35 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "leafdocs"
7
+ version = "0.1.0"
8
+ description = "Turn a directory of Markdown files into a self-hosted, searchable web reader."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Anshul", email = "you@example.com" }]
13
+ keywords = ["markdown", "docs", "flask", "self-hosted"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Framework :: Flask",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ ]
20
+ dependencies = [
21
+ "flask>=3.0",
22
+ "markdown>=3.5",
23
+ "python-frontmatter>=1.1",
24
+ "bcrypt>=4.1",
25
+ "python-dotenv>=1.0",
26
+ ]
27
+
28
+ [project.optional-dependencies]
29
+ dev = ["pytest", "pytest-flask"]
30
+
31
+ [tool.setuptools.packages.find]
32
+ where = ["src"]
33
+
34
+ [tool.setuptools.package-data]
35
+ "leafdocs" = ["templates/**", "static/**"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ from .app import LeafDocs
2
+
3
+ __all__ = ["LeafDocs"]