ic-web/server/database.lua

236 lines
9.6 KiB
Lua
Raw Permalink Normal View History

-- ─────────────────────────────────────────────────────────────────────────────
-- ic-web Datenzugriff
-- Saemtliches SQL steht hier. Der uebrige Code kennt keine Tabellennamen.
-- ─────────────────────────────────────────────────────────────────────────────
ICWeb = ICWeb or {}
ICWeb.DB = {}
local DB = ICWeb.DB
-- TINYINT(1) kommt je nach Treiberfassung als 1, als true oder als "1" an.
-- Ein Vergleich mit == 1 geht deshalb frueher oder spaeter schief und zwar
-- still: aus "aktiv" wird "gesperrt", ohne Fehlermeldung. Deshalb laeuft jede
-- Ja/Nein-Spalte aus der Datenbank durch diese Funktion.
function ICWeb.Bool(value)
if value == nil or value == false then return false end
if value == true then return true end
if type(value) == 'number' then return value ~= 0 end
if type(value) == 'string' then
return value ~= '0' and value ~= '' and value:lower() ~= 'false'
end
return true
end
-- ── Sites ───────────────────────────────────────────────────────────────────
function DB.GetSiteByDomain(domain)
return MySQL.single.await('SELECT * FROM ic_web_sites WHERE domain = ?', { domain })
end
function DB.GetSiteById(id)
return MySQL.single.await('SELECT * FROM ic_web_sites WHERE id = ?', { id })
end
function DB.GetSitesForOwner(ownerType, ownerId)
return MySQL.query.await(
'SELECT * FROM ic_web_sites WHERE owner_type = ? AND owner_id = ? ORDER BY domain',
{ ownerType, ownerId }) or {}
end
function DB.CountSitesForOwner(ownerType, ownerId)
local row = MySQL.single.await(
'SELECT COUNT(*) AS n FROM ic_web_sites WHERE owner_type = ? AND owner_id = ?',
{ ownerType, ownerId })
return (row and tonumber(row.n)) or 0
end
function DB.GetAllSites()
return MySQL.query.await('SELECT * FROM ic_web_sites ORDER BY domain') or {}
end
function DB.SetOwner(id, ownerType, ownerId)
return MySQL.update.await(
'UPDATE ic_web_sites SET owner_type = ?, owner_id = ? WHERE id = ?',
{ ownerType, ownerId, id })
end
function DB.CreateSite(data)
return MySQL.insert.await(
[[INSERT INTO ic_web_sites (domain, title, theme, owner_type, owner_id, published, created_by)
VALUES (?,?,?,?,?,?,?)]],
{ data.domain, data.title, data.theme, data.owner_type, data.owner_id,
data.published and 1 or 0, data.created_by })
end
function DB.UpdateSite(id, data)
return MySQL.update.await(
'UPDATE ic_web_sites SET title = ?, theme = ?, published = ? WHERE id = ?',
{ data.title, data.theme, data.published and 1 or 0, id })
end
function DB.SetBlocked(id, blocked)
return MySQL.update.await('UPDATE ic_web_sites SET blocked = ? WHERE id = ?',
{ blocked and 1 or 0, id })
end
function DB.DeleteSite(id)
return MySQL.update.await('DELETE FROM ic_web_sites WHERE id = ?', { id })
end
-- ── Seiten ──────────────────────────────────────────────────────────────────
function DB.GetPages(siteId)
return MySQL.query.await(
'SELECT * FROM ic_web_pages WHERE site_id = ? ORDER BY sort_order, id', { siteId }) or {}
end
function DB.GetPage(siteId, slug)
return MySQL.single.await(
'SELECT * FROM ic_web_pages WHERE site_id = ? AND slug = ?', { siteId, slug })
end
function DB.CountPages(siteId)
local row = MySQL.single.await(
'SELECT COUNT(*) AS n FROM ic_web_pages WHERE site_id = ?', { siteId })
return (row and tonumber(row.n)) or 0
end
-- Anlegen oder ueberschreiben. Der eindeutige Schluessel ist (site_id, slug).
function DB.SavePage(siteId, slug, title, blocksJson, sortOrder)
return MySQL.update.await(
[[INSERT INTO ic_web_pages (site_id, slug, title, blocks, sort_order)
VALUES (?,?,?,?,?)
ON DUPLICATE KEY UPDATE title = VALUES(title), blocks = VALUES(blocks),
sort_order = VALUES(sort_order)]],
{ siteId, slug, title, blocksJson, sortOrder or 0 })
end
function DB.DeletePage(siteId, slug)
return MySQL.update.await(
'DELETE FROM ic_web_pages WHERE site_id = ? AND slug = ?', { siteId, slug })
end
-- ── Zugaenge ────────────────────────────────────────────────────────────────
-- Das Passwort erscheint hier nur als Abfrageparameter. Gehasht wird in der
-- Datenbank, damit weder Klartext noch Hash in einer Lua-Variable landen.
local ACCOUNT_FIELDS =
'id, username, domain, role, display_name, active, must_change, created_by, created_at, last_login'
function DB.GetAccountByUsername(username)
return MySQL.single.await(
'SELECT ' .. ACCOUNT_FIELDS .. ' FROM ic_web_accounts WHERE username = ?', { username })
end
function DB.GetAccountById(id)
return MySQL.single.await(
'SELECT ' .. ACCOUNT_FIELDS .. ' FROM ic_web_accounts WHERE id = ?', { id })
end
function DB.GetAccountsForDomain(domain)
return MySQL.query.await(
'SELECT ' .. ACCOUNT_FIELDS .. ' FROM ic_web_accounts WHERE domain = ? ORDER BY username',
{ domain }) or {}
end
function DB.CountAccountsForDomain(domain)
local row = MySQL.single.await(
'SELECT COUNT(*) AS n FROM ic_web_accounts WHERE domain = ?', { domain })
return (row and tonumber(row.n)) or 0
end
function DB.GetAllAccounts()
return MySQL.query.await(
'SELECT ' .. ACCOUNT_FIELDS .. ' FROM ic_web_accounts ORDER BY domain, username') or {}
end
function DB.CreateAccount(data)
return MySQL.insert.await(
[[INSERT INTO ic_web_accounts
(username, domain, role, display_name, salt, password_hash, must_change, created_by)
VALUES (?,?,?,?,?, SHA2(CONCAT(?, ?), 256), ?, ?)]],
{ data.username, data.domain, data.role, data.display_name,
data.salt, data.salt, data.password,
data.must_change and 1 or 0, data.created_by })
end
-- Gibt den Zugang zurueck, wenn das Passwort stimmt, sonst nil.
function DB.CheckPassword(username, password)
return MySQL.single.await(
'SELECT ' .. ACCOUNT_FIELDS .. ' FROM ic_web_accounts'
.. ' WHERE username = ? AND password_hash = SHA2(CONCAT(salt, ?), 256)',
{ username, password })
end
function DB.SetPassword(id, salt, password)
return MySQL.update.await(
'UPDATE ic_web_accounts SET salt = ?, password_hash = SHA2(CONCAT(?, ?), 256),'
.. ' must_change = 0 WHERE id = ?',
{ salt, salt, password, id })
end
function DB.SetAccountActive(id, active)
return MySQL.update.await('UPDATE ic_web_accounts SET active = ? WHERE id = ?',
{ active and 1 or 0, id })
end
function DB.SetAccountRole(id, role)
return MySQL.update.await('UPDATE ic_web_accounts SET role = ? WHERE id = ?', { role, id })
end
function DB.DeleteAccount(id)
return MySQL.update.await('DELETE FROM ic_web_accounts WHERE id = ?', { id })
end
function DB.TouchLogin(id)
return MySQL.update.await('UPDATE ic_web_accounts SET last_login = NOW() WHERE id = ?', { id })
end
-- ── Fehlversuche ────────────────────────────────────────────────────────────
function DB.AddLoginFail(username, identifier)
return MySQL.insert.await(
'INSERT INTO ic_web_login_fails (username, identifier) VALUES (?,?)',
{ username, identifier })
end
function DB.CountLoginFails(username, withinSeconds)
local row = MySQL.single.await(
[[SELECT COUNT(*) AS n FROM ic_web_login_fails
WHERE username = ? AND created_at > DATE_SUB(NOW(), INTERVAL ? SECOND)]],
{ username, withinSeconds })
return (row and tonumber(row.n)) or 0
end
function DB.ClearLoginFails(username)
return MySQL.update.await('DELETE FROM ic_web_login_fails WHERE username = ?', { username })
end
function DB.PruneLoginFails(olderThanSeconds)
return MySQL.update.await(
'DELETE FROM ic_web_login_fails WHERE created_at < DATE_SUB(NOW(), INTERVAL ? SECOND)',
{ olderThanSeconds })
end
-- ── Meldungen ───────────────────────────────────────────────────────────────
function DB.AddReport(siteId, pageSlug, reason, reportedBy)
return MySQL.insert.await(
'INSERT INTO ic_web_reports (site_id, page_slug, reason, reported_by) VALUES (?,?,?,?)',
{ siteId, pageSlug, reason, reportedBy })
end
function DB.RecentReportBy(siteId, identifier, withinSeconds)
return MySQL.single.await(
[[SELECT id FROM ic_web_reports
WHERE site_id = ? AND reported_by = ?
AND created_at > DATE_SUB(NOW(), INTERVAL ? SECOND) LIMIT 1]],
{ siteId, identifier, withinSeconds })
end
function DB.GetOpenReports()
return MySQL.query.await(
[[SELECT r.*, s.domain FROM ic_web_reports r
JOIN ic_web_sites s ON s.id = r.site_id
WHERE r.status = 'open' ORDER BY r.created_at DESC LIMIT 100]]) or {}
end
function DB.SetReportStatus(id, status)
return MySQL.update.await('UPDATE ic_web_reports SET status = ? WHERE id = ?', { status, id })
end