Add auth service, storage service, types, and logging middleware

- Auth service: hashPassword/verifyPassword via bcrypt
- Storage service: saveFile, getFilePath, deleteStoredFile with ENOENT handling
- Types: JwtPayload interface
- Logging middleware: createLogger writing AUTH_FAILURE, AUTH_SUCCESS, FILE_NOT_FOUND
- 27 tests passing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-03 15:49:22 -08:00
parent b6aa6211a9
commit 157d1e8230
6 changed files with 178 additions and 0 deletions

40
src/middleware/logging.ts Normal file
View File

@@ -0,0 +1,40 @@
import { appendFile } from 'fs/promises';
interface AuthLogParams {
ip: string;
userAgent: string;
username: string;
}
interface FileNotFoundParams {
ip: string;
userAgent: string;
fileId: string;
}
export interface Logger {
authSuccess(params: AuthLogParams): Promise<void>;
authFailure(params: AuthLogParams): Promise<void>;
fileNotFound(params: FileNotFoundParams): Promise<void>;
}
export function createLogger(logFile: string): Logger {
function timestamp(): string {
return new Date().toISOString();
}
async function write(line: string): Promise<void> {
await appendFile(logFile, line + '\n');
}
function authLine(event: string, { ip, userAgent, username }: AuthLogParams): string {
return `[${timestamp()}] ${event} ip=${ip} user-agent="${userAgent}" username="${username}"`;
}
return {
authSuccess: (params) => write(authLine('AUTH_SUCCESS', params)),
authFailure: (params) => write(authLine('AUTH_FAILURE', params)),
fileNotFound: ({ ip, userAgent, fileId }) =>
write(`[${timestamp()}] FILE_NOT_FOUND ip=${ip} user-agent="${userAgent}" file_id="${fileId}"`),
};
}