IC-Webhosting: Domaenen, Webseiten und Postfaecher ueber Anmeldungen
Ein IC-Hostinganbieter im Spiel. Er vergibt Domaenen, richtet je Domaene einen Zugang ein, und die Inhaber pflegen darauf ihre Webseite. Die gesamte Verwaltung haengt an einer Anmeldung, nicht an Job oder Serverrechten. Ein Zugang ist zugleich ein Postfach: wer sich anmeldet, bekommt das Postfach dieser Adresse. Mehrere Personen koennen denselben Zugang benutzen und dasselbe Postfach gleichzeitig offen haben - ein Firmenpostfach gehoert der Firma, nicht einer Person. Drei Ebenen: superadmin vergibt Domaenen und setzt je Domaene einen Admin ein admin pflegt die Seiten seiner Domaene und legt dort Zugaenge an editor pflegt nur die Seiten Seiteninhalte sind kein HTML, sondern typisierte Bloecke. Die Seiten werden im NUI des IC-Computers angezeigt, also im selben Kontext wie dieser selbst - wer dort Markup einschleusen kann, kann auch dessen Callbacks aufrufen. Ein Blockmodell schliesst das konstruktiv aus, statt es filtern zu muessen. Passwoerter liegen gesalzen und gehasht in der Datenbank, gerechnet wird in der Datenbank. Fuenf Fehlversuche je Benutzername sperren den Namen kurzzeitig. Enthaelt README.md mit Einrichtung und Anbindung an den IC-Computer sowie ANLEITUNG.md fuer die Bedienung im Spiel.
This commit is contained in:
commit
a2ce3a69fd
14 changed files with 3018 additions and 0 deletions
411
server/main.lua
Normal file
411
server/main.lua
Normal file
|
|
@ -0,0 +1,411 @@
|
|||
-- ─────────────────────────────────────────────────────────────────────────────
|
||||
-- ic-web – Netzwerkereignisse, Exports, Konsolenbefehle
|
||||
--
|
||||
-- Jedes Ereignis ist frei aufrufbar und deshalb gedrosselt. Rechte werden
|
||||
-- ausnahmslos serverseitig aus der Anmeldung abgeleitet; der Client erfaehrt
|
||||
-- nur das Ergebnis.
|
||||
-- ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
local S = ICWeb.Sites
|
||||
local D = ICWeb.Domains
|
||||
local M = ICWeb.Manage
|
||||
local A = ICWeb.Auth
|
||||
local DB = ICWeb.DB
|
||||
local FW = ICWeb.FW
|
||||
|
||||
local function reply(src, reqId, ok, data, err)
|
||||
TriggerClientEvent('ic-web:client:response', src, reqId,
|
||||
{ ok = ok == true, data = data, error = err })
|
||||
end
|
||||
|
||||
-- ── Anmeldung ────────────────────────────────────────────────────────────────
|
||||
RegisterNetEvent('ic-web:server:login', function(reqId, username, password)
|
||||
local src = source
|
||||
-- Eng gedrosselt: die Sperre je Benutzername greift erst nach fuenf
|
||||
-- Versuchen, diese hier bremst das Durchprobieren vieler Benutzernamen.
|
||||
if not ICWeb.ThrottleOk(src, 'login', 8, 60000) then
|
||||
return reply(src, reqId, false, nil, 'Zu viele Versuche, bitte kurz warten.')
|
||||
end
|
||||
|
||||
local session, err = A.Login(src, username, password)
|
||||
reply(src, reqId, session ~= nil, session, err)
|
||||
end)
|
||||
|
||||
RegisterNetEvent('ic-web:server:logout', function(reqId)
|
||||
local src = source
|
||||
A.Logout(src)
|
||||
reply(src, reqId, true)
|
||||
end)
|
||||
|
||||
-- Wer bin ich gerade? Ohne Anmeldung: data = nil, aber ok = true.
|
||||
RegisterNetEvent('ic-web:server:session', function(reqId)
|
||||
local src = source
|
||||
if not ICWeb.ThrottleOk(src, 'session', 20, 10000) then return end
|
||||
|
||||
reply(src, reqId, true, {
|
||||
session = A.Public(src),
|
||||
themes = Config.Themes,
|
||||
tlds = Config.Domain.allowedTlds,
|
||||
imageHosts = Config.ImageHosts,
|
||||
provider = Config.MainAdmin.providerName,
|
||||
selfService = Config.SelfRegistration.enabled,
|
||||
})
|
||||
end)
|
||||
|
||||
RegisterNetEvent('ic-web:server:changePassword', function(reqId, accountId, password)
|
||||
local src = source
|
||||
if not ICWeb.ThrottleOk(src, 'passwd', 5, 60000) then return end
|
||||
|
||||
local ok, err = M.SetAccountPassword(src, accountId, password)
|
||||
if ok then FW.Notify(src, 'Passwort geändert.') end
|
||||
reply(src, reqId, ok, nil, err)
|
||||
end)
|
||||
|
||||
-- ── Lesen: jeder darf ────────────────────────────────────────────────────────
|
||||
RegisterNetEvent('ic-web:server:getSite', function(reqId, domain, slug)
|
||||
local src = source
|
||||
if not ICWeb.ThrottleOk(src, 'get', 30, 10000) then return end
|
||||
|
||||
local site, err = S.GetPublic(domain, slug)
|
||||
reply(src, reqId, site ~= nil, site, err)
|
||||
end)
|
||||
|
||||
-- ── Eigene Domaenen ──────────────────────────────────────────────────────────
|
||||
RegisterNetEvent('ic-web:server:listMine', function(reqId)
|
||||
local src = source
|
||||
if not ICWeb.ThrottleOk(src, 'listMine', 10, 10000) then return end
|
||||
reply(src, reqId, true, S.ListMine(src))
|
||||
end)
|
||||
|
||||
RegisterNetEvent('ic-web:server:getEditable', function(reqId, domain)
|
||||
local src = source
|
||||
if not ICWeb.ThrottleOk(src, 'getEditable', 20, 10000) then return end
|
||||
|
||||
local site, err = S.GetForEditor(src, domain)
|
||||
reply(src, reqId, site ~= nil, site, err)
|
||||
end)
|
||||
|
||||
-- ── Selbstanmeldung einer Domaene (nur wenn eingeschaltet) ───────────────────
|
||||
RegisterNetEvent('ic-web:server:register', function(reqId, domain, title)
|
||||
local src = source
|
||||
if not ICWeb.ThrottleOk(src, 'register', 3, 60000) then
|
||||
return reply(src, reqId, false, nil, 'Zu viele Versuche, bitte kurz warten.')
|
||||
end
|
||||
|
||||
local site, err, credentials = D.Register(src, domain, title)
|
||||
if not site then
|
||||
ICWeb.Log.Write(src, 'domain.rejected', { domain = tostring(domain), reason = err }, 'warn')
|
||||
return reply(src, reqId, false, nil, err)
|
||||
end
|
||||
|
||||
FW.Notify(src, ('Domäne %s registriert.'):format(site.domain))
|
||||
reply(src, reqId, true, {
|
||||
domain = site.domain, title = site.title, credentials = credentials,
|
||||
})
|
||||
end)
|
||||
|
||||
-- ── Bearbeiten ───────────────────────────────────────────────────────────────
|
||||
RegisterNetEvent('ic-web:server:savePage', function(reqId, domain, slug, title, blocks)
|
||||
local src = source
|
||||
if not ICWeb.ThrottleOk(src, 'save', 20, 60000) then
|
||||
return reply(src, reqId, false, nil, 'Zu viele Speichervorgänge, bitte kurz warten.')
|
||||
end
|
||||
|
||||
local ok, err = S.SavePage(src, domain, slug, title, blocks)
|
||||
reply(src, reqId, ok, nil, err)
|
||||
end)
|
||||
|
||||
RegisterNetEvent('ic-web:server:deletePage', function(reqId, domain, slug)
|
||||
local src = source
|
||||
if not ICWeb.ThrottleOk(src, 'delete', 10, 60000) then return end
|
||||
local ok, err = S.DeletePage(src, domain, slug)
|
||||
reply(src, reqId, ok, nil, err)
|
||||
end)
|
||||
|
||||
RegisterNetEvent('ic-web:server:updateSite', function(reqId, domain, data)
|
||||
local src = source
|
||||
if not ICWeb.ThrottleOk(src, 'update', 20, 60000) then return end
|
||||
if type(data) ~= 'table' then return reply(src, reqId, false, nil, 'Ungültige Daten.') end
|
||||
|
||||
local ok, err = S.UpdateSite(src, domain, data)
|
||||
reply(src, reqId, ok, nil, err)
|
||||
end)
|
||||
|
||||
-- ── Melden ───────────────────────────────────────────────────────────────────
|
||||
-- Ohne Anmeldung: jeder Spieler soll eine Seite melden koennen.
|
||||
RegisterNetEvent('ic-web:server:report', function(reqId, domain, slug, reason)
|
||||
local src = source
|
||||
if not ICWeb.ThrottleOk(src, 'report', 5, 60000) then return end
|
||||
|
||||
local ok, err = S.Report(src, domain, slug, reason)
|
||||
if ok then FW.Notify(src, 'Meldung eingegangen. Danke.') end
|
||||
reply(src, reqId, ok, nil, err)
|
||||
end)
|
||||
|
||||
-- ── Postfaecher im Mailclient ───────────────────────────────────────────────
|
||||
-- Ein Postfach freischalten ist eine Passwortpruefung wie die Anmeldung und
|
||||
-- deshalb genauso eng gedrosselt.
|
||||
RegisterNetEvent('ic-web:server:openMailbox', function(reqId, username, password)
|
||||
local src = source
|
||||
if not ICWeb.ThrottleOk(src, 'openMailbox', 8, 60000) then
|
||||
return reply(src, reqId, false, nil, 'Zu viele Versuche, bitte kurz warten.')
|
||||
end
|
||||
|
||||
local ok, err = A.OpenMailbox(src, username, password)
|
||||
if ok then FW.Notify(src, 'Postfach hinzugefügt.') end
|
||||
reply(src, reqId, ok, nil, err)
|
||||
end)
|
||||
|
||||
RegisterNetEvent('ic-web:server:closeMailbox', function(reqId, username)
|
||||
local src = source
|
||||
if not ICWeb.ThrottleOk(src, 'closeMailbox', 20, 60000) then return end
|
||||
|
||||
local ok, err = A.CloseMailbox(src, username)
|
||||
reply(src, reqId, ok, nil, err)
|
||||
end)
|
||||
|
||||
RegisterNetEvent('ic-web:server:listMailboxes', function(reqId)
|
||||
local src = source
|
||||
if not ICWeb.ThrottleOk(src, 'listMailboxes', 20, 10000) then return end
|
||||
reply(src, reqId, true, A.ListOpenMailboxes(src))
|
||||
end)
|
||||
|
||||
-- ── Zugaenge je Domaene ─────────────────────────────────────────────────────
|
||||
RegisterNetEvent('ic-web:server:listAccounts', function(reqId, domain)
|
||||
local src = source
|
||||
if not ICWeb.ThrottleOk(src, 'accounts', 20, 10000) then return end
|
||||
|
||||
local list, err = M.ListAccounts(src, domain)
|
||||
reply(src, reqId, list ~= nil, list, err)
|
||||
end)
|
||||
|
||||
RegisterNetEvent('ic-web:server:createAccount',
|
||||
function(reqId, domain, username, password, role, displayName)
|
||||
local src = source
|
||||
if not ICWeb.ThrottleOk(src, 'createAccount', 10, 60000) then return end
|
||||
|
||||
local account, err = M.CreateAccount(src, domain, username, password, role, displayName)
|
||||
reply(src, reqId, account ~= nil, account, err)
|
||||
end)
|
||||
|
||||
RegisterNetEvent('ic-web:server:setAccountPassword', function(reqId, accountId, password)
|
||||
local src = source
|
||||
if not ICWeb.ThrottleOk(src, 'setPassword', 10, 60000) then return end
|
||||
|
||||
local ok, err = M.SetAccountPassword(src, accountId, password)
|
||||
reply(src, reqId, ok, nil, err)
|
||||
end)
|
||||
|
||||
RegisterNetEvent('ic-web:server:setAccountActive', function(reqId, accountId, active)
|
||||
local src = source
|
||||
if not ICWeb.ThrottleOk(src, 'setActive', 20, 60000) then return end
|
||||
|
||||
local ok, err = M.SetAccountActive(src, accountId, active == true)
|
||||
reply(src, reqId, ok, nil, err)
|
||||
end)
|
||||
|
||||
RegisterNetEvent('ic-web:server:setAccountRole', function(reqId, accountId, role)
|
||||
local src = source
|
||||
if not ICWeb.ThrottleOk(src, 'setRole', 20, 60000) then return end
|
||||
|
||||
local ok, err = M.SetAccountRole(src, accountId, role)
|
||||
reply(src, reqId, ok, nil, err)
|
||||
end)
|
||||
|
||||
RegisterNetEvent('ic-web:server:deleteAccount', function(reqId, accountId)
|
||||
local src = source
|
||||
if not ICWeb.ThrottleOk(src, 'deleteAccount', 10, 60000) then return end
|
||||
|
||||
local ok, err = M.DeleteAccount(src, accountId)
|
||||
reply(src, reqId, ok, nil, err)
|
||||
end)
|
||||
|
||||
-- ── Anbieterebene ───────────────────────────────────────────────────────────
|
||||
RegisterNetEvent('ic-web:server:adminListSites', function(reqId)
|
||||
local src = source
|
||||
if not ICWeb.ThrottleOk(src, 'adminList', 20, 10000) then return end
|
||||
|
||||
local list, err = M.AdminListSites(src)
|
||||
reply(src, reqId, list ~= nil, list, err)
|
||||
end)
|
||||
|
||||
RegisterNetEvent('ic-web:server:adminListAccounts', function(reqId)
|
||||
local src = source
|
||||
if not ICWeb.ThrottleOk(src, 'adminAccounts', 20, 10000) then return end
|
||||
|
||||
local list, err = M.AdminListAccounts(src)
|
||||
reply(src, reqId, list ~= nil, list, err)
|
||||
end)
|
||||
|
||||
RegisterNetEvent('ic-web:server:adminListJobs', function(reqId)
|
||||
local src = source
|
||||
if not ICWeb.ThrottleOk(src, 'adminJobs', 10, 10000) then return end
|
||||
|
||||
local list, err = M.AdminListJobs(src)
|
||||
reply(src, reqId, list ~= nil, list, err)
|
||||
end)
|
||||
|
||||
RegisterNetEvent('ic-web:server:adminRegister',
|
||||
function(reqId, domain, title, ownerId, adminUser, adminPassword)
|
||||
local src = source
|
||||
if not ICWeb.ThrottleOk(src, 'adminRegister', 10, 60000) then return end
|
||||
|
||||
local site, err, credentials = M.AdminRegister(src, domain, title, ownerId,
|
||||
adminUser, adminPassword)
|
||||
reply(src, reqId, site ~= nil,
|
||||
site and { domain = site.domain, credentials = credentials } or nil, err)
|
||||
end)
|
||||
|
||||
RegisterNetEvent('ic-web:server:adminSetBlocked', function(reqId, domain, blocked)
|
||||
local src = source
|
||||
if not ICWeb.ThrottleOk(src, 'adminBlock', 20, 60000) then return end
|
||||
|
||||
local ok, err = M.AdminSetBlocked(src, domain, blocked == true)
|
||||
reply(src, reqId, ok, nil, err)
|
||||
end)
|
||||
|
||||
RegisterNetEvent('ic-web:server:adminSetOwner', function(reqId, domain, ownerId)
|
||||
local src = source
|
||||
if not ICWeb.ThrottleOk(src, 'adminOwner', 10, 60000) then return end
|
||||
|
||||
local ok, err = M.AdminSetOwner(src, domain, ownerId)
|
||||
reply(src, reqId, ok, nil, err)
|
||||
end)
|
||||
|
||||
RegisterNetEvent('ic-web:server:adminDeleteSite', function(reqId, domain)
|
||||
local src = source
|
||||
if not ICWeb.ThrottleOk(src, 'adminDelete', 5, 60000) then return end
|
||||
|
||||
local ok, err = M.AdminDeleteSite(src, domain)
|
||||
reply(src, reqId, ok, nil, err)
|
||||
end)
|
||||
|
||||
RegisterNetEvent('ic-web:server:adminListReports', function(reqId)
|
||||
local src = source
|
||||
if not ICWeb.ThrottleOk(src, 'adminReports', 20, 10000) then return end
|
||||
|
||||
local list, err = M.AdminListReports(src)
|
||||
reply(src, reqId, list ~= nil, list, err)
|
||||
end)
|
||||
|
||||
RegisterNetEvent('ic-web:server:adminSetReportStatus', function(reqId, id, status)
|
||||
local src = source
|
||||
if not ICWeb.ThrottleOk(src, 'adminReportStatus', 30, 60000) then return end
|
||||
|
||||
local ok, err = M.AdminSetReportStatus(src, id, status)
|
||||
reply(src, reqId, ok, nil, err)
|
||||
end)
|
||||
|
||||
-- ── Konsolenbefehle ─────────────────────────────────────────────────────────
|
||||
-- Kein IC-Weg, sondern die Notbremse des Serverbetreibers. Absichtlich nur in
|
||||
-- der Serverkonsole und fuer Serveradmins – nicht ueber die Anmeldung.
|
||||
local function consoleAllowed(src)
|
||||
if not src or src == 0 then return true end
|
||||
return FW.IsAdmin(src)
|
||||
end
|
||||
|
||||
RegisterCommand('webpasswd', function(src, args)
|
||||
if not consoleAllowed(src) then return end
|
||||
|
||||
local username = tostring(args[1] or ''):lower()
|
||||
local password = tostring(args[2] or '')
|
||||
if username == '' or password == '' then
|
||||
print('[ic-web] /webpasswd <benutzer@domaene> <neues-passwort>')
|
||||
return
|
||||
end
|
||||
|
||||
local account = DB.GetAccountByUsername(username)
|
||||
if not account then print('[ic-web] Zugang nicht gefunden.') return end
|
||||
|
||||
local ok, err = A.SetPassword(account.id, password)
|
||||
print(ok and ('[ic-web] Passwort von %s geändert.'):format(username)
|
||||
or ('[ic-web] Fehlgeschlagen: %s'):format(tostring(err)))
|
||||
end, true)
|
||||
|
||||
RegisterCommand('webaccounts', function(src)
|
||||
if not consoleAllowed(src) then return end
|
||||
|
||||
local rows = DB.GetAllAccounts()
|
||||
print(('[ic-web] %d Zugänge:'):format(#rows))
|
||||
for _, a in ipairs(rows) do
|
||||
print((' %-34s %-11s %-22s %s'):format(
|
||||
a.username, a.role, a.domain or '—',
|
||||
ICWeb.Bool(a.active) and 'aktiv' or 'gesperrt'))
|
||||
end
|
||||
end, true)
|
||||
|
||||
RegisterCommand('webreports', function(src)
|
||||
if not consoleAllowed(src) then return end
|
||||
|
||||
local reports = DB.GetOpenReports()
|
||||
if #reports == 0 then print('[ic-web] Keine offenen Meldungen.') return end
|
||||
|
||||
print(('[ic-web] %d offene Meldungen:'):format(#reports))
|
||||
for _, r in ipairs(reports) do
|
||||
print((' #%d %-24s %-10s %s'):format(
|
||||
r.id, r.domain, r.page_slug or '-', (r.reason or ''):sub(1, 60)))
|
||||
end
|
||||
print('[ic-web] Sperren mit /webblock <domain>, erledigen mit /webreport done <id>')
|
||||
end, true)
|
||||
|
||||
RegisterCommand('webblock', function(src, args)
|
||||
if not consoleAllowed(src) then return end
|
||||
|
||||
local domain = tostring(args[1] or ''):lower()
|
||||
local site = DB.GetSiteByDomain(domain)
|
||||
if not site then print('[ic-web] Domäne nicht gefunden.') return end
|
||||
|
||||
local blocked = not ICWeb.Bool(site.blocked)
|
||||
DB.SetBlocked(site.id, blocked)
|
||||
ICWeb.Log.Write(src, blocked and 'site.blocked' or 'site.unblocked', { domain = domain }, 'warn')
|
||||
print(('[ic-web] %s ist jetzt %s.'):format(domain, blocked and 'gesperrt' or 'frei'))
|
||||
end, true)
|
||||
|
||||
RegisterCommand('webreport', function(src, args)
|
||||
if not consoleAllowed(src) then return end
|
||||
|
||||
local action = tostring(args[1] or '')
|
||||
local id = tonumber(args[2])
|
||||
if (action ~= 'done' and action ~= 'dismiss') or not id then
|
||||
print('[ic-web] /webreport done <id> oder /webreport dismiss <id>')
|
||||
return
|
||||
end
|
||||
|
||||
DB.SetReportStatus(id, action == 'done' and 'reviewed' or 'dismissed')
|
||||
print(('[ic-web] Meldung #%d als %s markiert.'):format(id, action))
|
||||
end, true)
|
||||
|
||||
-- ── Exports fuer andere Resources ────────────────────────────────────────────
|
||||
exports('GetSite', function(domain, slug)
|
||||
return S.GetPublic(domain, slug)
|
||||
end)
|
||||
|
||||
exports('DomainExists', function(domain)
|
||||
return DB.GetSiteByDomain(tostring(domain or ''):lower()) ~= nil
|
||||
end)
|
||||
|
||||
exports('GetSitesForJob', function(job)
|
||||
return DB.GetSitesForOwner('job', job)
|
||||
end)
|
||||
|
||||
-- Wer ist an diesem Bildschirm angemeldet? pc-live nutzt das, um das Postfach
|
||||
-- des Zugangs zu oeffnen statt des persoenlichen.
|
||||
exports('GetSession', function(src)
|
||||
return A.Public(src)
|
||||
end)
|
||||
|
||||
-- ── Start ────────────────────────────────────────────────────────────────────
|
||||
AddEventHandler('onResourceStart', function(res)
|
||||
if res ~= GetCurrentResourceName() then return end
|
||||
|
||||
if GetResourceState('ic-mail') ~= 'started' then
|
||||
print('[ic-web] ^3Hinweis^7: ic-mail läuft nicht – Domänen werden ohne Postfächer angelegt.')
|
||||
end
|
||||
|
||||
A.EnsureMainAdmin()
|
||||
A.CleanupStaleMailAccess()
|
||||
DB.PruneLoginFails(86400)
|
||||
|
||||
local sites = MySQL.single.await('SELECT COUNT(*) AS n FROM ic_web_sites')
|
||||
print(('[ic-web] bereit. %d Domänen registriert.'):format((sites and sites.n) or 0))
|
||||
end)
|
||||
Loading…
Add table
Add a link
Reference in a new issue