Fix masked passwords being sent to test endpoints and saved to DB

The settings GET endpoint masks emailImapPass and ftpPass for security.
The test endpoints and save handler were using these masked values, causing
IMAP/FTP test failures and potentially overwriting real passwords. Now the
test endpoints fall back to the DB-stored password when they receive the
masked value, and the save handler strips masked passwords from the payload.

Made-with: Cursor
This commit is contained in:
Randall Stillwell 2026-04-17 14:37:55 -05:00
parent d0844ecadd
commit 58d8d99625
3 changed files with 25 additions and 3 deletions

View file

@ -120,10 +120,14 @@ export default function UploadSourcesPage() {
const handleSave = async () => {
setSaving(true);
try {
const payload = { ...settings } as Record<string, unknown>;
if (payload.emailImapPass === "••••••••") delete payload.emailImapPass;
if (payload.ftpPass === "••••••••") delete payload.ftpPass;
const res = await fetch("/api/settings", {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(settings),
body: JSON.stringify(payload),
});
if (!res.ok) throw new Error();
toast.success("Upload source settings saved");

View file

@ -1,6 +1,9 @@
import { NextRequest, NextResponse } from "next/server";
import { testEmailConnection } from "@/lib/email-watcher";
import { requireApiAuth, handleApiError } from "@/lib/api-auth";
import { prisma } from "@/lib/db";
const MASKED = "••••••••";
export async function POST(request: NextRequest) {
try {
@ -10,9 +13,14 @@ export async function POST(request: NextRequest) {
const host = String(body.host || "");
const port = parseInt(String(body.port)) || 993;
const user = String(body.user || "");
const pass = String(body.pass || "");
let pass = String(body.pass || "");
const tls = body.tls !== false;
if (!pass || pass === MASKED) {
const settings = await prisma.appSettings.findUnique({ where: { id: "singleton" } });
pass = settings?.emailImapPass || "";
}
if (!host || !user || !pass) {
return NextResponse.json(
{ error: "Host, username, and password are required" },

View file

@ -1,16 +1,26 @@
import { NextRequest, NextResponse } from "next/server";
import { testFtpConnection } from "@/lib/ftp-watcher";
import { requireApiAuth, handleApiError } from "@/lib/api-auth";
import { prisma } from "@/lib/db";
const MASKED = "••••••••";
export async function POST(request: NextRequest) {
try {
await requireApiAuth();
const body = await request.json();
let pass = body.pass || "";
if (!pass || pass === MASKED) {
const settings = await prisma.appSettings.findUnique({ where: { id: "singleton" } });
pass = settings?.ftpPass || "";
}
const result = await testFtpConnection({
host: body.host,
port: body.port || 21,
user: body.user,
pass: body.pass,
pass,
tls: body.tls ?? true,
incomingDir: body.incomingDir || "/incoming",
});