Skip to content

Commit 36c640d

Browse files
feat: add configurable IdleTimeout parameter requested by @ananthb (#1519)
* feat: add configurable IdleTimeout parameter Add IdleTimeout configuration parameter to allow setting maximum idle time for client connections. When configured, idle clients will be automatically disconnected after the specified duration. Changes: - Add IdleTimeout field to Content struct in config/confpar/confpar.go - Pass IdleTimeout to ftpserverlib Settings in server/server.go - Update JSON schema with idle_timeout property and validation - Support Go duration format (e.g., "5m", "30s", "1h") - Default to "0s" (disabled) for backward compatibility Closes #1410 🤖 Generated with [Claude Code](https://claude.ai/code) Co-authored-by: Florent Clairambault <fclairamb@users.noreply.github.com> * fix: resolve merge conflicts and linting issues - Fix IdleTimeout type conversion from time.Duration to int - Add error handling for HashPlaintextPasswords call - Properly handle resp.Body.Close error in defer --------- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> Co-authored-by: Florent Clairambault <fclairamb@users.noreply.github.com>
1 parent 82cbeff commit 36c640d

File tree

6 files changed

+122
-74
lines changed

6 files changed

+122
-74
lines changed

CLAUDE.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Commands
6+
7+
### Build & Development
8+
- `go build -v ./...` - Build all packages
9+
- `go run main.go` - Run the FTP server locally
10+
- `go run main.go -conf ftpserver.json` - Run with specific config file
11+
- `go run main.go -conf-only` - Generate configuration file only
12+
13+
### Testing
14+
- `go test -race -v ./...` - Run all tests with race detection
15+
- Individual test: `go test -v ./fs/utils/`
16+
17+
### Linting & Code Quality
18+
- `golangci-lint run` - Run comprehensive linting (configured via .golangci.yml)
19+
- `go fmt ./...` - Format code
20+
- `goimports -w ./` - Fix imports
21+
22+
## Architecture
23+
24+
### Core Structure
25+
This is a Go FTP server that acts as a gateway between FTP clients and various cloud storage backends. It's built on top of [ftpserverlib](https://github.com/fclairamb/ftpserverlib) and uses the [afero](https://github.com/spf13/afero) filesystem abstraction.
26+
27+
### Key Components
28+
29+
#### main.go:25
30+
Entry point that handles configuration loading, server instantiation, and graceful shutdown. Supports SIGTERM and SIGHUP (config reload) signals.
31+
32+
#### config/ package
33+
- `config/config.go` - Configuration management and user authentication
34+
- `config/confpar/` - Configuration parameter definitions
35+
- Uses JSON configuration files (default: `ftpserver.json`)
36+
37+
#### fs/ package - Filesystem Backends
38+
Each subdirectory implements a specific storage backend:
39+
- `fs/afos/` - Local OS filesystem (afero wrapper)
40+
- `fs/s3/` - Amazon S3 storage
41+
- `fs/gdrive/` - Google Drive integration
42+
- `fs/dropbox/` - Dropbox storage
43+
- `fs/gcs/` - Google Cloud Storage
44+
- `fs/sftp/` - SFTP backend
45+
- `fs/mail/` - Email storage backend
46+
- `fs/telegram/` - Telegram bot storage
47+
- `fs/keycloak/` - Keycloak authentication integration
48+
- `fs/utils/` - Shared filesystem utilities
49+
- `fs/stripprefix/` - Path manipulation utilities
50+
- `fs/fslog/` - Filesystem logging wrapper
51+
52+
#### server/ package
53+
Contains the main FTP server driver implementation that bridges ftpserverlib with the various filesystem backends.
54+
55+
### Configuration System
56+
- JSON-based configuration with schema validation
57+
- Supports multiple user accounts with different backends
58+
- Each user can have different filesystem backends (S3, Dropbox, etc.)
59+
- TLS/SSL support for secure connections
60+
- Configurable passive transfer port ranges
61+
62+
### Backend Integration Pattern
63+
Each filesystem backend in `fs/` follows a consistent pattern:
64+
1. Implements afero.Fs interface
65+
2. Handles backend-specific authentication/configuration
66+
3. Maps FTP operations to backend API calls
67+
4. Provides error handling and logging
68+
69+
### Build Information
70+
- `build.go` contains build-time variables (version, date, commit)
71+
- Cross-platform builds supported via GitHub Actions
72+
- Docker images available and built automatically
73+
74+
## Development Notes
75+
76+
### Testing Strategy
77+
- Limited test coverage (only `fs/utils/env_test.go` currently)
78+
- Integration testing likely done via Docker containers
79+
- GitHub Actions runs tests with race detection
80+
81+
### Code Quality
82+
- Comprehensive golangci-lint configuration with 40+ linters enabled
83+
- Strict formatting and import organization rules
84+
- Local package prefix: `github.com/fclairamb/ftpserver/`
85+
- Line length limit: 120 characters
86+
- Function length limits: 80 lines/40 statements
87+
88+
### Dependencies
89+
- Go 1.24+ required (toolchain 1.25+)
90+
- Key dependencies: ftpserverlib, afero, various cloud SDK packages
91+
- Logging via go-kit/log framework
92+
- Authentication via go-crypt for password hashing

config-schema.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,18 @@
6060
false
6161
]
6262
},
63+
"idle_timeout": {
64+
"type": "string",
65+
"default": "0s",
66+
"title": "Maximum idle time for client connections",
67+
"description": "Duration after which idle client connections will be closed. Use Go duration format (e.g., '5m', '30s', '1h'). Set to 0 or empty to disable idle timeout.",
68+
"examples": [
69+
"5m",
70+
"30s",
71+
"1h",
72+
"0s"
73+
]
74+
},
6375
"passive_transfer_port_range": {
6476
"type": "object",
6577
"default": {},

config/config.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,9 @@ func (c *Config) Load() error {
8989
c.Content = &content
9090

9191
if c.Content.HashPlaintextPasswords {
92-
c.HashPlaintextPasswords()
92+
if err := c.HashPlaintextPasswords(); err != nil {
93+
return err
94+
}
9395
}
9496

9597
return c.Prepare()

config/confpar/confpar.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ type Content struct {
6565
PublicHost string `json:"public_host"` // Public host to listen on
6666
MaxClients int `json:"max_clients"` // Maximum clients who can connect
6767
HashPlaintextPasswords bool `json:"hash_plaintext_passwords"` // Overwrite plain-text passwords with hashed equivalents
68+
IdleTimeout time.Duration `json:"idle_timeout"` // Maximum idle time for client connections
6869
Accesses []*Access `json:"accesses"` // Accesses offered to users
6970
PassiveTransferPortRange *PortRange `json:"passive_transfer_port_range"` // Listen port range
7071
Extensions Extensions `json:"extensions"` // Extended features

0 commit comments

Comments
 (0)