FTP¶
FTP accounts per webapp.
All endpoints below are mounted under /api. Authentication: Authorization: Bearer <token> unless noted as public.
Endpoint count: 7
Endpoints¶
| Method | Path | Handler | Guards |
|---|---|---|---|
GET |
/api/ftp/accounts |
getFTPAccountsByUsername |
RequirePermission(db, "websites_management") |
POST |
/api/ftp/accounts |
createFTPAccountByUsername |
RequirePermission(db, "websites_management") |
DELETE |
/api/ftp/accounts/:id |
deleteFTPAccount |
RequirePermission(db, "websites_management") |
PATCH |
/api/ftp/accounts/:id |
updateFTPAccount |
RequirePermission(db, "websites_management") |
GET |
/api/ftp/status |
getFTPStatus |
RequirePermission(db, "websites_management") |
GET |
/api/ftp/webapps/:id/accounts |
getFTPAccounts |
RequirePermission(db, "websites_management") |
POST |
/api/ftp/webapps/:id/accounts |
createFTPAccount |
RequirePermission(db, "websites_management") |
Source¶
Handlers for this section live in api/routes/handlers_*.go in the StarkOne source. Each handler binds its request body with gin.ShouldBindJSON(&req) and responds with a flat (non-enveloped) JSON body - there is no { "data": ... } wrapper. List endpoints return { "accounts": [...] }, create endpoints return { "success": true, "username": ... }, and delete/update return { "success": true }. See each endpoint below for its exact shape.
On error:
Reference¶
GET /api/ftp/accounts¶
Lists FTP accounts for a site identified by its site username, by scanning the FTP password file for entries prefixed with <username>_.
Guards: RequirePermission(db, "websites_management")
Query parameters
| Name | Type | Optional | Description |
|---|---|---|---|
username |
string | no | Site username whose FTP accounts to list. Used to look up the webapp and to filter password-file entries by the <username>_ prefix. |
Response - 200 OK
This handler returns a flat (non-enveloped) body. home_dir is read from each per-user vsftpd config file's local_root= line. If the password file cannot be read, accounts is an empty array.
{
"accounts": [
{ "username": "user1_backup", "home_dir": "/home/user1/domains/example.com/public_html" }
]
}
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
username query parameter is missing. |
404 |
not_found |
No webapp exists with the given site username. |
Example
curl -X GET "https://panel.example.com:2087/api/ftp/accounts?username=user1" \
-H "Authorization: Bearer $TOKEN"
POST /api/ftp/accounts¶
Creates an FTP account for a site (identified by site_user in the body), hashing the password with openssl passwd -1, appending it to the FTP password file, and writing a per-user vsftpd config.
Guards: RequirePermission(db, "websites_management")
Request body
{
"username": "string (required)",
"password": "string (required)",
"home_dir": "string (optional)",
"site_user": "string (required)"
}
| Field | Type | Required | Description |
|---|---|---|---|
username |
string | yes | FTP sub-username. Must match ^[a-zA-Z0-9_]+$ (English letters, digits, underscore only). The stored account becomes <site_user>_<username>. |
password |
string | yes | Plaintext password; hashed via openssl passwd -1 before storage. |
home_dir |
string | no | Home directory. Absolute path (leading /) is used as-is; a relative path is resolved under /home/<site_user>/<home_dir>. If empty, defaults to /home/<site_user>/domains/<domain>/public_html. |
site_user |
string | yes | Site username used to look up the webapp domain and to prefix the FTP account name. |
Response - 200 OK
Flat (non-enveloped) body. username is the fully-qualified stored FTP username.
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Request body failed to bind (missing required field or invalid JSON). |
400 |
invalid_username |
username contains characters outside [a-zA-Z0-9_]. |
400 |
exists |
An FTP account with this name already exists in the password file. |
404 |
not_found |
No webapp exists for the given site_user. |
500 |
hash_error |
openssl passwd failed to produce a hash. |
500 |
file_error |
The FTP password file could not be opened for writing. |
Side effects
- Runs
openssl passwd -1 <password>to hash the password. - Appends
<ftpUser>:<hash>to/etc/starkone/ftp-passwords(mode 0600). - Creates
/etc/vsftpd/usersif missing and writes/etc/vsftpd/users/<ftpUser>withlocal_rootandguest_username.
Example
curl -X POST https://panel.example.com:2087/api/ftp/accounts \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"username":"backup","password":"s3cret","site_user":"user1","home_dir":"public_html"}'
DELETE /api/ftp/accounts/:id¶
Deletes an FTP account by removing its line from the password file and deleting its per-user vsftpd config.
Guards: RequirePermission(db, "websites_management")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
string | The fully-qualified FTP username (e.g. user1_backup). Despite the name, this is the username, not a numeric ID - storage is file-based. |
Response - 200 OK
Flat (non-enveloped) body.
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
The :id path segment is empty. |
404 |
not_found |
The password file does not exist, or no matching account line was found. |
Side effects
- Rewrites
/etc/starkone/ftp-passwords(mode 0600) with the matching line removed. - Removes
/etc/vsftpd/users/<username>.
Example
curl -X DELETE https://panel.example.com:2087/api/ftp/accounts/user1_backup \
-H "Authorization: Bearer $TOKEN"
PATCH /api/ftp/accounts/:id¶
Updates an FTP account's password and/or home directory; each field is applied only if non-empty.
Guards: RequirePermission(db, "websites_management")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
string | The fully-qualified FTP username (e.g. user1_backup). File-based storage, so this is the username, not a numeric ID. |
Request body
Both fields are optional. Neither carries binding:"required"; an empty body binds successfully and results in no changes.
| Field | Type | Required | Description |
|---|---|---|---|
password |
string | no | If non-empty, hashed via openssl passwd -1 and replaces the account's password-file line. |
home_dir |
string | no | If non-empty, sets a new local_root. Absolute paths used as-is; relative paths resolved under /home/<guest_username>/<home_dir>, where guest_username is read from the existing per-user config. |
Response - 200 OK
Flat (non-enveloped) body.
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
The :id path segment is empty, or request body failed to bind. |
404 |
not_found |
A password was supplied but no matching account line was found. |
500 |
hash_error |
openssl passwd failed to produce a hash. |
Side effects
- If
passwordset: runsopenssl passwd -1and rewrites/etc/starkone/ftp-passwords(mode 0600) with the updated line. - If
home_dirset: rewrites/etc/vsftpd/users/<username>(mode 0644) with the newlocal_root.
Example
curl -X PATCH https://panel.example.com:2087/api/ftp/accounts/user1_backup \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"password":"newpass","home_dir":"/home/user1/files"}'
GET /api/ftp/status¶
Reports whether the FTP plugin is enabled, whether the FTP service is running, and whether FTPS is configured.
Guards: RequirePermission(db, "websites_management")
Response - 200 OK
Flat (non-enveloped) body. enabled comes from the plugins table (id = 'ftp'). running is true if systemctl is-active reports active for vsftpd or proftpd. ftps is true if /etc/vsftpd.conf contains ssl_enable=YES.
Errors
None returned explicitly; the handler always responds 200.
Example
GET /api/ftp/webapps/:id/accounts¶
Lists FTP accounts for a webapp identified by its numeric ID, by scanning the password file for entries prefixed with the site username.
Guards: RequirePermission(db, "websites_management")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric webapp ID. Used to look up the site username, which prefixes the FTP account names to filter on. |
Response - 200 OK
Flat (non-enveloped) body. home_dir is read from each per-user vsftpd config's local_root= line. If the password file cannot be read, accounts is an empty array.
{
"accounts": [
{ "username": "user1_backup", "home_dir": "/home/user1/domains/example.com/public_html" }
]
}
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
The :id path segment is not a valid integer. |
404 |
not_found |
No webapp exists with the given ID. |
Example
curl -X GET https://panel.example.com:2087/api/ftp/webapps/42/accounts \
-H "Authorization: Bearer $TOKEN"
POST /api/ftp/webapps/:id/accounts¶
Creates an FTP account for a webapp identified by its numeric ID, hashing the password, appending it to the password file, and writing a per-user vsftpd config.
Guards: RequirePermission(db, "websites_management")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric webapp ID. Used to look up the site domain and username. |
Request body
{
"username": "string (required)",
"password": "string (required)",
"home_dir": "string (optional)"
}
| Field | Type | Required | Description |
|---|---|---|---|
username |
string | yes | FTP sub-username. Must match ^[a-zA-Z0-9_]+$. The stored account becomes <site_username>_<username>. |
password |
string | yes | Plaintext password; hashed via openssl passwd -1 before storage. |
home_dir |
string | no | Home directory. Absolute path used as-is; relative path resolved under /home/<site_username>/<home_dir>. If empty, defaults to /home/<site_username>/domains/<domain>/public_html. |
Response - 200 OK
Flat (non-enveloped) body. username is the fully-qualified stored FTP username.
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
The :id path segment is not a valid integer, or request body failed to bind. |
400 |
invalid_username |
username contains characters outside [a-zA-Z0-9_]. |
400 |
exists |
An FTP account with this name already exists in the password file. |
404 |
not_found |
No webapp exists with the given ID. |
500 |
hash_error |
openssl passwd failed to produce a hash. |
500 |
file_error |
The FTP password file could not be opened for writing. |
Side effects
- Runs
openssl passwd -1 <password>to hash the password. - Appends
<ftpUser>:<hash>to/etc/starkone/ftp-passwords(mode 0600). - Creates
/etc/vsftpd/usersif missing and writes/etc/vsftpd/users/<ftpUser>withlocal_rootandguest_username.
Example