pispas_modules/
lib.rs

1//! # `pispas_modules` — Local Pispas service for Windows POS
2//!
3//! This crate is the **main runtime binary** of the Pispas installer. It runs
4//! as a Windows background service (installed by `pispas-service.exe`) and
5//! exposes a single modular WebSocket endpoint that the Pispas web/desktop
6//! clients talk to for printing, scale reads, RFID reads, card payments,
7//! cash drawer control, kitchen order dispatching, etc.
8//!
9//! The crate is `pispas-modules` on crates (see `Cargo.toml`), but Rust
10//! renames hyphens to underscores so everything imports as `pispas_modules`.
11//!
12//! ## High-level architecture
13//!
14//! ```text
15//!   +----------------------+         +----------------------------+
16//!   | Pispas web/mobile UI |         | Pispas tray-app (Windows)  |
17//!   | (app.unpispas.es)    |         | [pispas-tray-app.exe]      |
18//!   +----------+-----------+         +-------------+--------------+
19//!              |                                   |
20//!              |  wss://local.unpispas.es:5005     |  IPC (named pipe / WS)
21//!              |  (or ws:// fallback)              |
22//!              v                                   v
23//!   +-------------------------------------------------------------+
24//!   |            pispas-modules.exe  (THIS CRATE)                 |
25//!   |  Dual-mode WebSocket server (TLS + plain on same TCP port)  |
26//!   |                                                             |
27//!   |  +---------+  +---------+  +---------+  +---------+  +----+ |
28//!   |  | BASE    |  | PRINT   |  | SCALE   |  | RFID    |  | …  | |
29//!   |  +---------+  +---------+  +---------+  +---------+  +----+ |
30//!   |                                                             |
31//!   |  +------------+  +----------+  +--------+  +-------------+  |
32//!   |  | CASH_GUARD |  | PAYTEF   |  | MYPOS  |  | ORDER_KITC… |  |
33//!   |  +------------+  +----------+  +--------+  +-------------+  |
34//!   |                                                             |
35//!   |  +--------------+   +------------+                          |
36//!   |  | command_view |   | persistence|                          |
37//!   |  +------+-------+   +------------+                          |
38//!   +---------|---------------------------------------------------+
39//!             |
40//!             | spawns a sidecar web server on localhost:9000-ish
41//!             v
42//!   +----------------------------------------+
43//!   | command_viewer HTTP/WS (localhost only)|
44//!   | static HTML UI for ops / diagnostics   |
45//!   +----------------------------------------+
46//! ```
47//!
48//! `pispas_modules` **is the service** — the other binaries in the workspace
49//! orchestrate around it:
50//!
51//! * [`pispas-installer`]            installs this binary as a Windows
52//!                                   service and registers the scheduled
53//!                                   tasks, certificates, firewall rules,
54//!                                   etc.
55//! * [`pispas-service`]              Windows-service wrapper (launches
56//!                                   `pispas-modules.exe` and handles the
57//!                                   Service Control Manager life-cycle).
58//! * [`pispas-elevator`]             UAC-elevated helper (see `CLAUDE.md §6`).
59//! * [`pispas-tray-app`]             Tray-icon app in the system tray that
60//!                                   communicates with this service over a
61//!                                   local IPC channel, shows status and
62//!                                   exposes quick actions.
63//! * [`pispas-configurator-html`]    HTML configurator UI that writes to the
64//!                                   `.env` consumed by `ConfigEnv::load`.
65//! * [`pispas-order-kitchen`]        Dedicated order-kitchen display process
66//!                                   (separate crate; this crate's
67//!                                   `order_kitchen` module is the backend
68//!                                   that feeds it).
69//!
70//! [`pispas-installer`]: ../installer/index.html
71//! [`pispas-service`]:   ../pispas_service/index.html
72//! [`pispas-elevator`]:  ../pispas_elevator/index.html
73//! [`pispas-tray-app`]:  ../pispas_tray_app/index.html
74//! [`pispas-configurator-html`]: ../pispas_configurator_html/index.html
75//! [`pispas-order-kitchen`]: ../pispas_order_kitchen/index.html
76//!
77//! ## Network surface
78//!
79//! Every socket the service opens (all **loopback-only**, never 0.0.0.0):
80//!
81//! | Socket | What it is | Notes |
82//! |---|---|---|
83//! | `local.unpispas.es:5005` | Main dual-mode WebSocket endpoint | TLS + plain on the same TCP port. See [`start_local_server`]. `local.unpispas.es` is an `/etc/hosts`-style entry installed by the installer that resolves to `127.0.0.1`. |
84//! | `<remote>:8765` (out-bound) | Remote gateway client | Only when a `REMOTE_HOST` is configured. See [`start_remote_server`]. |
85//! | `127.0.0.1:8081` (HTTP)  | [`command_viewer`] landing page | Static UI `static/index.html`. Localhost-only. |
86//! | `127.0.0.1:9001` (WS)    | [`command_viewer`] WebSocket | Receives ticket/command events and forwards to subscribed printers. |
87//!
88//! Port numbers are defaults; all are configurable through `ConfigEnv`
89//! (see [`sharing::utils::ConfigEnv`](../sharing/utils/struct.ConfigEnv.html)).
90//!
91//! ## TLS and `local.unpispas.es`
92//!
93//! The service binds to `127.0.0.1` but **clients connect using
94//! `https://` / `wss://local.unpispas.es:<port>`**. This is because:
95//!
96//! * The cert embedded in the binary (`cert.pem` + `key.pem`) is a
97//!   CloudFlare Origin cert issued for `*.unpispas.es`. Clients only trust
98//!   it if the hostname they connect to matches that SAN.
99//! * `local.unpispas.es` is added to the system `hosts` file by the
100//!   installer, resolving to `127.0.0.1` (and also to the LAN IP, so other
101//!   devices on the same network can reach this service).
102//! * Chrome's Private Network Access policy blocks `https` → `http`
103//!   mixed-scheme requests to loopback; serving TLS end-to-end avoids that.
104//!
105//! The dual-mode listener inspects the first byte of each accepted
106//! connection: `0x16` means a TLS ClientHello (wrap in `TlsAcceptor`),
107//! otherwise treat as plain WebSocket. See the detailed flow in
108//! [`utils::start_local_server`].
109//!
110//! ## Message protocol
111//!
112//! Every request is a JSON object with at least:
113//!
114//! ```json
115//! {
116//!   "UUIDV4": "7c3f…",
117//!   "TARGET": "PRINT",
118//!   "ACTION": "..."
119//! }
120//! ```
121//!
122//! The server routes on `TARGET` to one of the modules listed in
123//! [Modules](#modules). The reply is:
124//!
125//! ```json
126//! {
127//!   "SERVICE_NAME":  "<module>",
128//!   "SERVICE_VERS":  "1.0.0",
129//!   "MESSAGE_TYPE":  "RESPONSE",
130//!   "MESSAGE_EXEC":  "SUCCESS" | "FAILURE",
131//!   "MESSAGE_UUID":  "7c3f…",          // echoed from request.UUIDV4
132//!   "MESSAGE_DATA":  <action result>
133//! }
134//! ```
135//!
136//! `MESSAGE_UUID` always echoes the request's `UUIDV4` so clients can
137//! match responses to requests over the same socket (multiple in-flight
138//! requests are allowed).
139//!
140//! ## Modules
141//!
142//! Each public module below is registered in [`utils::load_services`] and
143//! picks a `TARGET` string that clients use to address it.
144//!
145//! | `TARGET`        | Module | What it does |
146//! |---|---|---|
147//! | `BASE`          | [`base`] | Housekeeping: `CHECK` (handshake sanity), `PING` (keep-alive), `VERSION`, `CONFIG`. |
148//! | `PRINT`         | [`printsrvc`] | The whole ESC/POS printing pipeline: discover printers, render HTML → PDF → raster → ESC/POS, send to USB / network / Bluetooth / Windows spooler. Also `getPrinters` returns the list of installed printers on Windows. |
149//! | `SCALE`         | [`scale`] | Reads a COM-port scale (weight, tare, zero). |
150//! | `RFID`          | [`rfid`] | Reads an RFID tag from a configured reader. |
151//! | `CASHGUARD`     | [`cash_guard`] | Cash-drawer control via the CashGuard HTTP API (open, balance, status). |
152//! | `PAYTEF`        | [`paytef`] | Card-payment flow through Paytef terminals. |
153//! | `MYPOS`         | [`mypos`] | Card-payment flow through MyPOS terminals. |
154//! | `ORDER_KITCHEN` | [`order_kitchen`] | Feeds tickets to the [`pispas-order-kitchen`] display app over IPC. |
155//! | `CACHE`         | [`persistence`] | Local key/value blob stored on disk (`file.bin`) so printing survives server hiccups. See [`sharing::PERSISTANCE_FILE_NAME`](../sharing/constant.PERSISTANCE_FILE_NAME.html). |
156//! | — (side-car)    | [`command_viewer`] | Not a TARGET. Spawns an HTTP + WS server on localhost for an in-house ops/diagnostics UI (`static/`) that lets support staff see incoming commands in real time and re-send them to printers. |
157//!
158//! ## Service and tray-app interaction
159//!
160//! The tray-app (`pispas-tray-app.exe`) is a stand-alone Windows program
161//! that sits in the system tray. It connects to **this** service over the
162//! same WebSocket (`local.unpispas.es:5005`) and issues `BASE` / `CHECK`
163//! pings to show service health, exposes a right-click menu to open the
164//! configurator, and surfaces notifications when a printer fails.
165//!
166//! The configurator (`pispas-configurator-html.exe`) is a one-shot program
167//! launched from the tray: it shows a local HTML UI (bundled in
168//! `binaries/configurator_html/dist`), reads/writes the `.env` that backs
169//! [`sharing::utils::ConfigEnv`](../sharing/utils/struct.ConfigEnv.html),
170//! and restarts the service when the user saves.
171//!
172//! ## Lifecycle
173//!
174//! 1. `main.rs` creates the install directory, initializes logging
175//!    (`easy_trace`), and calls `ConfigEnv::load` to materialize the
176//!    configuration from `.env`.
177//! 2. [`utils::load_services`] reads `MODULES` (env var, default
178//!    `"base,print,scale,rfid"`) and instantiates the corresponding
179//!    [`Service`] implementations into a
180//!    `HashMap<String, Arc<dyn Service>>`.
181//! 3. [`utils::start_local_server`] binds TCP `127.0.0.1:<local_port>` and
182//!    accepts both TLS and plain clients, dispatching each connection to
183//!    [`utils::websocket_handler`].
184//! 4. If a `REMOTE_HOST` is configured, [`utils::start_remote_server`]
185//!    opens an outbound WebSocket acting as a gateway client (tunnels
186//!    commands coming from the cloud back into this service).
187//! 5. Incoming WS frames are JSON-decoded, routed by `TARGET` through the
188//!    services map, and the response is sent back on the same socket.
189//!
190//! ## What is (re-)exported
191//!
192//! Everything in [`prelude`] is flattened at the crate root via
193//! `pub use self::prelude::*;`, so downstream binaries (`main.rs`,
194//! `pispas-service`, tests) can just `use pispas_modules::*;`.
195
196pub mod service;
197pub mod pdf_manager;
198pub mod base;
199pub mod scale;
200pub mod cash_guard;
201pub mod printsrvc;
202pub mod rfid;
203pub mod utils;
204pub mod command_viewer;
205pub mod mypos;
206pub mod order_kitchen;
207pub mod paytef;
208pub mod persistence;
209
210pub use self::prelude::*;
211
212/// One-stop re-export hub. `use pispas_modules::prelude::*;` pulls every
213/// public item of every module into scope at once.
214///
215/// This is also what `pub use self::prelude::*;` flattens to the crate
216/// root — see the crate-level docs for the full architecture.
217pub mod prelude {
218    pub use crate::service::*;
219    pub use crate::pdf_manager::*;
220    pub use crate::base::*;
221    pub use crate::scale::*;
222    pub use crate::cash_guard::*;
223    pub use crate::printsrvc::*;
224    pub use crate::rfid::*;
225    pub use crate::utils::*;
226    pub use crate::mypos::*;
227    pub use crate::paytef::*;
228    pub use crate::persistence::*;
229}