420 lines
15 KiB
Lua
420 lines
15 KiB
Lua
|
|
-- ─────────────────────────────────────────────────────────────────────────────
|
|||
|
|
-- Database layer – all MySQL calls live here
|
|||
|
|
-- ─────────────────────────────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
DB = {}
|
|||
|
|
|
|||
|
|
-- ── Users ──────────────────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
---@param identifier string
|
|||
|
|
---@param displayName string
|
|||
|
|
---@return table?
|
|||
|
|
function DB.GetOrCreateUser(identifier, displayName)
|
|||
|
|
local row = MySQL.single.await(
|
|||
|
|
'SELECT * FROM pc_live_users WHERE identifier = ?',
|
|||
|
|
{ identifier }
|
|||
|
|
)
|
|||
|
|
if row then return row end
|
|||
|
|
|
|||
|
|
MySQL.insert.await(
|
|||
|
|
'INSERT INTO pc_live_users (identifier, display_name, settings_json) VALUES (?, ?, ?)',
|
|||
|
|
{ identifier, displayName, '{}' }
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
return MySQL.single.await(
|
|||
|
|
'SELECT * FROM pc_live_users WHERE identifier = ?',
|
|||
|
|
{ identifier }
|
|||
|
|
)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
---@param identifier string
|
|||
|
|
---@param settings table
|
|||
|
|
function DB.SaveSettings(identifier, settings)
|
|||
|
|
MySQL.update.await(
|
|||
|
|
'UPDATE pc_live_users SET settings_json = ? WHERE identifier = ?',
|
|||
|
|
{ json.encode(settings), identifier }
|
|||
|
|
)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
-- ── Installs ───────────────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
---@param userId number
|
|||
|
|
---@return table[]
|
|||
|
|
function DB.GetInstalls(userId)
|
|||
|
|
return MySQL.query.await(
|
|||
|
|
'SELECT * FROM pc_live_installs WHERE user_id = ?',
|
|||
|
|
{ userId }
|
|||
|
|
)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
---@param userId number
|
|||
|
|
---@param appId string
|
|||
|
|
---@param version string
|
|||
|
|
function DB.InstallApp(userId, appId, version)
|
|||
|
|
local existing = MySQL.single.await(
|
|||
|
|
'SELECT id FROM pc_live_installs WHERE user_id = ? AND app_id = ?',
|
|||
|
|
{ userId, appId }
|
|||
|
|
)
|
|||
|
|
if existing then
|
|||
|
|
MySQL.update.await(
|
|||
|
|
'UPDATE pc_live_installs SET version = ?, installed_at = NOW() WHERE id = ?',
|
|||
|
|
{ version, existing.id }
|
|||
|
|
)
|
|||
|
|
else
|
|||
|
|
MySQL.insert.await(
|
|||
|
|
'INSERT INTO pc_live_installs (user_id, app_id, version, installed_at) VALUES (?, ?, ?, NOW())',
|
|||
|
|
{ userId, appId, version }
|
|||
|
|
)
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
---@param userId number
|
|||
|
|
---@param appId string
|
|||
|
|
function DB.UninstallApp(userId, appId)
|
|||
|
|
MySQL.update.await(
|
|||
|
|
'DELETE FROM pc_live_installs WHERE user_id = ? AND app_id = ?',
|
|||
|
|
{ userId, appId }
|
|||
|
|
)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
-- ── Mail ───────────────────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
---@param toId string
|
|||
|
|
---@return table[]
|
|||
|
|
function DB.GetInbox(toId)
|
|||
|
|
return MySQL.query.await(
|
|||
|
|
'SELECT * FROM pc_live_mail WHERE to_identifier = ? ORDER BY sent_at DESC LIMIT 100',
|
|||
|
|
{ toId }
|
|||
|
|
)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
---@param id number
|
|||
|
|
---@param toId string
|
|||
|
|
---@return table?
|
|||
|
|
function DB.GetMail(id, toId)
|
|||
|
|
return MySQL.single.await(
|
|||
|
|
'SELECT * FROM pc_live_mail WHERE id = ? AND to_identifier = ?',
|
|||
|
|
{ id, toId }
|
|||
|
|
)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
---@param toId string
|
|||
|
|
---@param fromId string
|
|||
|
|
---@param subject string
|
|||
|
|
---@param body string
|
|||
|
|
---@return number inserted id
|
|||
|
|
function DB.SendMail(toId, fromId, subject, body)
|
|||
|
|
return MySQL.insert.await(
|
|||
|
|
'INSERT INTO pc_live_mail (to_identifier, from_identifier, subject, body, is_read, sent_at) VALUES (?, ?, ?, ?, 0, NOW())',
|
|||
|
|
{ toId, fromId, subject, body }
|
|||
|
|
)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
---@param toId string Empfänger-Identifier (aufgelöst)
|
|||
|
|
---@param fromDisplay string Absender-Anzeige (E-Mail oder Identifier)
|
|||
|
|
---@param senderId string Roher Identifier des Absenders (für Gesendet-Abfrage)
|
|||
|
|
---@param toAddress string Originale Empfängeradresse
|
|||
|
|
---@param subject string
|
|||
|
|
---@param body string
|
|||
|
|
---@return number
|
|||
|
|
--- deliveryId klammert die Kopien einer Zustellung zusammen: eine Mail an ein
|
|||
|
|
--- geteiltes Postfach wird je Empfaenger einmal gespeichert, soll aber fuer
|
|||
|
|
--- alle im selben Ordner landen.
|
|||
|
|
function DB.SendMailExtended(toId, fromDisplay, senderId, toAddress, subject, body, deliveryId)
|
|||
|
|
return MySQL.insert.await(
|
|||
|
|
'INSERT INTO pc_live_mail'
|
|||
|
|
.. ' (to_identifier, from_identifier, sender_id, to_address, subject, body,'
|
|||
|
|
.. ' delivery_id, is_read, sent_at)'
|
|||
|
|
.. ' VALUES (?, ?, ?, ?, ?, ?, ?, 0, NOW())',
|
|||
|
|
{ toId, fromDisplay, senderId, toAddress, subject, body, deliveryId }
|
|||
|
|
)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
---@param id number
|
|||
|
|
---@param toId string
|
|||
|
|
function DB.MarkRead(id, toId)
|
|||
|
|
MySQL.update.await(
|
|||
|
|
'UPDATE pc_live_mail SET is_read = 1 WHERE id = ? AND to_identifier = ?',
|
|||
|
|
{ id, toId }
|
|||
|
|
)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
---@param id number
|
|||
|
|
---@param toId string
|
|||
|
|
function DB.DeleteMail(id, toId)
|
|||
|
|
MySQL.update.await(
|
|||
|
|
'DELETE FROM pc_live_mail WHERE id = ? AND to_identifier = ?',
|
|||
|
|
{ id, toId }
|
|||
|
|
)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
---@param toId string
|
|||
|
|
---@return number
|
|||
|
|
function DB.CountUnread(toId)
|
|||
|
|
local row = MySQL.single.await(
|
|||
|
|
'SELECT COUNT(*) as cnt FROM pc_live_mail WHERE to_identifier = ? AND is_read = 0',
|
|||
|
|
{ toId }
|
|||
|
|
)
|
|||
|
|
return row and row.cnt or 0
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
---@param fromId string Spieler-Identifier
|
|||
|
|
---@return table[]
|
|||
|
|
function DB.GetSent(fromId)
|
|||
|
|
return MySQL.query.await(
|
|||
|
|
'SELECT * FROM pc_live_mail'
|
|||
|
|
.. ' WHERE from_identifier = ? OR sender_id = ?'
|
|||
|
|
.. ' ORDER BY sent_at DESC LIMIT 100',
|
|||
|
|
{ fromId, fromId }
|
|||
|
|
)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
-- ── Mail Folders ───────────────────────────────────────────────────────────
|
|||
|
|
--
|
|||
|
|
-- Ordner gehoeren zum Postfach, nicht zur Person: ein geteiltes Postfach wird
|
|||
|
|
-- von mehreren bedient und braucht dieselbe Struktur fuer alle.
|
|||
|
|
--
|
|||
|
|
-- address = '' → persoenliche Ordner (Spalte address IS NULL)
|
|||
|
|
-- address ~= '' → Ordner dieses Postfachs
|
|||
|
|
--
|
|||
|
|
-- Der Aufrufer muss vorher pruefen, ob die Person das Postfach benutzen darf.
|
|||
|
|
|
|||
|
|
---@param identifier string
|
|||
|
|
---@param address string|nil
|
|||
|
|
---@return table[]
|
|||
|
|
function DB.GetFolders(identifier, address)
|
|||
|
|
if address and address ~= '' then
|
|||
|
|
return MySQL.query.await(
|
|||
|
|
'SELECT * FROM pc_live_mail_folders WHERE address = ? ORDER BY name ASC',
|
|||
|
|
{ address }
|
|||
|
|
) or {}
|
|||
|
|
end
|
|||
|
|
return MySQL.query.await(
|
|||
|
|
'SELECT * FROM pc_live_mail_folders WHERE identifier = ? AND address IS NULL ORDER BY name ASC',
|
|||
|
|
{ identifier }
|
|||
|
|
) or {}
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
---@param identifier string
|
|||
|
|
---@param address string|nil
|
|||
|
|
---@param name string
|
|||
|
|
---@return number
|
|||
|
|
function DB.CreateFolder(identifier, address, name)
|
|||
|
|
return MySQL.insert.await(
|
|||
|
|
'INSERT INTO pc_live_mail_folders (identifier, address, name) VALUES (?, ?, ?)',
|
|||
|
|
{ identifier, (address ~= '' and address or nil), name }
|
|||
|
|
)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--- Gibt den Ordner zurueck, wenn er zu diesem Postfach gehoert – sonst nil.
|
|||
|
|
--- Damit haengt jede Ordneraktion an einer Besitzpruefung statt an der Id
|
|||
|
|
--- allein, die der Client frei mitschicken kann.
|
|||
|
|
---@param folderId number
|
|||
|
|
---@param identifier string
|
|||
|
|
---@param address string|nil
|
|||
|
|
---@return table|nil
|
|||
|
|
function DB.GetFolder(folderId, identifier, address)
|
|||
|
|
if address and address ~= '' then
|
|||
|
|
return MySQL.single.await(
|
|||
|
|
'SELECT * FROM pc_live_mail_folders WHERE id = ? AND address = ?',
|
|||
|
|
{ folderId, address }
|
|||
|
|
)
|
|||
|
|
end
|
|||
|
|
return MySQL.single.await(
|
|||
|
|
'SELECT * FROM pc_live_mail_folders WHERE id = ? AND identifier = ? AND address IS NULL',
|
|||
|
|
{ folderId, identifier }
|
|||
|
|
)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
---@param folderId number
|
|||
|
|
---@param identifier string
|
|||
|
|
---@param address string|nil
|
|||
|
|
function DB.DeleteFolder(folderId, identifier, address)
|
|||
|
|
-- Mails zurueck in den Posteingang. Bei einem geteilten Postfach betrifft
|
|||
|
|
-- das die Kopien aller Beteiligten, sonst blieben deren Mails in einem
|
|||
|
|
-- Ordner haengen, den es nicht mehr gibt.
|
|||
|
|
if address and address ~= '' then
|
|||
|
|
MySQL.update.await(
|
|||
|
|
'UPDATE pc_live_mail SET folder_id = NULL WHERE folder_id = ? AND to_address = ?',
|
|||
|
|
{ folderId, address }
|
|||
|
|
)
|
|||
|
|
else
|
|||
|
|
MySQL.update.await(
|
|||
|
|
'UPDATE pc_live_mail SET folder_id = NULL WHERE folder_id = ? AND to_identifier = ?',
|
|||
|
|
{ folderId, identifier }
|
|||
|
|
)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
MySQL.update.await('DELETE FROM pc_live_mail_folders WHERE id = ?', { folderId })
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--- Mail einsortieren.
|
|||
|
|
--- Im geteilten Postfach wandern alle Kopien derselben Zustellung mit, damit
|
|||
|
|
--- die Ablage fuer alle gleich aussieht.
|
|||
|
|
---@param mailId number
|
|||
|
|
---@param folderId number|nil nil = Posteingang
|
|||
|
|
---@param identifier string
|
|||
|
|
---@param address string|nil
|
|||
|
|
function DB.MoveMailToFolder(mailId, folderId, identifier, address)
|
|||
|
|
local mail = MySQL.single.await(
|
|||
|
|
'SELECT id, delivery_id, to_address FROM pc_live_mail WHERE id = ? AND to_identifier = ?',
|
|||
|
|
{ mailId, identifier }
|
|||
|
|
)
|
|||
|
|
if not mail then return end
|
|||
|
|
|
|||
|
|
local shared = address and address ~= '' and mail.to_address == address
|
|||
|
|
|
|||
|
|
if shared and mail.delivery_id then
|
|||
|
|
MySQL.update.await(
|
|||
|
|
'UPDATE pc_live_mail SET folder_id = ? WHERE delivery_id = ? AND to_address = ?',
|
|||
|
|
{ folderId, mail.delivery_id, address }
|
|||
|
|
)
|
|||
|
|
return
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
-- Aeltere Mails haben keine delivery_id – dann bleibt es bei der Kopie.
|
|||
|
|
MySQL.update.await(
|
|||
|
|
'UPDATE pc_live_mail SET folder_id = ? WHERE id = ? AND to_identifier = ?',
|
|||
|
|
{ folderId, mailId, identifier }
|
|||
|
|
)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
---@param folderId number
|
|||
|
|
---@param identifier string
|
|||
|
|
---@return table[]
|
|||
|
|
function DB.GetFolderMails(folderId, identifier)
|
|||
|
|
return MySQL.query.await(
|
|||
|
|
'SELECT * FROM pc_live_mail WHERE folder_id = ? AND to_identifier = ? ORDER BY sent_at DESC',
|
|||
|
|
{ folderId, identifier }
|
|||
|
|
) or {}
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
---@param identifier string
|
|||
|
|
---@return table[]
|
|||
|
|
function DB.GetInboxOnly(identifier)
|
|||
|
|
return MySQL.query.await(
|
|||
|
|
'SELECT * FROM pc_live_mail WHERE to_identifier = ? AND folder_id IS NULL ORDER BY sent_at DESC LIMIT 100',
|
|||
|
|
{ identifier }
|
|||
|
|
)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
-- ── Calendar ───────────────────────────────────────────────────────────────
|
|||
|
|
--
|
|||
|
|
-- Ein Termin ist entweder privat (address IS NULL) oder gehoert einem Postfach.
|
|||
|
|
-- Postfachtermine sind entweder nur fuer dessen Leute sichtbar ('shared') oder
|
|||
|
|
-- fuer alle ('public') – bearbeiten darf sie in beiden Faellen nur, wer das
|
|||
|
|
-- Postfach bedienen darf.
|
|||
|
|
|
|||
|
|
--- Alle Termine, die diese Person sehen darf.
|
|||
|
|
---@param identifier string
|
|||
|
|
---@param addresses string[] Postfaecher, auf die sie Zugriff hat
|
|||
|
|
---@return table[]
|
|||
|
|
function DB.GetCalendar(identifier, addresses)
|
|||
|
|
addresses = addresses or {}
|
|||
|
|
|
|||
|
|
local params = { identifier }
|
|||
|
|
local clauses = { '(c.identifier = ? AND c.address IS NULL)' }
|
|||
|
|
|
|||
|
|
if #addresses > 0 then
|
|||
|
|
local marks = {}
|
|||
|
|
for _, addr in ipairs(addresses) do
|
|||
|
|
marks[#marks + 1] = '?'
|
|||
|
|
params[#params + 1] = addr
|
|||
|
|
end
|
|||
|
|
clauses[#clauses + 1] = ('(c.address IN (%s))'):format(table.concat(marks, ','))
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
-- Oeffentliche Termine sieht jeder, auch ohne Zugriff auf das Postfach.
|
|||
|
|
clauses[#clauses + 1] = "(c.visibility = 'public')"
|
|||
|
|
|
|||
|
|
return MySQL.query.await(
|
|||
|
|
'SELECT c.* FROM pc_live_calendar c WHERE '
|
|||
|
|
.. table.concat(clauses, ' OR ') .. ' ORDER BY c.start_at ASC',
|
|||
|
|
params
|
|||
|
|
) or {}
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
---@param id number
|
|||
|
|
---@return table|nil
|
|||
|
|
function DB.GetCalendarEvent(id)
|
|||
|
|
return MySQL.single.await('SELECT * FROM pc_live_calendar WHERE id = ?', { id })
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
---@param identifier string
|
|||
|
|
---@param address string|nil '' oder nil = privat
|
|||
|
|
---@param visibility string 'private' | 'shared' | 'public'
|
|||
|
|
---@param data table
|
|||
|
|
---@return number
|
|||
|
|
function DB.AddCalendarEvent(identifier, address, visibility, data)
|
|||
|
|
return MySQL.insert.await(
|
|||
|
|
'INSERT INTO pc_live_calendar'
|
|||
|
|
.. ' (identifier, address, visibility, title, description, start_at, end_at, color)'
|
|||
|
|
.. ' VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
|
|||
|
|
{ identifier, (address ~= '' and address or nil), visibility,
|
|||
|
|
data.title, data.description or '', data.start_at,
|
|||
|
|
data.end_at or nil, data.color or '#00aaff' }
|
|||
|
|
)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--- Aendern. Die Berechtigung prueft der Aufrufer (server/calendar.lua) –
|
|||
|
|
--- hier steht keine Bedingung mehr, weil ein Postfachtermin auch von jemandem
|
|||
|
|
--- geaendert werden darf, der ihn nicht angelegt hat.
|
|||
|
|
---@param id number
|
|||
|
|
---@param address string|nil
|
|||
|
|
---@param visibility string
|
|||
|
|
---@param data table
|
|||
|
|
function DB.UpdateCalendarEvent(id, address, visibility, data)
|
|||
|
|
MySQL.update.await(
|
|||
|
|
'UPDATE pc_live_calendar SET address=?, visibility=?, title=?, description=?,'
|
|||
|
|
.. ' start_at=?, end_at=?, color=? WHERE id=?',
|
|||
|
|
{ (address ~= '' and address or nil), visibility,
|
|||
|
|
data.title, data.description or '', data.start_at,
|
|||
|
|
data.end_at or nil, data.color or '#00aaff', id }
|
|||
|
|
)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
---@param id number
|
|||
|
|
function DB.DeleteCalendarEvent(id)
|
|||
|
|
MySQL.update.await('DELETE FROM pc_live_calendar WHERE id = ?', { id })
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
-- ── Contacts ───────────────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
---@param identifier string
|
|||
|
|
---@return table[]
|
|||
|
|
function DB.GetContacts(identifier)
|
|||
|
|
return MySQL.query.await(
|
|||
|
|
'SELECT * FROM pc_live_contacts WHERE identifier = ? ORDER BY name ASC',
|
|||
|
|
{ identifier }
|
|||
|
|
)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
---@param identifier string
|
|||
|
|
---@param data table
|
|||
|
|
---@return number
|
|||
|
|
function DB.AddContact(identifier, data)
|
|||
|
|
return MySQL.insert.await(
|
|||
|
|
'INSERT INTO pc_live_contacts (identifier, name, email, phone, notes) VALUES (?, ?, ?, ?, ?)',
|
|||
|
|
{ identifier, data.name, data.email or '', data.phone or '', data.notes or '' }
|
|||
|
|
)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
---@param id number
|
|||
|
|
---@param identifier string
|
|||
|
|
---@param data table
|
|||
|
|
function DB.UpdateContact(id, identifier, data)
|
|||
|
|
MySQL.update.await(
|
|||
|
|
'UPDATE pc_live_contacts SET name=?, email=?, phone=?, notes=? WHERE id=? AND identifier=?',
|
|||
|
|
{ data.name, data.email or '', data.phone or '', data.notes or '', id, identifier }
|
|||
|
|
)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
---@param id number
|
|||
|
|
---@param identifier string
|
|||
|
|
function DB.DeleteContact(id, identifier)
|
|||
|
|
MySQL.update.await(
|
|||
|
|
'DELETE FROM pc_live_contacts WHERE id = ? AND identifier = ?',
|
|||
|
|
{ id, identifier }
|
|||
|
|
)
|
|||
|
|
end
|