# Example with database
This example shows you how to use the FTP server with the built-in SQLite database and Argon2 password hashing.
Database Support
Version 2.0+ uses SQLite exclusively via @db/sqlite for better performance and simpler deployment.
# Key Points
databaseoption: Configure the database directly inFTPServerOptionsserver.users: Access theUserRepositoryvia the server instanceUserRepository: Exported instantiable class for advanced management- Multi-tenant: Allows multiple servers with different databases (ideal for multi-tenant or testing)
# Import
Add the package to your project:
deno add @dftp/server
Import the server, user repository and Argon2:
import { Server, UserRepository } from "@dftp/server";
import { hash, verify } from "@node-rs/argon2";
# Initialize server with database
Pass the database option directly in the server options:
// Create server with its own database
const server = new Server({
port: 21,
database: { connector: "SQLite", filepath: "./users.db" }
});
// Access UserRepository via server.users
server.users.create({
username: "admin",
password: await hash("secret"),
root: "/srv/ftp",
uid: 1000,
gid: 1000,
});
Multi-tenant and Testing
Each Server instance can have its own database, allowing you to run multiple isolated FTP servers (multi-tenant) or create independent test environments.
# Handle connections
Authenticate users against the database:
for await (const conn of server) {
conn.on("login", async ({ username, password }, resolve, reject) => {
const user = server.users.findByUsername(username);
if (user && await verify(user.password, password)) {
resolve({ root: user.root, uid: user.uid, gid: user.gid });
} else {
reject();
}
});
}
# Advanced usage: Standalone UserRepository
For advanced management, you can instantiate UserRepository directly:
import { UserRepository } from "@dftp/server";
// Create an independent repository
const repo = new UserRepository({ connector: "SQLite", filepath: "./custom.db" });
// Manage users
repo.create({ username: "user1", password: await hash("pass"), root: "/data", uid: 1001, gid: 1001 });
const user = repo.findByUsername("user1");
# Full example
import { Server, UserRepository } from "@dftp/server";
import { hash, verify } from "@node-rs/argon2";
// Create server with its own database
const server = new Server({
port: 21,
database: { connector: "SQLite", filepath: "./users.db" }
});
// Create a user (run once)
server.users.create({
username: "admin",
password: await hash("secret"),
root: "/srv/ftp",
uid: 1000,
gid: 1000,
});
// Start server
console.log("FTP server listening on port 21");
for await (const conn of server) {
conn.on("login", async ({ username, password }, resolve, reject) => {
const user = server.users.findByUsername(username);
if (user && await verify(user.password, password)) {
resolve({ root: user.root, uid: user.uid, gid: user.gid });
} else {
reject();
}
});
}
# Multi-tenant example
import { Server } from "@dftp/server";
import { hash, verify } from "@node-rs/argon2";
// Tenant A server
const serverA = new Server({
port: 2121,
database: { connector: "SQLite", filepath: "./tenant-a.db" }
});
// Tenant B server
const serverB = new Server({
port: 2122,
database: { connector: "SQLite", filepath: "./tenant-b.db" }
});
// Each server has its own isolated UserRepository
serverA.users.create({ username: "admin", password: await hash("a"), root: "/a", uid: 1000, gid: 1000 });
serverB.users.create({ username: "admin", password: await hash("b"), root: "/b", uid: 1000, gid: 1000 });