docs: harden demo email migration checks
This commit is contained in:
@@ -73,10 +73,10 @@ Todas usan la contraseña **`demo1234`**.
|
|||||||
| Dueño | `[email protected]` | `demo1234` | Daniela Reyes |
|
| Dueño | `[email protected]` | `demo1234` | Daniela Reyes |
|
||||||
| Empleado | `[email protected]` | `demo1234` | Valentina Cruz |
|
| Empleado | `[email protected]` | `demo1234` | Valentina Cruz |
|
||||||
| Empleado | `[email protected]` | `demo1234` | Mateo Herrera |
|
| Empleado | `[email protected]` | `demo1234` | Mateo Herrera |
|
||||||
| Empleado | `sofia.rami[email protected]` | `demo1234` | Sofía Ramírez |
|
| Empleado | `sofía.ramí[email protected]` | `demo1234` | Sofía Ramírez |
|
||||||
| Empleado | `[email protected]` | `demo1234` | Diego Castillo |
|
| Empleado | `[email protected]` | `demo1234` | Diego Castillo |
|
||||||
| Empleado | `[email protected]` | `demo1234` | Isabela Torres |
|
| Empleado | `[email protected]` | `demo1234` | Isabela Torres |
|
||||||
| Empleado | `carolina.me[email protected]` | `demo1234` | Carolina Méndez |
|
| Empleado | `carolina.mé[email protected]` | `demo1234` | Carolina Méndez |
|
||||||
|
|
||||||
> En la pantalla de login aparecen botones de acceso rápido a las cuentas demo,
|
> En la pantalla de login aparecen botones de acceso rápido a las cuentas demo,
|
||||||
> y desde la barra lateral puedes cambiar de cuenta con **“Ver como…”**.
|
> y desde la barra lateral puedes cambiar de cuenta con **“Ver como…”**.
|
||||||
|
|||||||
@@ -4,14 +4,14 @@
|
|||||||
|
|
||||||
**Goal:** Standardize demo accounts on `@agendamax.demo`, make every demo login explicit in the README, and migrate local and Coolify data without deleting records.
|
**Goal:** Standardize demo accounts on `@agendamax.demo`, make every demo login explicit in the README, and migrate local and Coolify data without deleting records.
|
||||||
|
|
||||||
**Architecture:** Keep the current simple demo authentication and replace only the two platform-owned demo email constants plus their test fixtures. Apply one targeted, idempotent SQLite `UPDATE` to existing local and production users; appointments, businesses, and related records remain unchanged. Deploy the source through the existing GitHub mirror and verify the live app through health and login endpoints.
|
**Architecture:** Keep the current simple demo authentication and replace only the two platform-owned demo email constants plus their test fixtures. Apply one targeted, idempotent SQLite `UPDATE` to the exact existing admin and owner rows; appointments, businesses, and related records remain unchanged. Deploy the source through the existing GitHub mirror and verify the live app through health and login endpoints.
|
||||||
|
|
||||||
**Tech Stack:** React/Vite, Express, TypeScript, Node 22 `node:sqlite`, SQLite, PowerShell/OpenSSH, Coolify v4.1.2.
|
**Tech Stack:** React/Vite, Express, TypeScript, Node 22 `node:sqlite`, SQLite, PowerShell/OpenSSH, Coolify v4.1.2.
|
||||||
|
|
||||||
## Global Constraints
|
## Global Constraints
|
||||||
|
|
||||||
- Keep the demo password exactly `demo1234`.
|
- Keep the demo password exactly `demo1234`.
|
||||||
- Replace active `@agendapro.demo` references with `@agendamax.demo`; retain the old domain only in migration documentation or historical records.
|
- Replace active `@agendapro.demo` references with `@agendamax.demo`; retain the old domain only in explicit negative-login assertions or migration-history documentation.
|
||||||
- Do not reset or delete local or production business, appointment, client, employee, ticket, or review data.
|
- Do not reset or delete local or production business, appointment, client, employee, ticket, or review data.
|
||||||
- Never commit `.env.local.ps1` or print token values.
|
- Never commit `.env.local.ps1` or print token values.
|
||||||
- Production deployment must finish before the production data migration is run.
|
- Production deployment must finish before the production data migration is run.
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
- Modify: `server/scripts/seed.ts:315-334` — use the new admin and owner emails for fresh databases.
|
- Modify: `server/scripts/seed.ts:315-334` — use the new admin and owner emails for fresh databases.
|
||||||
- Modify: `src/pages/LoginPage.tsx:8-15` — use the new default development login.
|
- Modify: `src/pages/LoginPage.tsx:8-15` — use the new default development login.
|
||||||
- Modify: `admin-test.mjs`, `e2e-test.mjs`, `e2e-full.mjs`, `server/scripts/booking-e2e.mjs`, `visual-audit.mjs`, and any other active file returned by `rg -l 'agendapro\.demo'` — keep automated login fixtures aligned.
|
- Modify: `admin-test.mjs`, `e2e-test.mjs`, `e2e-full.mjs`, `server/scripts/booking-e2e.mjs`, `visual-audit.mjs`, and any other active file returned by `rg -l 'agendapro\.demo'` — keep automated login fixtures aligned.
|
||||||
- Modify: active plan/report documents containing executable login examples; leave the new migration specification as the historical explanation of the old domain.
|
- Modify: active plan/report documents containing executable login examples; keep old-domain examples only as migration-history documentation or explicit negative-login assertions.
|
||||||
|
|
||||||
**Interfaces:**
|
**Interfaces:**
|
||||||
- Fresh seed continues to create users with the existing schema and password.
|
- Fresh seed continues to create users with the existing schema and password.
|
||||||
@@ -41,7 +41,7 @@ Run:
|
|||||||
rg -n --glob '!node_modules' --glob '!data/**' 'agendapro\.demo|demo1234' .
|
rg -n --glob '!node_modules' --glob '!data/**' 'agendapro\.demo|demo1234' .
|
||||||
```
|
```
|
||||||
|
|
||||||
Expected: references are limited to the known seed, login defaults, tests, README, and historical documentation; no secret token values are printed.
|
Expected: references are limited to the known seed, login defaults, tests, README, explicit negative-login assertions, and migration-history documentation; no secret token values are printed.
|
||||||
|
|
||||||
- [ ] **Step 2: Update the README credentials table first**
|
- [ ] **Step 2: Update the README credentials table first**
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ Run:
|
|||||||
rg -n --glob '!node_modules' --glob '!data/**' --glob '!docs/superpowers/specs/**' 'agendapro\.demo' .
|
rg -n --glob '!node_modules' --glob '!data/**' --glob '!docs/superpowers/specs/**' 'agendapro\.demo' .
|
||||||
```
|
```
|
||||||
|
|
||||||
Expected: no output, except explicitly retained historical migration notes if they are included by the chosen glob.
|
Expected: no output from application code or positive fixtures; the intentional old-domain negative-login assertions and migration-history documents are the only allowed matches.
|
||||||
|
|
||||||
### Task 2: Migrate the Local SQLite Demo Users
|
### Task 2: Migrate the Local SQLite Demo Users
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ Expected: no output, except explicitly retained historical migration notes if th
|
|||||||
|
|
||||||
**Interfaces:**
|
**Interfaces:**
|
||||||
- Uses the existing `server/db.ts` connection and `users` table.
|
- Uses the existing `server/db.ts` connection and `users` table.
|
||||||
- Produces updated admin/owner email rows while preserving all row counts outside `users.email`.
|
- Produces updated exact admin/owner email rows while preserving all row counts outside `users.email`.
|
||||||
|
|
||||||
- [ ] **Step 1: Confirm the local database and take a count snapshot**
|
- [ ] **Step 1: Confirm the local database and take a count snapshot**
|
||||||
|
|
||||||
@@ -103,22 +103,30 @@ node --import tsx --input-type=module -e "import { db } from './server/db.ts'; c
|
|||||||
|
|
||||||
Expected: the local database exists or the command reports that a fresh seed is required; existing appointment/client counts are recorded before mutation.
|
Expected: the local database exists or the command reports that a fresh seed is required; existing appointment/client counts are recorded before mutation.
|
||||||
|
|
||||||
|
Before updating, inspect the exact old/new admin and owner rows from the `users`
|
||||||
|
snapshot. For either role, if its target email already exists while its matching
|
||||||
|
old-domain row also exists, stop and resolve that unique-email collision instead
|
||||||
|
of running the update. If no old-domain rows exist and the new rows are present,
|
||||||
|
record a safe no-op and skip the update.
|
||||||
|
|
||||||
- [ ] **Step 2: Apply the idempotent local email update**
|
- [ ] **Step 2: Apply the idempotent local email update**
|
||||||
|
|
||||||
Run:
|
Run:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
node --import tsx --input-type=module -e "import { db } from './server/db.ts'; const r = db.prepare(\"UPDATE users SET email = REPLACE(email, '@agendapro.demo', '@agendamax.demo') WHERE email LIKE '%@agendapro.demo'\").run(); console.log(JSON.stringify(r));"
|
node --% --import tsx --input-type=module -e "import { db } from './server/db.ts'; const r = db.prepare(\"UPDATE users SET email = CASE email WHEN '[email protected]' THEN '[email protected]' WHEN 'owner@agendapro.demo' THEN 'owner@agendamax.demo' END WHERE (email = '[email protected]' AND role = 'admin') OR (email = '[email protected]' AND role = 'owner')\").run(); console.log(JSON.stringify(r));"
|
||||||
```
|
```
|
||||||
|
|
||||||
Expected: only the old-domain demo user rows are changed; running the same command again reports zero additional changes.
|
Expected: only the exact email/role pairs are changed; an old-domain employee or
|
||||||
|
other role is untouched. Running the same command again reports zero additional
|
||||||
|
changes.
|
||||||
|
|
||||||
- [ ] **Step 3: Verify local credentials and preservation**
|
- [ ] **Step 3: Verify local credentials and preservation**
|
||||||
|
|
||||||
Run:
|
Run:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
node --import tsx --input-type=module -e "import { db } from './server/db.ts'; console.log(JSON.stringify({demo: db.prepare(\"SELECT email,role,password FROM users WHERE email LIKE '%@agendamax.demo' ORDER BY role\").all(), appointments: db.prepare('SELECT COUNT(*) AS c FROM appointments').get(), clients: db.prepare('SELECT COUNT(*) AS c FROM clients').get()}));"
|
node --% --import tsx --input-type=module -e "import { db } from './server/db.ts'; console.log(JSON.stringify({demo: db.prepare(\"SELECT email,role,password FROM users WHERE email IN ('[email protected]','owner@agendamax.demo') ORDER BY role\").all(), appointments: db.prepare('SELECT COUNT(*) AS c FROM appointments').get(), clients: db.prepare('SELECT COUNT(*) AS c FROM clients').get()}));"
|
||||||
```
|
```
|
||||||
|
|
||||||
Expected: `[email protected]` and `[email protected]` use `demo1234`; appointment and client counts match the snapshot.
|
Expected: `[email protected]` and `[email protected]` use `demo1234`; appointment and client counts match the snapshot.
|
||||||
@@ -148,7 +156,12 @@ npm run test:admin
|
|||||||
npm run test:e2e
|
npm run test:e2e
|
||||||
```
|
```
|
||||||
|
|
||||||
Expected: all existing checks pass with the new admin/owner emails. If a test requires a running dev server, start the repository's documented server first without resetting the database.
|
Expected: typecheck, build, unit, admin, and non-slot authentication checks pass. On the
|
||||||
|
already-migrated local database, `businesses.working_hours` and employee
|
||||||
|
`working_hours` are null, so the slot-dependent e2e/booking assertions may fail
|
||||||
|
before booking (including their existing downstream dereference); record this
|
||||||
|
pre-existing limitation as unrelated to the email change and do not change
|
||||||
|
scheduling data for this task.
|
||||||
|
|
||||||
- [ ] **Step 3: Inspect the final diff**
|
- [ ] **Step 3: Inspect the final diff**
|
||||||
|
|
||||||
@@ -216,28 +229,38 @@ Use the existing Proxmox SSH chain to run inside LXC 102:
|
|||||||
```sh
|
```sh
|
||||||
container=$(docker ps --format '{{.ID}} {{.Names}}' | awk '$2 ~ /agendamax|s30f7/ {print $1; exit}')
|
container=$(docker ps --format '{{.ID}} {{.Names}}' | awk '$2 ~ /agendamax|s30f7/ {print $1; exit}')
|
||||||
test -n "$container" || { echo 'AgendaMax container not found' >&2; exit 1; }
|
test -n "$container" || { echo 'AgendaMax container not found' >&2; exit 1; }
|
||||||
docker exec "$container" node -e "const {DatabaseSync}=require('node:sqlite'); const db=new DatabaseSync('/app/data/agendapro.db'); console.log(db.prepare(\"SELECT id,email,role FROM users WHERE email LIKE '%@agendapro.demo' OR email LIKE '%@agendamax.demo' ORDER BY id\").all())"
|
docker exec "$container" node -e "const {DatabaseSync}=require('node:sqlite'); const db=new DatabaseSync('/app/data/agendapro.db'); console.log(db.prepare(\"SELECT id,email,role FROM users WHERE email IN ('admin@agendapro.demo','[email protected]','[email protected]','owner@agendamax.demo') ORDER BY id\").all())"
|
||||||
```
|
```
|
||||||
|
|
||||||
Expected: the app is running the new image and the current admin/owner rows are visible before mutation.
|
Expected: the app is running the new image and the current exact admin/owner
|
||||||
|
rows are visible before mutation. If an old row and its matching new row both
|
||||||
|
exist, stop and resolve the unique-email collision before updating.
|
||||||
|
|
||||||
- [ ] **Step 2: Apply the production email update**
|
- [ ] **Step 2: Apply the production email update**
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
docker exec "$container" node -e "const {DatabaseSync}=require('node:sqlite'); const db=new DatabaseSync('/app/data/agendapro.db'); const r=db.prepare(\"UPDATE users SET email=REPLACE(email, '@agendapro.demo', '@agendamax.demo') WHERE email LIKE '%@agendapro.demo'\").run(); console.log(r)"
|
docker exec "$container" node -e "const {DatabaseSync}=require('node:sqlite'); const db=new DatabaseSync('/app/data/agendapro.db'); const r=db.prepare(\"UPDATE users SET email=CASE email WHEN 'admin@agendapro.demo' THEN 'admin@agendamax.demo' WHEN '[email protected]' THEN '[email protected]' END WHERE (email='[email protected]' AND role='admin') OR (email='[email protected]' AND role='owner')\").run(); console.log(r)"
|
||||||
```
|
```
|
||||||
|
|
||||||
Expected: the admin and owner rows move to `@agendamax.demo`; no business or appointment rows are deleted.
|
Expected: only the matching admin and owner rows move to `@agendamax.demo`; no
|
||||||
|
old-domain employee or other role is rewritten, and no business or appointment
|
||||||
|
rows are deleted.
|
||||||
|
|
||||||
|
If the preflight query shows an old row alongside its matching new row, stop and
|
||||||
|
resolve the unique-email collision. If it shows no old-domain rows because the
|
||||||
|
deployment seeded a fresh database with the new domain, record the update as a
|
||||||
|
safe no-op and skip it.
|
||||||
|
|
||||||
- [ ] **Step 3: Verify live health and credentials**
|
- [ ] **Step 3: Verify live health and credentials**
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
Invoke-RestMethod https://agendamax.urieljareth.org/api/health
|
$health = Invoke-RestMethod https://agendamax.urieljareth.org/api/health
|
||||||
Invoke-RestMethod https://agendamax.urieljareth.org/api/auth/login -Method Post -ContentType 'application/json' -Body '{"email":"[email protected]","password":"demo1234"}'
|
$admin = Invoke-RestMethod https://agendamax.urieljareth.org/api/auth/login -Method Post -ContentType 'application/json' -Body '{"email":"[email protected]","password":"demo1234"}'
|
||||||
Invoke-RestMethod https://agendamax.urieljareth.org/api/auth/login -Method Post -ContentType 'application/json' -Body '{"email":"[email protected]","password":"demo1234"}'
|
$owner = Invoke-RestMethod https://agendamax.urieljareth.org/api/auth/login -Method Post -ContentType 'application/json' -Body '{"email":"[email protected]","password":"demo1234"}'
|
||||||
|
Write-Host "health_ok=$($health.ok) admin_role=$($admin.user.role) admin_token_present=$([bool]$admin.token) owner_role=$($owner.user.role) owner_token_present=$([bool]$owner.token)"
|
||||||
```
|
```
|
||||||
|
|
||||||
Expected: health returns `ok: true`; both logins return tokens and the correct roles.
|
Expected: health returns `ok: true`; both logins return the correct roles. Only boolean token-presence values are printed, never token contents.
|
||||||
|
|
||||||
- [ ] **Step 4: Verify the deployed page and repository state**
|
- [ ] **Step 4: Verify the deployed page and repository state**
|
||||||
|
|
||||||
|
|||||||
@@ -8,22 +8,25 @@ data, and the deployed Coolify instance.
|
|||||||
|
|
||||||
## Scope
|
## Scope
|
||||||
|
|
||||||
- Replace active `@agendapro.demo` references in application code, tests,
|
- Replace active positive `@agendapro.demo` references in application code,
|
||||||
README, and operational documentation with `@agendamax.demo`; retain the old
|
tests, README, and operational documentation with `@agendamax.demo`; retain
|
||||||
domain only where this specification documents the migration itself.
|
the old domain only in migration-history documentation or explicit
|
||||||
|
negative-login assertions.
|
||||||
- Keep the existing demo password `demo1234` and expose it clearly beside each
|
- Keep the existing demo password `demo1234` and expose it clearly beside each
|
||||||
demo account in `README.md`.
|
demo account in `README.md`.
|
||||||
- Update the local SQLite demo users without deleting businesses, appointments,
|
- Update the local SQLite demo users without deleting businesses, appointments,
|
||||||
or other seeded records.
|
or other seeded records.
|
||||||
- Deploy the code to the existing AgendaMax Coolify application.
|
- Deploy the code to the existing AgendaMax Coolify application.
|
||||||
- Update the persistent production SQLite users for the admin and owner accounts
|
- Update the persistent production SQLite users for the exact admin and owner
|
||||||
with a targeted SQL change, preserving all other data.
|
accounts with a targeted SQL change, preserving all other data.
|
||||||
|
|
||||||
## Data Flow
|
## Data Flow
|
||||||
|
|
||||||
Fresh databases receive the new admin and owner emails from `server/scripts/seed.ts`
|
Fresh databases receive the new admin and owner emails from `server/scripts/seed.ts`
|
||||||
and `server/index.ts`. Existing local and production databases are migrated by
|
and `server/index.ts`. Existing local and production databases are migrated by
|
||||||
updating only `users.email` values whose domain is `agendapro.demo`.
|
updating only these two exact rows: `admin@agendapro.demo` and
|
||||||
|
`[email protected]`. If the active database is fresh and already contains the
|
||||||
|
new emails, the SQL update is a no-op and is skipped.
|
||||||
|
|
||||||
The employee accounts already use their business domains and are not changed.
|
The employee accounts already use their business domains and are not changed.
|
||||||
Login verification uses `POST /api/auth/login` with the documented password and
|
Login verification uses `POST /api/auth/login` with the documented password and
|
||||||
@@ -34,8 +37,11 @@ does not expose password values through the API.
|
|||||||
1. Run the local checks and build.
|
1. Run the local checks and build.
|
||||||
2. Push the change to the AgendaMax source mirror used by Coolify.
|
2. Push the change to the AgendaMax source mirror used by Coolify.
|
||||||
3. Trigger and monitor a Coolify deployment.
|
3. Trigger and monitor a Coolify deployment.
|
||||||
4. Run a targeted SQLite update inside the persistent production app volume:
|
4. Inspect the exact old/new admin and owner rows. If an old row and its matching
|
||||||
`UPDATE users SET email = REPLACE(email, '@agendapro.demo', '@agendamax.demo') WHERE email LIKE '%@agendapro.demo';`
|
new row both exist, stop and resolve the unique-email collision. If no old
|
||||||
|
rows exist and the new rows are present, record a safe no-op. Otherwise run a
|
||||||
|
targeted SQLite update inside the persistent production app volume:
|
||||||
|
`UPDATE users SET email = CASE email WHEN '[email protected]' THEN '[email protected]' WHEN '[email protected]' THEN '[email protected]' END WHERE (email = '[email protected]' AND role = 'admin') OR (email = '[email protected]' AND role = 'owner');`
|
||||||
5. Verify health, login for admin and owner, and the production page.
|
5. Verify health, login for admin and owner, and the production page.
|
||||||
|
|
||||||
If deployment fails, do not run the production SQL migration; inspect the
|
If deployment fails, do not run the production SQL migration; inspect the
|
||||||
@@ -43,11 +49,25 @@ deployment first and use the existing Coolify rollback procedure.
|
|||||||
|
|
||||||
## Acceptance Criteria
|
## Acceptance Criteria
|
||||||
|
|
||||||
- `rg "agendapro\\.demo"` returns no active application, test, or README
|
- `rg "agendapro\\.demo"` returns no active positive application, test, or
|
||||||
references (historical notes may be explicitly excluded if any are retained).
|
README references; only explicit negative-login assertions and
|
||||||
|
migration-history documentation may retain the old domain.
|
||||||
- README lists every demo account with email and `demo1234`.
|
- README lists every demo account with email and `demo1234`.
|
||||||
- Fresh seed creates `[email protected]` and `[email protected]`.
|
- Fresh seed creates `[email protected]` and `[email protected]`.
|
||||||
- Existing local data contains the new emails and retains its records.
|
- Existing local data contains the new emails and retains its records.
|
||||||
- Coolify deployment finishes successfully.
|
- Coolify deployment finishes successfully.
|
||||||
- Production `/api/health` returns `ok: true`.
|
- Production `/api/health` returns `ok: true`.
|
||||||
- Production admin and owner logins succeed with `demo1234`.
|
- Production admin and owner logins succeed with `demo1234`.
|
||||||
|
- Production admin and owner logins using the old domain return `401`.
|
||||||
|
- README employee emails exactly match the accent-preserving emails generated by the seed.
|
||||||
|
- The targeted SQL pairs each old email with its matching role and never rewrites
|
||||||
|
another old-domain user.
|
||||||
|
|
||||||
|
## Known Verification Limitation
|
||||||
|
|
||||||
|
The already-migrated local database has null `businesses.working_hours` and
|
||||||
|
employee `working_hours` values. As a result, the slot-dependent `test:e2e` and
|
||||||
|
`test:booking` assertions can fail before booking, including their existing
|
||||||
|
downstream dereference. This is a pre-existing scheduling-data limitation,
|
||||||
|
unrelated to the email-domain change; do not change working-hours data for this
|
||||||
|
task.
|
||||||
|
|||||||
@@ -27,6 +27,18 @@ const me = await req("/api/auth/demo-users"); check("demo-users >= 8", me.json.u
|
|||||||
const owner = await login("[email protected]"); check("owner login", !!owner.token && owner.user.role === "owner");
|
const owner = await login("[email protected]"); check("owner login", !!owner.token && owner.user.role === "owner");
|
||||||
const empLogin = await login("[email protected]"); check("employee login", empLogin.user.role === "employee" && empLogin.user.employee_id);
|
const empLogin = await login("[email protected]"); check("employee login", empLogin.user.role === "employee" && empLogin.user.employee_id);
|
||||||
const adminLogin = await login("[email protected]"); check("admin login", adminLogin.user.role === "admin");
|
const adminLogin = await login("[email protected]"); check("admin login", adminLogin.user.role === "admin");
|
||||||
|
const legacyAdmin = await req("/api/auth/login", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ email: "[email protected]", password: "demo1234" }),
|
||||||
|
});
|
||||||
|
check("[email protected] legacy login rejected", legacyAdmin.status === 401, `status=${legacyAdmin.status}`);
|
||||||
|
const legacyOwner = await req("/api/auth/login", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ email: "[email protected]", password: "demo1234" }),
|
||||||
|
});
|
||||||
|
check("[email protected] legacy login rejected", legacyOwner.status === 401, `status=${legacyOwner.status}`);
|
||||||
const t = owner.token, eT = empLogin.token, aT = adminLogin.token;
|
const t = owner.token, eT = empLogin.token, aT = adminLogin.token;
|
||||||
|
|
||||||
// /me fix
|
// /me fix
|
||||||
|
|||||||
Reference in New Issue
Block a user