Skip to content

Commit 168c617

Browse files
author
Carl Chalmers
committed
feat: add FTP server support
- Added sparrow/ftp_server.py - Updated requirements.txt for FTP dependencies - Updated supervisord.conf to run FTP service - Updated Dockerfile to expose port 21 - Updated docker-compose.yml to map port 21 - Updated sparrow.env with FTP configuration variables
1 parent 45972af commit 168c617

File tree

6 files changed

+91
-2
lines changed

6 files changed

+91
-2
lines changed

docker-compose.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ services:
99
- ./sparrow.env
1010
ports:
1111
- "25:25" # SMTP server port
12+
- "21:21" # FTP server port
1213
volumes:
1314
- ./sparrow/config:/app/config
1415
- ./sparrow/images:/app/images

sparrow.env

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@ VEDIRECT_PORT=/dev/ttyUSB0
77
VEDIRECT_BAUD=19200
88
SERVER_BASE_URL=https://server.sparrow-earth.com
99
TZ=Etc/UTC
10-
ONLY_SAVE_ANIMALS=true
10+
ONLY_SAVE_ANIMALS=true
11+
FTP_USER=camera
12+
FTP_PASS=

sparrow/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,6 @@ RUN pip install --default-timeout=100 --no-cache-dir -r requirements.txt
3434
COPY . /app
3535

3636
EXPOSE 25
37+
EXPOSE 21
3738
COPY supervisord.conf /etc/supervisor/supervisord.conf
3839
CMD ["supervisord", "-c", "/etc/supervisor/supervisord.conf"]

sparrow/ftp_server.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
"""
2+
FTP server for camera feed: accepts FTP connections,
3+
authenticates a single user, and stores uploaded files in /app/images.
4+
"""
5+
6+
import os
7+
import logging
8+
from pyftpdlib.authorizers import DummyAuthorizer
9+
from pyftpdlib.handlers import FTPHandler
10+
from pyftpdlib.servers import FTPServer
11+
12+
# Configuration Paths
13+
SAVE_DIR = "/app/images"
14+
LOG_DIR = "/app/logs"
15+
os.makedirs(LOG_DIR, exist_ok=True)
16+
os.makedirs(SAVE_DIR, exist_ok=True)
17+
LOG_FILE = os.path.join(LOG_DIR, "ftp_server.log")
18+
19+
# Setup Logging & Folders
20+
logging.basicConfig(
21+
level=logging.INFO,
22+
format="%(asctime)s %(levelname)s %(message)s",
23+
handlers=[
24+
logging.FileHandler(LOG_FILE),
25+
logging.StreamHandler()
26+
]
27+
)
28+
logger = logging.getLogger(__name__)
29+
30+
FTP_USER = os.getenv("FTP_USER", "camera")
31+
FTP_PASS = os.getenv("FTP_PASS")
32+
FTP_PORT = 21
33+
34+
class CustomFTPHandler(FTPHandler):
35+
permit_foreign_addresses = True
36+
37+
def on_connect(self):
38+
logger.info(f"New connection from {self.remote_ip}:{self.remote_port}")
39+
40+
def on_disconnect(self):
41+
logger.info(f"Disconnected: {self.remote_ip}")
42+
43+
def on_login(self, username):
44+
logger.info(f"User logged in: {username} from {self.remote_ip}")
45+
46+
def on_login_failed(self, username, password):
47+
logger.warning(f"Failed login attempt: username={username} ip={self.remote_ip}")
48+
49+
def on_file_received(self, file_path):
50+
logger.info(f"File received and saved: {file_path}")
51+
52+
def on_incomplete_file_received(self, file_path):
53+
logger.warning(f"Incomplete file received (removed): {file_path}")
54+
55+
56+
def main():
57+
authorizer = DummyAuthorizer()
58+
authorizer.add_user(FTP_USER, FTP_PASS, SAVE_DIR, perm="elradfmw")
59+
60+
handler = CustomFTPHandler
61+
handler.authorizer = authorizer
62+
handler.banner = "Reolink FTP server ready."
63+
64+
address = ("0.0.0.0", FTP_PORT)
65+
server = FTPServer(address, handler)
66+
67+
# Connection limits
68+
server.max_cons = 256
69+
server.max_cons_per_ip = 10
70+
71+
logger.info(f"FTP server listening on 0.0.0.0:{FTP_PORT}, saving to {SAVE_DIR}")
72+
server.serve_forever()
73+
74+
75+
if __name__ == "__main__":
76+
main()

sparrow/requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,5 @@ audioread==3.0.1
2323
pooch==1.8.2
2424
librosa==0.10.2.post1
2525
numba==0.59.1
26-
llvmlite==0.42.0
26+
llvmlite==0.42.0
27+
pyftpdlib==1.5.9

sparrow/supervisord.conf

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
[supervisord]
22
nodaemon=true
33

4+
[program:ftp_server]
5+
command=python /app/ftp_server.py
6+
autostart=true
7+
autorestart=true
8+
priority=20
9+
stdout_logfile=/dev/null
10+
stderr_logfile=/dev/null
11+
412
[program:rtc_sync_once]
513
directory=/app/tools
614
command=python /app/tools/rtc_sync.py

0 commit comments

Comments
 (0)