sharedserver 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,57 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+
5
+ from fastapi import WebSocket, WebSocketDisconnect
6
+ from pydantic import ValidationError
7
+
8
+ from .models import ClipboardUpdate
9
+
10
+
11
+ class ClipboardHub:
12
+ def __init__(self) -> None:
13
+ self.content = ""
14
+ self.clients: set[WebSocket] = set()
15
+ self.lock = asyncio.Lock()
16
+
17
+ async def connect(self, websocket: WebSocket) -> None:
18
+ await websocket.accept()
19
+ async with self.lock:
20
+ self.clients.add(websocket)
21
+ content = self.content
22
+ await websocket.send_json({"type": "clipboard", "content": content})
23
+
24
+ async def disconnect(self, websocket: WebSocket) -> None:
25
+ async with self.lock:
26
+ self.clients.discard(websocket)
27
+
28
+ async def broadcast(self, content: str) -> None:
29
+ async with self.lock:
30
+ self.content = content
31
+ clients = tuple(self.clients)
32
+ failed: list[WebSocket] = []
33
+ for client in clients:
34
+ try:
35
+ await client.send_json({"type": "clipboard", "content": content})
36
+ except (RuntimeError, WebSocketDisconnect):
37
+ failed.append(client)
38
+ if failed:
39
+ async with self.lock:
40
+ self.clients.difference_update(failed)
41
+
42
+ async def handle(self, websocket: WebSocket) -> None:
43
+ await self.connect(websocket)
44
+ try:
45
+ while True:
46
+ try:
47
+ message = ClipboardUpdate.model_validate(await websocket.receive_json())
48
+ except (ValidationError, ValueError):
49
+ await websocket.send_json(
50
+ {"type": "error", "message": "invalid clipboard message"}
51
+ )
52
+ continue
53
+ await self.broadcast(message.content)
54
+ except WebSocketDisconnect:
55
+ pass
56
+ finally:
57
+ await self.disconnect(websocket)
@@ -0,0 +1,177 @@
1
+ Metadata-Version: 2.4
2
+ Name: sharedserver
3
+ Version: 0.1.0
4
+ Summary: Zero-configuration LAN file sharing and real-time text clipboard
5
+ Author: Kyi Wong
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/NihaoKangkang/sharedserver
8
+ Project-URL: Repository, https://github.com/NihaoKangkang/sharedserver
9
+ Project-URL: Issues, https://github.com/NihaoKangkang/sharedserver/issues
10
+ Keywords: file-sharing,clipboard,lan,fastapi,websocket
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Web Environment
13
+ Classifier: Framework :: FastAPI
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: fastapi<1,>=0.115
25
+ Requires-Dist: PyYAML<7,>=6
26
+ Requires-Dist: uvicorn<1,>=0.30
27
+ Requires-Dist: websockets<17,>=13
28
+ Provides-Extra: dev
29
+ Requires-Dist: build<2,>=1.2; extra == "dev"
30
+ Requires-Dist: httpx2<3,>=2; extra == "dev"
31
+ Requires-Dist: pytest<9,>=8; extra == "dev"
32
+ Requires-Dist: twine<7,>=5; extra == "dev"
33
+ Dynamic: license-file
34
+
35
+ # SharedServer
36
+
37
+ SharedServer is a zero-configuration, login-free LAN file server and real-time text clipboard. It is as easy to start as `python -m http.server`, while adding uploads, directory browsing, live text synchronization, and allowlisted clipboard actions.
38
+
39
+ > SharedServer has no authentication. Run it only on a trusted home, lab, or development network. Never expose it directly to the public internet.
40
+
41
+ ## Features
42
+
43
+ - A shared text clipboard synchronized between all open pages over WebSocket
44
+ - A responsive file browser with directory navigation, file sizes, and modification times
45
+ - File uploads from desktop and mobile browsers with live progress
46
+ - Folder creation and safe, non-overwriting uploads
47
+ - Copy actions for allowlisted text and image formats
48
+ - Download-only behavior for other formats such as ZIP, RAR, 7z, EXE, APK, and ISO
49
+ - Protection against path traversal, directory escape, symlink escape, and unsafe filenames
50
+ - No accounts, database, device discovery, template engine, or frontend framework
51
+
52
+ ## Requirements
53
+
54
+ - Python 3.10 or newer
55
+ - A modern desktop or mobile browser
56
+
57
+ ## Installation
58
+
59
+ Install from PyPI:
60
+
61
+ ```bash
62
+ pip install sharedserver
63
+ ```
64
+
65
+ Install from source:
66
+
67
+ ```bash
68
+ git clone https://github.com/NihaoKangkang/sharedserver.git
69
+ cd sharedserver
70
+ pip install .
71
+ ```
72
+
73
+ ## Quick start
74
+
75
+ Run this command from the directory you want to share:
76
+
77
+ ```bash
78
+ python -m sharedserver 8080
79
+ ```
80
+
81
+
82
+ ## CLI reference
83
+
84
+ ```text
85
+ usage: sharedserver [-h] [--host HOST] [-d DIRECTORY] [-c CONFIG]
86
+ [--max-upload-size MB] [--version]
87
+ [port]
88
+ ```
89
+
90
+ | Argument | Description |
91
+ | --- | --- |
92
+ | `port` | Listening port. Uses the configuration value or `8000` by default. |
93
+ | `--host HOST` | Bind address. Defaults to `0.0.0.0`. |
94
+ | `-d, --directory PATH` | Directory to share. Defaults to the current directory. |
95
+ | `-c, --config PATH` | YAML configuration file. |
96
+ | `--max-upload-size MB` | Maximum upload size in MiB. Defaults to 1024 MiB. |
97
+ | `--version` | Print the installed version. |
98
+
99
+ CLI arguments override configuration file values. Without `--config`, SharedServer automatically loads `sharedserver.yaml` from the current directory when it exists.
100
+
101
+ ## Configuration
102
+
103
+ Copy `sharedserver.example.yaml` to `sharedserver.yaml` and edit it as needed:
104
+
105
+ ```yaml
106
+ server:
107
+ host: 0.0.0.0
108
+ port: 8080
109
+
110
+ share:
111
+ directory: ./
112
+ max_upload_size: 1073741824
113
+
114
+ clipboard:
115
+ max_text_size: 2097152
116
+ text_extensions:
117
+ - .txt
118
+ - .md
119
+ image_extensions:
120
+ - .png
121
+ - .jpg
122
+ ```
123
+
124
+ Relative shared directory paths are resolved from the configuration file's directory. Custom extension lists replace the defaults, and matching is case-insensitive.
125
+
126
+ Default text extensions:
127
+
128
+ ```text
129
+ .txt .md .json .xml .yaml .yml .csv .log .py .js .html .css .java .c .cpp
130
+ ```
131
+
132
+ Default image extensions:
133
+
134
+ ```text
135
+ .png .jpg .jpeg .webp
136
+ ```
137
+
138
+ ## Security model
139
+
140
+ - Every user-supplied path is resolved with `Path.resolve()` and checked against the shared root.
141
+ - Absolute paths and `..` path segments are rejected.
142
+ - Symlinks are hidden from directory listings and rejected by file operations.
143
+ - Upload filenames are stripped of path components, control characters, and unsafe cross-platform characters.
144
+ - Uploads are written to a temporary file and published without overwriting an existing file.
145
+ - Both `Content-Length` validation and streamed byte counting enforce the upload limit.
146
+ - Clipboard actions and server read endpoints use the same strict extension allowlists.
147
+ - Browser WebSocket connections from a different origin are rejected.
148
+ - Shared text exists only in process memory and is cleared when the server stops.
149
+
150
+ SharedServer intentionally has no login or authorization layer. Every device that can reach the server can read and write the shared directory and shared text.
151
+
152
+ ## Development
153
+
154
+ Clone the project and create a virtual environment:
155
+
156
+ ```bash
157
+ git clone https://github.com/NihaoKangkang/sharedserver.git
158
+ cd sharedserver
159
+ python -m venv .venv
160
+ ```
161
+
162
+ Activate the environment, then install development dependencies:
163
+
164
+ ```bash
165
+ pip install -e ".[dev]"
166
+ ```
167
+
168
+ Start a development server:
169
+
170
+ ```bash
171
+ python -m sharedserver 8080 --directory .
172
+ ```
173
+
174
+
175
+ ## License
176
+
177
+ [MIT](LICENSE)
@@ -0,0 +1,19 @@
1
+ sharedserver/__init__.py,sha256=fcXQwomL1kmmlLnKRSx_v3eeMDHiskXHGWOdqrgPpBA,52
2
+ sharedserver/__main__.py,sha256=14FfnaF7zY550dRxcOV5XinMWDf5fRBJZkgTBoqeVj8,63
3
+ sharedserver/cli.py,sha256=InIP39ACgt_YL9xjHHqCStjqJdqT8NSbbNuCq0HbVg8,2447
4
+ sharedserver/clipboard.py,sha256=y_Kn30yJnJbhNL1isybDLiKv_6XdZ5Xh__vjIrbxkXY,1102
5
+ sharedserver/config.py,sha256=pPMbLFuSiQhT5mL52ro8rxhKPKdSguFDumVdcWIWF-M,5018
6
+ sharedserver/file_manager.py,sha256=wAKIPy-yY1iQaBTbEQsqUIrDrBEK8Ca5bhV-K8S1T8U,5049
7
+ sharedserver/models.py,sha256=8UNpOFRBFUsZKTEcPD5qN5w_xX9BFlZLHHyGovlTsbA,725
8
+ sharedserver/security.py,sha256=qrH-wiST8rOQLAJY4S3zU12wlWk5SNnWRryXnnZXbB0,1734
9
+ sharedserver/server.py,sha256=rO-9Pw2dxOmCN7A1OhVXtHxzYELOTM7NKKuBaGrZlTA,5902
10
+ sharedserver/websocket.py,sha256=o7G9ZWFYGncxN9cpSWP0mqC8SOohSCugkvQe-Tzs3Xg,1930
11
+ sharedserver/static/app.css,sha256=Ll-940sgztrniR-1HUxzcaLMqjDdc6pftzJd78q3uPs,9186
12
+ sharedserver/static/app.js,sha256=6e4nUsZPPjJhihUz4X4Cq-OKb_dmlk9Gy5Ew7IlYQZI,12818
13
+ sharedserver/templates/index.html,sha256=5rG2p52LH2tuulrhjOtz8gbfhuof0TOfdha1mJYf_uw,3380
14
+ sharedserver-0.1.0.dist-info/licenses/LICENSE,sha256=inYxby017tQkLDQchOc_uUz2nDVLJSY___IjpZcGid4,1066
15
+ sharedserver-0.1.0.dist-info/METADATA,sha256=WDcyZRyLVmtJ6Z89kke03G7eAbgm3f0LnX-0Lmw5Huc,5553
16
+ sharedserver-0.1.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
17
+ sharedserver-0.1.0.dist-info/entry_points.txt,sha256=rsNs9N4tdGREmL84F9dD50h-8eUBjD9L_MAsJzmtY6c,55
18
+ sharedserver-0.1.0.dist-info/top_level.txt,sha256=TRb9rewpeC-6OHWWPSBGa-b2CrV3hMmusWV-KhpRgX0,13
19
+ sharedserver-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (83.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ sharedserver = sharedserver.cli:main
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kyi Wong
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.
22
+
@@ -0,0 +1 @@
1
+ sharedserver