236 lines
9.6 KiB
Lua
236 lines
9.6 KiB
Lua
|
|
-- ─────────────────────────────────────────────────────────────────────────────
|
|||
|
|
-- 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
|