Skip to content

Conversation

@lomeliDev
Copy link

This PR introduces significant improvements to the core message handling, extends the Dashboard UI, and enhances the local development environment with Docker.

🚀 Key Changes

  1. Backend & Core Logic
    Smart JID Validation: Implemented a robust validation mechanism using IsOnWhatsApp.

Fix: Automatically corrects regional JID formats before sending messages. This specifically resolves issues with Mexican numbers (converting +52 to +521 when required by WhatsApp) and prevents sending messages to non-existent numbers.

New Endpoint (POST /misc/phone): Added a utility endpoint to validate and format phone numbers (returns the correct JID and existence status) without sending a message.

Code Cleanup: Fixed variable shadowing in handlers.go (MarkRead) and standardized logging to use zerolog for better consistency.

  1. Frontend (Dashboard)
    Session Management: Added a new "Session" widget section to the Dashboard.

Features: UI cards for Connect, Disconnect, and Logout.

UX: Implemented Semantic UI confirmation modals and toast notifications for success/error states.

Logic: Javascript functions now properly handle auth tokens and interact with /session/* endpoints.

  1. Infrastructure (Docker)
    Local S3 (MinIO): Added a minio service to docker-compose.yml (ports 9000/9001) to facilitate local testing of S3 media storage.

Proxy Testing: Added proxy-http (TinyProxy) and proxy-socks (Go-Socks5) services to the compose file to allow easy testing of WuzAPI's proxy configurations in a local environment.

Env Variables: Updated environment configuration to support these new services (RabbitMQ, MinIO, Adminer).

🧪 How to Test
Phone Validation:

POST to /misc/phone with a payload like {"phone": "521234567890"}.

Response should return the corrected JID (e.g., adding the '1' for Mexico if needed) and exists: true.

Dashboard:

Go to /dashboard.

Use the new Session cards to Disconnect and Reconnect the session.

Docker Infra:

Run docker-compose up -d minio proxy-http.

Verify MinIO console at http://localhost:9001.

📸 Screenshots

Captura de pantalla 2025-12-27 a la(s) 1 43 04 p m Captura de pantalla 2025-12-27 a la(s) 1 46 20 p m

Checklist
[x] Code compiles correctly

[x] New endpoint /misc/phone tested

[x] Dashboard UI functional

[x] Docker containers build and run

- Refactor RabbitMQ configuration to use environment variables.
- Expose PostgreSQL ports to the host for local access.
- Update .env.sample with new configuration options.
- Add logic to switch between local relative path and binary path.
- Add GO_ENV variable to .env to enable local development mode with Air.
…sOnWhatsApp" -m "- Replace basic parsing with online WhatsApp API check.\n- Automatically normalize regional formats (e.g., fix Mexico +52 to +521).\n- Validate existence of the number before sending.
- Introduce 'GetFormattedPhone' handler to verify numbers via WhatsApp API.\n- Register POST /misc/phone route.\n- Allow JID normalization (e.g., Mexico +521) without sending messages.
- Add MinIO container exposing ports 9000 (API) and 9001 (Console).\n- Configure default credentials and persistence volume.\n- Enable S3-compatible media storage for WuzAPI.
… logout)

- Add Session section cards to dashboard.\n- Implement Semantic UI modals for session actions.\n- Add JS logic to consume /session/* endpoints with auth token.\n- Fix modal buttons with 'approve' class for event triggering.
- Add 'proxy-http' service (TinyProxy) on port 8888 for HTTP testing.\n- Add 'proxy-socks' service (Go-Socks5) on port 1080 for SOCKS5 auth testing.\n- Facilitate local development and testing of WuzAPI proxy configurations.
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @lomeliDev, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a suite of enhancements across the application's core, frontend, and development infrastructure. It significantly improves the reliability of message handling by validating recipient phone numbers against the WhatsApp network, provides users with direct control over their session status through an intuitive dashboard interface, and expands the local development environment with essential tools for testing and debugging various integrations and proxy configurations.

Highlights

  • Smart JID Validation & Phone Utility Endpoint: Implemented a robust JID validation mechanism using WhatsApp's IsOnWhatsApp API, which automatically corrects regional JID formats (e.g., adding '1' for Mexican numbers) and prevents sending messages to non-existent numbers. A new utility endpoint, POST /misc/phone, has been added to validate and format phone numbers without sending a message.
  • Dashboard Session Management UI: The Dashboard UI now includes a new 'Session' widget section with cards for 'Connect', 'Disconnect', and 'Logout'. These features are backed by JavaScript functions that handle authentication tokens and interact with the /session/* API endpoints, providing confirmation modals and toast notifications for user feedback.
  • Enhanced Local Development Infrastructure: The docker-compose.yml configuration has been significantly expanded to include services for local S3 (MinIO), a database management tool (Adminer), a webhook tester, an HTTP proxy (TinyProxy), and a SOCKS5 proxy (Go-Socks5). This facilitates comprehensive local testing of various WuzAPI features and configurations.
  • Core Logic Refinements: Variable shadowing issues in handlers.go (specifically in the MarkRead function) have been resolved, and logging has been standardized across the backend to use zerolog for improved consistency and better log analysis.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces significant enhancements to the application's environment, Docker Compose setup, and core message handling logic, along with new UI features. The changes include adding GO_ENV, ADMINER_PORT, and MinIO configuration to .env.sample, and including tmp in .gitignore. The docker-compose.yml file is updated to make RabbitMQ configuration dynamic, expose the database port, and integrate new services like MinIO, Adminer, Webhook Tester, and HTTP/SOCKS5 proxies. Crucially, the handlers.go file refactors the validateMessageFields function to accept a whatsmeow.Client for real-time WhatsApp number validation and JID correction, applying this new logic across numerous message and group management handlers. A new /misc/phone API endpoint and corresponding GetFormattedPhone handler are added to leverage this validation. The routes.go file updates static file serving logic and registers the new /misc/phone route. The OpenAPI specification in static/api/spec.yml is updated for the new endpoint. Finally, the dashboard UI (static/dashboard/index.html and static/dashboard/js/app.js) gains new 'Session' management features, allowing users to connect, disconnect, and log out from WhatsApp sessions via dedicated buttons and modals. Review comments suggest translating Spanish comments in GetFormattedPhone to English, improving error handling for non-existent phone numbers in the GetFormattedPhone endpoint to return a structured {"exists": false} response, and extracting duplicated API token retrieval logic in app.js into a reusable helper function.

Comment on lines +6520 to +6556
// Necesitamos el cliente conectado para preguntar a la API de WhatsApp
client := clientManager.GetWhatsmeowClient(txtid)
if client == nil {
s.Respond(w, r, http.StatusInternalServerError, errors.New("no session found"))
return
}

decoder := json.NewDecoder(r.Body)
var t phoneStruct
err := decoder.Decode(&t)
if err != nil {
s.Respond(w, r, http.StatusBadRequest, errors.New("could not decode Payload"))
return
}

if t.Phone == "" {
s.Respond(w, r, http.StatusBadRequest, errors.New("missing Phone in Payload"))
return
}

// Usamos la función mágica que ya creamos/arreglamos
// Pasamos nil en stanza y participant porque no es un mensaje real, solo queremos validar el JID
jid, err := validateMessageFields(client, t.Phone, nil, nil)

if err != nil {
// Si falla (ej. el número no existe en WhatsApp), devolvemos error
s.Respond(w, r, http.StatusBadRequest, fmt.Errorf("validation failed: %v", err))
return
}

// Preparamos la respuesta con los datos limpios
response := map[string]interface{}{
"input": t.Phone, // Lo que enviaste (ej: 5233...)
"jid": jid.String(), // El JID completo (ej: 52133...@s.whatsapp.net)
"number": jid.User, // Solo el número corregido (ej: 52133...)
"exists": true, // Confirmación de que existe
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This function contains several comments in Spanish. To maintain consistency with the rest of the codebase, which is primarily in English, it's recommended to translate these comments to English. This improves maintainability for all contributors.

Comment on lines +6544 to +6548
if err != nil {
// Si falla (ej. el número no existe en WhatsApp), devolvemos error
s.Respond(w, r, http.StatusBadRequest, fmt.Errorf("validation failed: %v", err))
return
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The error handling for a non-existent phone number returns a generic error message. For a better and more consistent API experience, it would be beneficial to return a structured JSON response indicating the number does not exist, similar to the success case. You can check for the specific error no account exists from validateMessageFields and respond with {"exists": false}.

Suggested change
if err != nil {
// Si falla (ej. el número no existe en WhatsApp), devolvemos error
s.Respond(w, r, http.StatusBadRequest, fmt.Errorf("validation failed: %v", err))
return
}
if err != nil {
if err.Error() == "no account exists" {
response := map[string]interface{}{
"input": t.Phone,
"exists": false,
}
responseJson, _ := json.Marshal(response)
s.Respond(w, r, http.StatusOK, string(responseJson))
return
}
s.Respond(w, r, http.StatusBadRequest, fmt.Errorf("validation failed: %v", err))
return
}

Comment on lines +22 to +31
const rawToken = localStorage.getItem('token');
let apiToken = "";
try {
if (rawToken) {
apiToken = JSON.parse(rawToken).value;
}
} catch (e) {
showError("Error parsing auth token");
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The logic for retrieving and parsing the API token from localStorage is duplicated across the doConnect, doDisconnect, and doLogout functions. Consider extracting this into a helper function to reduce redundancy and improve maintainability. For example:

function getApiToken() {
  const rawToken = localStorage.getItem('token');
  if (!rawToken) {
    showError("Auth token not found");
    return null;
  }
  try {
    return JSON.parse(rawToken).value;
  } catch (e) {
    showError("Error parsing auth token");
    return null;
  }
}

Then you can simplify the calling functions to const apiToken = getApiToken();.

@lomeliDev lomeliDev closed this Dec 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant