security improvements
This commit is contained in:
@@ -44,6 +44,22 @@ export interface SermonNote {
|
||||
updated_at?: string
|
||||
}
|
||||
|
||||
export interface Session {
|
||||
id?: number
|
||||
token: string
|
||||
username: string
|
||||
expires_at: string
|
||||
created_at?: string
|
||||
}
|
||||
|
||||
export interface RateLimit {
|
||||
id?: number
|
||||
identifier: string
|
||||
endpoint: string
|
||||
attempts: number
|
||||
reset_at: string
|
||||
}
|
||||
|
||||
export function getDatabase() {
|
||||
if (!db) {
|
||||
const dbPath = join(process.cwd(), 'data', 'sermons.db')
|
||||
@@ -102,6 +118,27 @@ export function getDatabase() {
|
||||
)
|
||||
`)
|
||||
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS sessions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
token TEXT UNIQUE NOT NULL,
|
||||
username TEXT NOT NULL,
|
||||
expires_at DATETIME NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
`)
|
||||
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS rate_limits (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
identifier TEXT NOT NULL,
|
||||
endpoint TEXT NOT NULL,
|
||||
attempts INTEGER DEFAULT 1,
|
||||
reset_at DATETIME NOT NULL,
|
||||
UNIQUE(identifier, endpoint)
|
||||
)
|
||||
`)
|
||||
|
||||
// Insert default admin user from environment variables with hashed password
|
||||
const config = useRuntimeConfig()
|
||||
const adminUsername = config.adminUsername
|
||||
@@ -249,3 +286,61 @@ export function deleteSermonNote(userId: number, sermonId: number) {
|
||||
const db = getDatabase()
|
||||
return db.prepare('DELETE FROM sermon_notes WHERE user_id = ? AND sermon_id = ?').run(userId, sermonId)
|
||||
}
|
||||
|
||||
// Session management functions
|
||||
export function createSession(token: string, username: string, expiresAt: string) {
|
||||
const db = getDatabase()
|
||||
return db.prepare('INSERT INTO sessions (token, username, expires_at) VALUES (?, ?, ?)')
|
||||
.run(token, username, expiresAt)
|
||||
}
|
||||
|
||||
export function getSessionByToken(token: string) {
|
||||
const db = getDatabase()
|
||||
return db.prepare("SELECT * FROM sessions WHERE token = ? AND expires_at > datetime('now')")
|
||||
.get(token) as Session | undefined
|
||||
}
|
||||
|
||||
export function deleteSession(token: string) {
|
||||
const db = getDatabase()
|
||||
return db.prepare('DELETE FROM sessions WHERE token = ?').run(token)
|
||||
}
|
||||
|
||||
export function deleteExpiredSessions() {
|
||||
const db = getDatabase()
|
||||
return db.prepare("DELETE FROM sessions WHERE expires_at <= datetime('now')").run()
|
||||
}
|
||||
|
||||
// Rate limiting functions
|
||||
export function checkRateLimit(identifier: string, endpoint: string, maxAttempts: number, windowMinutes: number): boolean {
|
||||
const db = getDatabase()
|
||||
|
||||
// Clean up expired rate limit records
|
||||
db.prepare("DELETE FROM rate_limits WHERE reset_at <= datetime('now')").run()
|
||||
|
||||
const existing = db.prepare('SELECT * FROM rate_limits WHERE identifier = ? AND endpoint = ?')
|
||||
.get(identifier, endpoint) as RateLimit | undefined
|
||||
|
||||
if (!existing) {
|
||||
// First attempt - create new record
|
||||
const resetAt = new Date(Date.now() + windowMinutes * 60 * 1000).toISOString()
|
||||
db.prepare('INSERT INTO rate_limits (identifier, endpoint, attempts, reset_at) VALUES (?, ?, 1, ?)')
|
||||
.run(identifier, endpoint, resetAt)
|
||||
return true
|
||||
}
|
||||
|
||||
if (existing.attempts >= maxAttempts) {
|
||||
// Rate limit exceeded
|
||||
return false
|
||||
}
|
||||
|
||||
// Increment attempts
|
||||
db.prepare('UPDATE rate_limits SET attempts = attempts + 1 WHERE identifier = ? AND endpoint = ?')
|
||||
.run(identifier, endpoint)
|
||||
return true
|
||||
}
|
||||
|
||||
export function resetRateLimit(identifier: string, endpoint: string) {
|
||||
const db = getDatabase()
|
||||
return db.prepare('DELETE FROM rate_limits WHERE identifier = ? AND endpoint = ?')
|
||||
.run(identifier, endpoint)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user