Skip to content

Add Recebimento e Conferência web app, Supabase schema and TeraBox backend - #3

Open
hhhthiti wants to merge 6 commits into
mainfrom
codex/integrate-supabase-user-authentication-system-ck34ep
Open

Add Recebimento e Conferência web app, Supabase schema and TeraBox backend#3
hhhthiti wants to merge 6 commits into
mainfrom
codex/integrate-supabase-user-authentication-system-ck34ep

Conversation

@hhhthiti

@hhhthiti hhhthiti commented Apr 3, 2026

Copy link
Copy Markdown
Owner

Motivation

  • Provide a complete MVP for NF-e receiving and conference with separated ADM/Operação flows, XML upload/parse, PDF/label generation and NQ export.
  • Add a small backend to persist TXT logs to TeraBox and a Supabase schema to enable running the app in both hosted and local modes.

Description

  • Add frontend: index.html, styles.css and main.js implementing UI, Supabase integration (with localStorage fallback when schema is missing), XML parsing, PDF generation (jsPDF + html2canvas), NQ line export, logs, chat and role-based views.
  • Add backend: server.js and package.json providing TeraBox OAuth flow (/auth/terabox, /auth/terabox/callback) and an endpoint to upload TXT logs (/exportar-txt-terabox), plus .env.example with required env vars: TERABOX_CLIENT_ID, TERABOX_CLIENT_SECRET, TERABOX_REDIRECT_URI, TERABOX_TARGET_DIR and PORT.
  • Add database schema: supabase-schema.sql containing idempotent CREATE TABLE IF NOT EXISTS statements for usuarios, notas, conferencias, logs, chats and nq_reports, and migration-safe ALTER TABLE clauses.
  • Add documentation: README.md with setup and run instructions (including cp .env.example .env, npm install and npm start guidance) and details about running only on Render or with localhost backend URL.

Testing

  • No automated tests were added or executed as part of this change.

Codex Task

@vercel

vercel Bot commented Apr 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
recebimento Ready Ready Preview, Comment Apr 4, 2026 11:25pm

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 24818b767e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread main.js
Comment on lines +495 to +499
pl2,
paletes_total: paletesTotal,
inicio_carga: inicioCarga,
fim_carga: fimCarga,
reabertura_finalizada: !!nota.reaberta,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Persist conference fields in schema before inserting

The conference submit path writes pl2, paletes_total, inicio_carga, fim_carga, and reabertura_finalizada into conferencias, but supabase-schema.sql does not create those columns. In Supabase mode this insert will fail with a missing-column error, and your maybeSwitchToLocalMode logic then downgrades the app to local storage, so conferences stop syncing to the shared database immediately after the first submission.

Useful? React with 👍 / 👎.

Comment thread main.js
Comment on lines +506 to +510
inicio_descarga: inicioCarga,
fim_descarga: fimCarga,
pl2,
paletes_total: paletesTotal,
reaberta: false,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add notas columns used by conference updates

The post-conference update writes fields like inicio_descarga, fim_descarga, pl2, paletes_total, and reaberta to notas, but these columns are absent from the schema file that the README instructs users to apply. In Supabase mode this update fails with column-not-found, which again triggers the local-mode fallback and causes ADM/Operação data divergence instead of persisting workflow state online.

Useful? React with 👍 / 👎.

Comment thread main.js
try {
await dbUpdate('usuarios', { matricula }, { senha: novaSenha });
if (state.mode === 'supabase') {
const { error } = await supabase.functions.invoke('send-email', { body: { to: 'leseliv487@fengnu.com', subject: 'Nova senha', text: `Nova senha: ${novaSenha}` } });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Send recovery email to the requested user

recoverByEmail always sends the new password to a hardcoded address (leseliv487@fengnu.com) instead of the account owner's email. This both leaks credentials to an unrelated recipient and prevents the requesting user from receiving their reset password, effectively breaking email recovery in production.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f4dc69d6d7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread main.js
Comment on lines +267 to +270
await dbInsert('usuarios', {
matricula: f.get('matricula'),
senha: f.get('senha'),
role: f.get('role'),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Block unauthenticated creation of ADM users

The registration flow writes whatever role comes from the public form directly into usuarios without any privilege check, so any visitor can self-register as adm and immediately gain full admin access. This is an authorization bypass in environments where the app is reachable beyond a trusted internal audience.

Useful? React with 👍 / 👎.

Comment thread main.js
Comment on lines +791 to +796
await dbUpdate('usuarios', { matricula }, { senha: novaSenha });
if (state.mode === 'supabase') {
const { error } = await supabase.functions.invoke('send-email', { body: { to: 'leseliv487@fengnu.com', subject: 'Nova senha', text: `Nova senha: ${novaSenha}` } });
if (error) throw error;
}
alert(`Senha resetada. Nova senha: ${novaSenha}`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Require verification before completing email recovery

This reset path updates the password using only matricula and then shows the new password in an alert, which means anyone who knows a matrícula can reset that account and read the new credential immediately. In production this enables straightforward account takeover without proving control of the user’s email.

Useful? React with 👍 / 👎.

Comment thread main.js
Comment on lines +318 to +319
const alreadyExists = await dbSelect('notas', { eq: { numero_nota: nota.numeroNota } });
if ((alreadyExists || []).length) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Deduplicate notes by NF-e key, not invoice number alone

The publish guard treats numero_nota as globally unique, but invoice numbers are typically only unique per emitter. If two suppliers issue the same number, the second valid NF-e is rejected as duplicate and cannot be published, blocking normal receiving operations; use chave_nfe (or emitter+number) for uniqueness.

Useful? React with 👍 / 👎.

Comment thread server.js
Comment on lines +138 to +142
app.post('/exportar-txt-terabox', async (req, res) => {
try {
ensureConfig();
ensureToken();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Authenticate export endpoint before using shared TeraBox token

The upload endpoint has no caller authentication/authorization check; it only verifies that a global in-memory token exists. After any admin completes OAuth once, any client that can reach this backend can POST arbitrary fileName/content and write files into the same TeraBox directory.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8bd9cb2a89

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread main.js
(nota.itens_json || []).forEach((item) => {
const code = normalizeProductCode(item.codigo);
const fardosPorPalete = getFardosPorPalete(code);
conferenciaForm.insertAdjacentHTML('beforeend', `<div class="item"><p><strong>${code}</strong> - ${item.descricao}</p><p class="hint">${fardosPorPalete} fardos por palete</p><label>Quantidade conferida<input type="number" step="0.01" min="0" required name="${code}" /></label><label class="fracao-fardo hidden" data-fracao="${code}">Fardos fracionados (quando conferir em paletes)<input type="number" step="0.01" min="0" name="${code}__fracao" value="0" /></label></div>`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Escape invoice fields before rendering HTML

item.descricao comes directly from uploaded XML and is interpolated into innerHTML without escaping. A crafted NF-e payload (for example with an <img onerror=...> in xProd) will execute script when Operação opens the conference form, creating a stored XSS that can exfiltrate localStorage/session data from any user who views the note.

Useful? React with 👍 / 👎.

Comment thread server.js
Comment on lines +114 to +118
app.get('/auth/terabox/callback', async (req, res) => {
try {
ensureConfig();
const { code } = req.query;
if (!code) return res.status(400).send('Código de autorização não recebido.');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Bind TeraBox OAuth callback to an authenticated session

The callback accepts any code and writes its access token into the global teraboxToken without checking who initiated OAuth or validating a state value. In a public deployment, any external caller can complete OAuth against this backend and overwrite the shared token, redirecting all subsequent exports to their own TeraBox account.

Useful? React with 👍 / 👎.

Comment thread main.js
Comment on lines +284 to +285
const data = await dbSelect('usuarios', { eq: { matricula: f.get('matricula'), senha: f.get('senha') }, single: true });
if (!data) return alert('Matrícula ou senha inválida.');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle unknown credentials without throwing in login flow

login expects dbSelect(..., single: true) to return null for invalid credentials, but Supabase .single() returns an error when no rows match. In Supabase mode, failed login attempts therefore raise an uncaught exception instead of showing the intended "Matrícula ou senha inválida" message, breaking normal authentication UX for wrong-password cases.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8a4de2568b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread main.js
numero_nota: nota.numeroNota,
chave_nfe: nota.chave,
motorista: nota.transporte.motorista,
transportadora: nota.transporte.transportadora,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove unsupported notas field from publish insert

publishInvoice inserts transportadora into notas, but supabase-schema.sql never defines that column in public.notas (table block at lines 12–28). In Supabase mode this makes the first publish fail with a missing-column error, and the error path switches the app to local mode, so shared online synchronization is lost right when ADM tries to publicar a nota.

Useful? React with 👍 / 👎.

Comment thread main.js
const { error } = await supabase.functions.invoke('send-sms', { body: { to: telefone, message: `Nova senha: ${novaSenha}` } });
if (error) throw error;
}
alert(`Senha resetada. Nova senha: ${novaSenha}`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Stop exposing new password in SMS recovery alert

The SMS recovery flow resets the password and then shows the new credential directly in a browser alert, so anyone who knows a user's matrícula and registered phone number can take over the account without proving phone possession. This is especially risky in production because the attacker gets the usable password immediately from the UI.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant