feat: add auto-migrations on startup

This commit is contained in:
Kenshin13
2025-08-24 17:20:20 +02:00
parent f75aab49de
commit dd60b20c99
4 changed files with 100 additions and 2 deletions
+3 -1
View File
@@ -35,7 +35,9 @@ server_scripts {
'server/bridge/**/*.lua',
'server/modules/npwd.lua',
'server/modules/createJob.lua'
'server/modules/createJob.lua',
'server/migration/**/main.lua',
'server/migration/main.lua',
}
client_scripts {
+6 -1
View File
@@ -818,8 +818,9 @@ function Core.IsPlayerAdmin(playerSrc)
end
-- Generates a unique 9-digit SSN in dashed format (XXX-XX-XXXX).
---@param skipUniqueCheck boolean?
---@return string
function Core.generateSSN()
function Core.generateSSN(skipUniqueCheck)
local reservedSSNs = {
["078-05-1120"] = true,
["219-09-9999"] = true,
@@ -852,6 +853,10 @@ function Core.generateSSN()
goto continue
end
if skipUniqueCheck then
return candidate
end
local exists = MySQL.scalar.await("SELECT 1 FROM `users` WHERE `ssn` = ? LIMIT 1", { candidate })
if not exists then
@@ -0,0 +1,30 @@
RegisterCommand("resetmigrations", function(src)
if src > 0 then
print("^1[ERROR]^7 This command can only be run from the server console.")
return
end
for version, _ in pairs(Migrations or {}) do
DeleteResourceKvp(("esx_migration:%s"):format(version))
end
print("^2[SUCCESS]^7 Reset all migrations. This will re-run all migrations on the next server start.")
end)
for esxVersion, migrations in pairs(Migrations or {}) do
---@cast esxVersion string
---@cast migrations table<string, function>
if GetResourceKvpInt(("esx_migration:%s"):format(esxVersion)) ~= 1 then
print(("^4[INFO]^7 Running migrations for ESX version %s"):format(esxVersion))
for migrationName, migration in pairs(migrations) do
local success, err = pcall(migration)
if not success then
error(("^1[ERROR]^7 Failed migration ^4['%s.%s']^7: %s"):format(esxVersion, migrationName, err))
end
end
SetResourceKvpInt(("esx_migration:%s"):format(esxVersion), 1)
print(("^2[SUCCESS]^7 Successfully completed migrations for ESX version %s"):format(esxVersion))
end
end
@@ -0,0 +1,61 @@
Migrations = Migrations or {}
Migrations["v1.13.3"] = Migrations["v1.13.3"] or {}
Migrations["v1.13.3"].ssn = function()
print("^4[esx_migration:v.1.13.3:ssn]^7 Adding SSN column to users table.")
local col = MySQL.scalar.await([[
SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'users'
AND COLUMN_NAME = 'ssn'
]])
local idx = MySQL.scalar.await([[
SELECT COUNT(*)
FROM INFORMATION_SCHEMA.STATISTICS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'users'
AND INDEX_NAME = 'unique_ssn'
]])
if col == 0 and idx == 0 then
MySQL.update.await([[
ALTER TABLE `users`
ADD COLUMN `ssn` VARCHAR(11) NULL DEFAULT NULL AFTER `identifier`,
ADD UNIQUE KEY `unique_ssn` (`ssn`)
]])
elseif col == 0 then
MySQL.update.await("ALTER TABLE `users` ADD COLUMN `ssn` VARCHAR(11) NULL DEFAULT NULL AFTER `identifier`")
elseif idx == 0 then
MySQL.update.await("ALTER TABLE `users` ADD UNIQUE KEY `unique_ssn` (`ssn`)")
end
local Result = MySQL.query.await("SELECT `identifier` FROM `users` WHERE `ssn` IS NULL")
if #Result == 0 then
print("^4[esx_migration:v.1.13.3:ssn]^7 No users found without SSN, migration not needed.")
return
end
print("^4[esx_migration:v.1.13.3:ssn]^7 Generating SSN for existing users.")
local GeneratedSSNs = {}
local Parameters = {}
for i = 1, #Result do
local ssn
repeat
ssn = Core.generateSSN(true)
until not GeneratedSSNs[ssn]
GeneratedSSNs[ssn] = true
Parameters[i] = { ssn, Result[i].identifier }
end
print("^4[esx_migration:v.1.13.3:ssn]^7 Updating users with generated SSN. This may take a minute...")
MySQL.prepare.await("UPDATE `users` SET `ssn` = ? WHERE `identifier` = ?", Parameters)
print("^4[esx_migration:v.1.13.3:ssn]^7 Removing SSN default value.")
MySQL.update.await("ALTER TABLE `users` MODIFY `ssn` VARCHAR(11) NOT NULL")
print(("^4[esx_migration:v.1.13.3:ssn]^7 Successfully migrated %d users."):format(#Parameters))
end