feat: SQLite DB with migrations replaces all JSON file storage

- Add db.php with PDO singleton, migration runner, and all helper functions
- Add migrations/001_initial_schema.sql (full schema)
- Add migrations/002_import_json.php (one-time JSON → DB importer)
- Add _incl/switch_tenant.php POST endpoint for tenant/centro switching
- Update tools.auth.php: DB-backed login, cookie auth, session reload, init_active_centro()
- Update all sysadmin pages (users, centros, aularios, invitations, reset_password) to use DB
- Update aulatek/index.php, aulario.php, supercafe.php, supercafe_edit.php to use DB
- Update aulatek/comedor.php and api/comedor.php to use DB
- Update aulatek/paneldiario.php: aulario config + comedor data from DB
- Update aulatek/proyectos.php: aulario config + sharing metadata from DB
- Update club/cal.php, index.php, edit_data.php, upload/upload.php to use DB
- Update account/index.php: rich profile, tenant list, aula list, session info, permissions
- Update pre-body.php account dropdown: shows active org + inline tenant switcher
- Update DATA_STRUCTURE.md to document DB approach and migration system

Co-authored-by: naielv <109038805+naielv@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-03-06 22:00:48 +00:00
parent 937a0f4083
commit 0c362fd40b
30 changed files with 2050 additions and 1646 deletions

View File

@@ -1,100 +1,91 @@
<?php
require_once "_incl/auth_redir.php";
require_once "../_incl/db.php";
switch ($_GET['form']) {
switch ($_GET['form'] ?? '') {
case "create":
// Handle creation logic here
$invitations = json_decode(file_get_contents("/DATA/Invitaciones_de_usuarios.json"), true) ?? [];
$invitation_code = strtoupper($_POST['invitation_code'] ?? '');
$single_use = isset($_POST['single_use']) ? true : false;
if (isset($invitations[$invitation_code])) {
$code = strtoupper(trim($_POST['invitation_code'] ?? ''));
$single_use = isset($_POST['single_use']);
if (empty($code)) {
header("Location: /sysadmin/invitations.php?action=new&_resultcolor=red&_result=" . urlencode("Código de invitación vacío."));
exit;
}
if (db_get_invitation($code)) {
header("Location: /sysadmin/invitations.php?action=new&_resultcolor=red&_result=" . urlencode("El código de invitación ya existe."));
exit;
}
$invitations[$invitation_code] = [
"active" => true,
"single_use" => $single_use
];
file_put_contents("/DATA/Invitaciones_de_usuarios.json", json_encode($invitations, JSON_PRETTY_PRINT));
header("Location: /sysadmin/invitations.php?_result=" . urlencode("Código $invitation_code creado correctamente."));
db_upsert_invitation($code, true, $single_use);
header("Location: /sysadmin/invitations.php?_result=" . urlencode("Código $code creado correctamente."));
exit;
break;
case "delete":
// Handle deletion logic here
$invitations = json_decode(file_get_contents("/DATA/Invitaciones_de_usuarios.json"), true) ?? [];
$invitation_code = strtoupper($_POST['invitation_code'] ?? '');
if (isset($invitations[$invitation_code])) {
unset($invitations[$invitation_code]);
file_put_contents("/DATA/Invitaciones_de_usuarios.json", json_encode($invitations, JSON_PRETTY_PRINT));
}
header("Location: /sysadmin/invitations.php?_result=" . urlencode("Codigo $invitation_code borrado"));
$code = strtoupper(trim($_POST['invitation_code'] ?? ''));
db_delete_invitation($code);
header("Location: /sysadmin/invitations.php?_result=" . urlencode("Código $code borrado."));
exit;
break;
}
require_once "_incl/pre-body.php";
switch ($_GET['action']) {
switch ($_GET['action'] ?? 'index') {
case "new":
?>
<div class="card pad">
<div>
<h1 class="card-title">Nueva invitación de usuario</h1>
<form method="post" action="?form=create">
<div class="card pad" style="max-width: 500px;">
<div>
<div class="mb-3">
<label for="invitation_code" class="form-label"><b>Código de invitación:</b></label>
<input type="text" id="invitation_code" name="invitation_code" class="form-control" required />
<small>Formato: 123456-ABCDEF</small>
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" name="single_use" id="single_use">
<label class="form-check-label" for="single_use">
Uso único
</label>
</div>
<button type="submit" class="btn btn-primary">Crear invitación</button>
</div>
?>
<div class="card pad">
<div>
<h1 class="card-title">Nueva invitación de usuario</h1>
<form method="post" action="?form=create">
<div class="card pad" style="max-width: 500px;">
<div>
<div class="mb-3">
<label for="invitation_code" class="form-label"><b>Código de invitación:</b></label>
<input type="text" id="invitation_code" name="invitation_code" class="form-control" required />
<small>Formato: 123456-ABCDEF</small>
</div>
</form>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" name="single_use" id="single_use">
<label class="form-check-label" for="single_use">Uso único</label>
</div>
<button type="submit" class="btn btn-primary">Crear invitación</button>
</div>
</div>
</div>
<?php
</form>
</div>
</div>
<?php
break;
default:
case "index":
?>
<div class="card pad">
<div>
<h1>Invitaciones de usuarios</h1>
<span>Desde aquí puedes gestionar las invitaciones de usuarios.</span>
<table class="table table-striped table-hover">
<thead class="table-dark">
<th>Codigo de invitación</th>
<th>
<a href="?action=new" class="btn btn-success">+ Nuevo</a>
</th>
</thead>
<tbody>
<?php
$invitations = json_decode(file_get_contents("/DATA/Invitaciones_de_usuarios.json"), true);
foreach ($invitations as $inv_key => $inv_data) {
echo "<tr>";
echo "<td>" . htmlspecialchars($inv_key) . "</td>";
echo "<td>";
echo '<form method="post" action="?form=delete" style="display:inline;">';
echo '<input type="hidden" name="invitation_code" value="' . htmlspecialchars($inv_key) . '"/>';
echo '<button type="submit" class="btn btn-danger" onclick="return confirm(\'¿Estás seguro de que deseas eliminar esta invitación?\');">Eliminar</button>';
echo '</form>';
echo "</td>";
echo "</tr>";
}
?>
</tbody>
</table>
</div>
</div>
$invitations = db_get_all_invitations();
?>
<div class="card pad">
<div>
<h1>Invitaciones de usuarios</h1>
<table class="table table-striped table-hover">
<thead class="table-dark">
<th>Código</th>
<th>Activo</th>
<th>Uso único</th>
<th><a href="?action=new" class="btn btn-success">+ Nuevo</a></th>
</thead>
<tbody>
<?php foreach ($invitations as $inv): ?>
<tr>
<td><?= htmlspecialchars($inv['code']) ?></td>
<td><?= $inv['active'] ? 'Sí' : 'No' ?></td>
<td><?= $inv['single_use'] ? 'Sí' : 'No' ?></td>
<td>
<form method="post" action="?form=delete" style="display:inline">
<input type="hidden" name="invitation_code" value="<?= htmlspecialchars($inv['code']) ?>">
<button type="submit" class="btn btn-danger btn-sm">Borrar</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<?php
break;
}
require_once "_incl/post-body.php";
require_once "_incl/post-body.php";