Skip to content

Feature/service api#3

Merged
dominicusin merged 8 commits intomainfrom
feature/service-api
Mar 29, 2026
Merged

Feature/service api#3
dominicusin merged 8 commits intomainfrom
feature/service-api

Conversation

@dominicusin
Copy link
Copy Markdown
Owner

@dominicusin dominicusin commented Mar 29, 2026

Summary by CodeRabbit

  • New Features

    • Added comprehensive data access layer with extensive database query functions
    • Added audit logging system to track system activities
    • Added inventory management with stock receipt, issue, and transfer operations
    • Added payroll management with salary calculations and payroll reports
    • Expanded report generation capabilities for sales, inventory, and financial reports
    • Enhanced JWT authentication with proper token pair management
    • Added WebSocket support with role-based access control
    • Added Prometheus monitoring configuration
  • Documentation

    • Added module-level documentation to API server

- Add jose, websockets, wai-websockets dependencies to Surypus.cabal
- Add websockets to extra-deps in stack.yaml
- Remove Service submodules (placeholders) that don't compile yet
- Keep Service.hs as placeholder for future implementation
- AccountingService: transaction processing, account balances, ledger generation
- InventoryService: stock receipts, issues, transfers
- PayrollService: salary calculation, payroll reports
- ReportService: sales, inventory, financial reports
- AuditService: audit logging

All services have stub implementations with proper type signatures.
- Replace placeholder token generation with HMAC-SHA256 JWT
- Use jose library for encode/decode operations
- Add jose to stack.yaml extra-deps
- Maintain backward compatible API with TokenPair, generateAccessToken,
  validateAccessToken, generateRefreshToken, validateRefreshToken
- Integrate JWT validation into WebSocket connections
- Add path-based routing for /ws and /ws/:path endpoints
- Token extraction from query parameters
- Role-based message filtering with broadcastToRole
- Store JWTPayload in connection context for authorization
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 29, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ed66ce01-7de7-441d-b598-49eee0be12df

📥 Commits

Reviewing files that changed from the base of the PR and between db2f21f and bc8e399.

⛔ Files ignored due to path filters (1)
  • stack.yaml.lock is excluded by !**/*.lock
📒 Files selected for processing (40)
  • .hlint.yaml
  • Surypus.cabal
  • prometheus/prometheus.yml
  • src/APIServer.hs
  • src/DAL/Queries.hs
  • src/DAL/Queries.hs.before_sql_fmt
  • src/DAL/Queries.hs.sqlformat
  • src/DAL/Queries.hs.tmp
  • src/DAL/Repository.hs
  • src/DAL/Repository/AccPlan.hs
  • src/DAL/Repository/AccTurn.hs
  • src/DAL/Repository/Bill.hs
  • src/DAL/Repository/Currency.hs
  • src/DAL/Repository/Goods.hs
  • src/DAL/Repository/Location.hs
  • src/DAL/Repository/Order.hs
  • src/DAL/Repository/Payment.hs
  • src/DAL/Repository/Person.hs
  • src/DAL/Repository/Tax.hs
  • src/DB/Connection.hs
  • src/Domain/Payroll.hs
  • src/Domain/Types.hs
  • src/Service/AccountingService.hs
  • src/Service/AuditService.hs
  • src/Service/InventoryService.hs
  • src/Service/PayrollService.hs
  • src/Service/ReportService.hs
  • src/Surypus/JWT.hs
  • src/Surypus/TypeLevel.hs
  • src/Surypus/WebSocket.hs
  • stack.yaml
  • test/API/ServerSpec.hs
  • test/DB/RepositoriesSpec.hs
  • test/Domain/BillSpec.hs
  • test/Domain/GoodsSpec.hs
  • test/Domain/LocationSpec.hs
  • test/Domain/PersonSpec.hs
  • test/Domain/TypesSpec.hs
  • test/NewtypeGuardsTest.hs
  • test/Test.hs

Walkthrough

This pull request introduces a comprehensive data access layer with Hasql-based queries, refactors the repository pattern with improved error handling, implements multiple services (accounting, audit, inventory, payroll, reporting), enhances JWT authentication, adds WebSocket authentication support, and expands the library's public API surface with numerous domain modules and database connectivity abstractions.

Changes

Cohort / File(s) Summary
Build & Configuration
.hlint.yaml, Surypus.cabal, stack.yaml, prometheus/prometheus.yml
Extended HLint suppressions for APIServer, significantly expanded library's exposed modules (DAL, Domain, Reports), added build dependencies (mtl, scientific, jose, wai-websockets, websockets, warp, wai, http-types), updated test configuration, added Prometheus monitoring setup, and pinned jose and websockets dependencies.
Data Access Layer (DAL) - Queries
src/DAL/Queries.hs, src/DAL/Queries.hs.before_sql_fmt, src/DAL/Queries.hs.sqlformat, src/DAL/Queries.hs.tmp
Introduced comprehensive Hasql-based query module with 50+ exported query functions, row decoders for all domain entities, pagination helpers (persons, goods, bills, orders), and specialized aggregation queries (sales summaries, stock inventories, dashboard stats) returning standardized QueryResult error handling.
Repository Pattern Refactoring
src/DAL/Repository.hs, src/DAL/Repository/AccPlan.hs, src/DAL/Repository/AccTurn.hs, src/DAL/Repository/Bill.hs, src/DAL/Repository/Currency.hs, src/DAL/Repository/Goods.hs, src/DAL/Repository/Location.hs, src/DAL/Repository/Order.hs, src/DAL/Repository/Payment.hs, src/DAL/Repository/Person.hs, src/DAL/Repository/Tax.hs
Redesigned repository typeclass from monad-based to context-based (Repository repo entity), changed error types to carry Text instead of String, removed Pagination/Filters types, introduced RepositoryContext and RepositoryT, and updated all repository implementations to wire database operations via DAL queries with new error semantics.
Database Connection
src/DB/Connection.hs
Extended PoolConfig with new fields pcStripes and pcIdleTime, updated defaults to support configurable connection pool striping and idle timeout behavior.
Service Layer - Accounting
src/Service/AccountingService.hs
Replaced placeholder with full implementation: added transaction validation (validateTransaction), replaced repository dependencies with direct pool access, implemented real DB inserts into acc_turn table, added balance/turnover/ledger queries backed by SQL, changed numeric types from Decimal to Double with currency scaling, and exposed new reporting APIs (getAccountTurnovers).
Service Layer - Audit
src/Service/AuditService.hs
Added new audit logging service with AuditEvent/AuditAction/AuditEntityType types, query functions for log retrieval by entity/user/pagination, and Hasql-based persistence to audit_log table with automatic ID retrieval.
Service Layer - Inventory
src/Service/InventoryService.hs
Replaced placeholder with complete inventory persistence layer: validates stock operations, converts quantities to scaled integers, prevents issues/transfers with insufficient stock, upserts to stock table, and provides query functions for balances by location/goods.
Service Layer - Payroll
src/Service/PayrollService.hs
Implemented full payroll calculation and reporting: queries employee records, computes gross/NDFL/net with currency scaling, aggregates payroll totals via SQL, generates detailed payroll reports, and processes payroll payments with automatic ID retrieval.
Service Layer - Reports
src/Service/ReportService.hs
Added database-backed report generators for sales, inventory, financial, payroll, and tax reports with dedicated SQL statements, error handling via Either Text, and zero-value fallbacks for empty result sets.
Authentication & Security
src/Surypus/JWT.hs, src/Surypus/WebSocket.hs
Replaced ad-hoc token generation with proper JWT library integration using HS256, added ToJSON/FromJSON instances, separated access/refresh token logic, and extended WebSocket server with JWT-gated connections using query parameters, role-based message filtering, and authenticated client tracking.
Type-Level Abstractions
src/Surypus/TypeLevel.hs
Introduced type families (EntityId, EntityFilter, EntitySort), repository typeclass parameterized by entity type, and type-safe document hierarchy using GADTs (DocumentType, Document) with accessor typeclass (DocumentOps).
Domain Types
src/Domain/Types.hs, src/Domain/Payroll.hs
Added conversion utilities for Money, Pagination field accessors, PPID conversions, new Flags32 newtype with bitwise operations and monoid instances, removed unused imports.
API Documentation
src/APIServer.hs
Added Haddock module documentation describing Scotty-based HTTP server, configured features (routes, CORS, rate limiting, JSON), and example usage.
Tests
test/API/ServerSpec.hs, test/DB/RepositoriesSpec.hs, test/Domain/BillSpec.hs, test/Domain/GoodsSpec.hs, test/Domain/LocationSpec.hs, test/Domain/PersonSpec.hs, test/Domain/TypesSpec.hs, test/NewtypeGuardsTest.hs, test/Test.hs
Removed extensive API endpoint tests, simplified domain type assertions, removed QuickCheck coverage, added newtype guard tests for AccountBalance/StockQty/TaxRate, and cleaned up unused imports.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 The warren rejoices with queries galore,
SQL statements encoded in every door,
JWT tokens now guard the tight gate,
While services flourish to manage the state,
Repositories refactored, type-safe and neat—
A data layer complete, our triumph's sweet! 🚀

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/service-api

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@qodo-code-review
Copy link
Copy Markdown

CI Feedback 🧐

A test triggered by this PR failed. Here is an AI-generated analysis of the failure:

Action: docker

Failed stage: Build Docker image [❌]

Failed test name: ""

Failure summary:

The action failed during the Docker image build because the Dockerfile tries to copy a test
directory that does not exist in the build context.
- Failing step: COPY test ./test (Dockerfile
line 20)
- Error: failed to calculate checksum ... "/test": not found, which indicates the test/
path is missing (or excluded from the build context, e.g., by .dockerignore).
- The
SecretsUsedInArgOrEnv message about ENV "DB_PASSWORD" is only a warning and did not cause the
failure.

Relevant error logs:
1:  ##[group]Runner Image Provisioner
2:  Hosted Compute Agent
...

131:  #6 DONE 0.0s
132:  #7 [internal] load build context
133:  #7 transferring context: 1.05MB 0.0s done
134:  #7 DONE 0.0s
135:  #8 [builder 3/8] COPY stack.yaml stack.yaml.lock ./
136:  #8 CACHED
137:  #9 [builder 6/8] COPY src ./src
138:  #9 CACHED
139:  #10 [builder 2/8] WORKDIR /build
140:  #10 CACHED
141:  #11 [builder 4/8] COPY Surypus.cabal ./
142:  #11 CACHED
143:  #12 [builder 5/8] RUN stack setup
144:  #12 CACHED
145:  #13 [builder 7/8] COPY test ./test
146:  #13 ERROR: failed to calculate checksum of ref 26c853c4-ebf5-403c-a446-44e39e017c08::c3my5mvn71w6e89tzo943lofb: "/test": not found
147:  #14 [builder 1/8] FROM docker.io/library/haskell:9.6.6@sha256:33e280e96a347f31cb9eb595c9e2ff134fc42d5175a50a9d958194d711402876
...

157:  #15 sha256:d6b3fe87704bf89df3b8ab19d5f3ab0a248bf5b6b76a602da86d611ea9d23ae0 453B / 453B done
158:  #15 CANCELED
159:  ------
160:  > [builder 7/8] COPY test ./test:
161:  ------
162:  �[33m1 warning found (use docker --debug to expand):
163:  �[0m - SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ENV "DB_PASSWORD") (line 55)
164:  Dockerfile:20
165:  --------------------
166:  18 |     # Copy source and build
167:  19 |     COPY src ./src
168:  20 | >>> COPY test ./test
169:  21 |     
170:  22 |     # Build production executable
171:  --------------------
172:  ERROR: failed to build: failed to solve: failed to compute cache key: failed to calculate checksum of ref 26c853c4-ebf5-403c-a446-44e39e017c08::c3my5mvn71w6e89tzo943lofb: "/test": not found
173:  ##[error]Process completed with exit code 1.
174:  Post job cleanup.

- AccountingService: transaction processing, account balances, turnovers, ledger
- InventoryService: stock receipts, issues, transfers with balance validation
- PayrollService: salary calculation, NDFL, payroll reports
- ReportService: sales, inventory, financial, payroll, tax reports
- AuditService: audit logging with entity/user filtering

All services use Hasql for type-safe PostgreSQL queries with proper
encoders/decoders for Int64 cents/kopecks storage.
@qodo-code-review
Copy link
Copy Markdown

Review Summary by Qodo

Service API Implementation with JWT Authentication, Repository Pattern Refactoring, and Comprehensive Service Layer

✨ Enhancement 🐞 Bug fix 🧪 Tests 📝 Documentation

Grey Divider

Walkthroughs

Description
• **JWT Authentication & WebSocket Support**: Implemented proper JWT-based authentication using the
  jose library with HS256 algorithm, replacing simple string tokens. Added WebSocket support with
  role-based access control and message filtering through jwtWebSocketApp and
  runWebSocketServerWithAuth functions.
• **Repository Pattern Refactoring**: Refactored the Repository typeclass to use monadic error
  handling with ExceptT, standardized CRUD operation signatures across all repository
  implementations (AccPlan, Bill, Currency, Goods, Location, Person, AccTurn, Order, Payment, Tax),
  and added dependency injection support through HasRepository typeclass.
• **Service Layer Implementation**: Created comprehensive service modules for Accounting, Audit,
  Payroll, Inventory, and Report services with stub implementations accepting Pool for database
  operations. Added support for audit logging with role-based filtering and multiple report types.
• **Type-Level Programming Utilities**: Introduced new TypeLevel.hs module with type families
  (EntityId, EntityFilter, EntitySort) and GADTs (DocumentType, Document) for type-safe
  entity and document operations.
• **Database Query Layer**: Implemented comprehensive DAL with 60+ query functions using Hasql
  library, including row decoders for all business entities, CRUD operations, pagination with
  filtering/sorting, and specialized analytics queries.
• **Domain Type Enhancements**: Added utility functions for type conversions (ppidToInt64,
  toMoney, fromMoney), introduced Flags32 newtype with bitwise operations, and added accessor
  functions for Pagination.
• **Test Simplification**: Simplified domain tests (Bill, Goods, Person, Location) to focus on basic
  entity creation, removing complex property-based tests. Added new tests for newtype guard
  implementations.
• **Configuration & Dependencies**: Extended connection pool configuration with tuning parameters,
  added new dependencies (mtl, scientific, jose, wai-websockets, websockets, warp, wai,
  http-types), and created Prometheus monitoring configuration.
• **Bug Fixes**: Fixed Repository typeclass implementations in AccTurn, Order, Payment, and Tax
  repositories to return proper types (Int64 from create, Maybe entity from update/delete).
• **Documentation**: Added comprehensive module documentation with usage examples to APIServer,
  Bill, Currency, Goods, Location, and Person repositories.
Diagram
flowchart LR
  JWT["JWT Authentication<br/>jose library HS256"]
  WS["WebSocket Support<br/>Role-based Access"]
  Repo["Repository Pattern<br/>ExceptT Monadic"]
  Services["Service Layer<br/>Accounting Audit Payroll"]
  DAL["Database Query Layer<br/>60+ Hasql Functions"]
  Types["Type-Level Utils<br/>Type Families GADTs"]
  
  JWT --> WS
  Repo --> Services
  DAL --> Repo
  Types --> Repo
  Services --> DAL
Loading

Grey Divider

File Changes

1. src/Surypus/WebSocket.hs ✨ Enhancement +120/-23

JWT authentication and role-based access control for WebSocket

• Added JWT authentication support to WebSocket connections with jwtWebSocketApp and
 runWebSocketServerWithAuth functions
• Extended WebSocketHub to store optional JWTPayload alongside client connections for role-based
 access control
• Implemented token extraction from WebSocket request headers and JWT validation using
 validateAccessToken
• Added path-based routing and role-based message filtering with handleMessage and
 broadcastToRole functions
• Updated client registration to include JWT payload and modified all client tracking tuples to
 include authentication data

src/Surypus/WebSocket.hs


2. src/Surypus/JWT.hs ✨ Enhancement +112/-33

Migrate from string tokens to proper JWT implementation

• Replaced simple string-based token format with proper JWT implementation using jose library with
 HS256 algorithm
• Added ToJSON and FromJSON instances for JWTPayload to support serialization
• Implemented generateAccessToken and generateRefreshToken as separate functions returning
 Either Text Text
• Updated token validation functions to decode JWT claims and extract user information from standard
 JWT fields
• Changed default JWT secret to include production warning and improved claims structure with issuer
 and subject fields

src/Surypus/JWT.hs


3. src/DAL/Repository/AccPlan.hs ✨ Enhancement +174/-12

Implement repository pattern with validation and error handling

• Added comprehensive module documentation with usage examples for repository operations
• Implemented Repository typeclass instance with proper error handling and database query
 integration
• Added HasAccPlanRepository and HasRepository typeclasses for dependency injection patterns
• Introduced runAccPlanRepository function to execute repository operations with error handling
• Added validation layer using validateAccPlanInputRepo and helper functions for mutation result
 extraction

src/DAL/Repository/AccPlan.hs


View more (37)
4. src/Surypus/TypeLevel.hs ✨ Enhancement +149/-0

Add type-level programming utilities for entities and documents

• Created new module with type-level programming utilities using type families and GADTs
• Defined EntityId, EntityFilter, and EntitySort type families for flexible entity operations
• Implemented RepositoryTF typeclass with type family-based methods for generic repository
 operations
• Added DocumentType GADT and Document GADT for type-safe document hierarchy with Bill, Order,
 Invoice, and Receipt variants
• Implemented DocumentOps typeclass with type-safe operations for extracting document properties

src/Surypus/TypeLevel.hs


5. src/DAL/Repository/Bill.hs 📝 Documentation +71/-3

Add documentation and fix repository pattern implementation

• Added extensive module documentation with practical usage examples for bill repository operations
• Fixed Repository typeclass implementation to return Int64 from create and Maybe entity
 from update and delete
• Improved error handling consistency across CRUD operations with proper ExceptT usage

src/DAL/Repository/Bill.hs


6. src/DAL/Repository/Currency.hs 📝 Documentation +74/-3

Add documentation and standardize repository implementation

• Added comprehensive module documentation with usage examples for currency repository operations
• Fixed Repository typeclass implementation to properly return entity IDs and optional entities
• Standardized error handling across all CRUD operations

src/DAL/Repository/Currency.hs


7. src/DAL/Repository/Goods.hs 📝 Documentation +55/-3

Add documentation and fix repository implementation

• Added detailed module documentation with practical examples for goods repository operations
• Fixed Repository typeclass implementation to return proper types from CRUD operations
• Standardized error handling and return value patterns

src/DAL/Repository/Goods.hs


8. test/Domain/BillSpec.hs 🧪 Tests +8/-55

Simplify bill domain tests to basic creation

• Removed complex QuickCheck property tests and validation logic tests
• Simplified test suite to focus on basic bill creation with required fields
• Removed tests for bill line calculations, VAT computations, and total summations

test/Domain/BillSpec.hs


9. src/Service/AccountingService.hs ✨ Enhancement +31/-46

Refactor accounting service with simplified architecture

• Simplified service structure to accept Pool directly instead of repository instances
• Replaced placeholder implementations with stub functions returning Left "Not implemented"
• Added getAccountTurnovers and validateTransaction functions with proper type signatures
• Updated data types with cleaner structure for Transaction, Entry, Ledger, and new
 Turnovers type

src/Service/AccountingService.hs


10. src/DAL/Repository/Location.hs 📝 Documentation +52/-3

Add documentation and standardize repository implementation

• Added comprehensive module documentation with usage examples
• Fixed Repository typeclass implementation to return proper types from CRUD operations
• Standardized error handling across all repository functions

src/DAL/Repository/Location.hs


11. test/Domain/GoodsSpec.hs 🧪 Tests +11/-55

Simplify goods domain tests to basic creation

• Removed QuickCheck imports and property-based tests
• Simplified test suite to basic goods creation with required fields
• Removed tests for goods filters, stock records, and barcode functionality

test/Domain/GoodsSpec.hs


12. src/DAL/Repository/Person.hs 📝 Documentation +37/-3

Add documentation and standardize repository implementation

• Added module documentation with usage examples for person repository operations
• Fixed Repository typeclass implementation to return proper types from CRUD operations
• Standardized error handling patterns

src/DAL/Repository/Person.hs


13. src/DAL/Repository.hs ✨ Enhancement +36/-28

Refactor repository pattern with monadic error handling

• Refactored Repository typeclass to use RepositoryM monad (ExceptT-based) for error handling
• Changed method signatures to accept repository instance as first parameter instead of using type
 class constraints
• Added RepositoryContext, RepositoryT, and HasRepository typeclass for dependency injection
• Introduced isNotFoundMessage helper and runRepository function for executing repository
 operations
• Updated RepositoryError to use Text instead of String for error messages

src/DAL/Repository.hs


14. src/Service/AuditService.hs ✨ Enhancement +72/-0

Add audit service for system-wide event logging

• Created new audit service module with comprehensive audit logging functionality
• Defined AuditService accepting Pool for database operations
• Implemented stub functions for logging and retrieving audit events with role-based filtering
• Added AuditAction enum with CRUD, authentication, and execution actions
• Added AuditEntityType enum covering all major entity types in the system

src/Service/AuditService.hs


15. test/NewtypeGuardsTest.hs 🧪 Tests +43/-0

Add tests for newtype guard implementations

• Created new test module for newtype guard validation
• Added tests for AccountBalance, StockQty, and TaxRate newtypes with creation and arithmetic
 operations
• Verified that newtypes support Num operations with proper scaling

test/NewtypeGuardsTest.hs


16. test/Domain/PersonSpec.hs 🧪 Tests +9/-29

Simplify person domain tests to basic creation

• Removed complex tests for person status values and filter pagination
• Simplified test suite to focus on basic person creation with required fields
• Removed tests for extended person records and filter functionality

test/Domain/PersonSpec.hs


17. src/Service/ReportService.hs ✨ Enhancement +63/-2

Implement report service with multiple report types

• Replaced placeholder implementation with comprehensive report service module
• Added ReportService accepting Pool for database operations
• Implemented stub functions for generating sales, inventory, financial, payroll, and tax reports
• Defined data types for SalesReport, InventoryReport, FinancialReport, PayrollSummary, and
 TaxReport

src/Service/ReportService.hs


18. src/Service/PayrollService.hs ✨ Enhancement +59/-2

Implement payroll service with calculation and reporting

• Replaced placeholder implementation with comprehensive payroll service module
• Added PayrollService accepting Pool for database operations
• Implemented stub functions for salary calculation, payroll processing, and report generation
• Defined data types for Employee, PayrollResult, and PayrollReport with detailed payroll
 information

src/Service/PayrollService.hs


19. test/Domain/LocationSpec.hs 🧪 Tests +5/-25

Simplify location domain tests to basic creation

• Removed tests for location types and filter pagination
• Simplified test suite to focus on basic location creation with required fields
• Removed tests for location type variations and filter functionality

test/Domain/LocationSpec.hs


20. src/DB/Connection.hs ⚙️ Configuration changes +9/-3

Extend connection pool configuration with tuning parameters

• Added pcStripes and pcIdleTime fields to PoolConfig for connection pool tuning
• Updated defaultPoolConfig with new fields set to 1 stripe and 60 seconds idle time
• Modified poolConfigFromEnv to preserve new configuration fields from defaults

src/DB/Connection.hs


21. src/Domain/Types.hs ✨ Enhancement +40/-0

Add utility functions and flag operations to domain types

• Added helper functions ppidToInt64 and int64ToPPID for PPID conversions
• Added toMoney and fromMoney functions for Money type conversions
• Introduced Flags32 newtype with Semigroup and Monoid instances for bitwise flag operations
• Added hasFlag function for checking flag membership
• Added accessor functions offset and limit for Pagination type

src/Domain/Types.hs


22. src/Service/InventoryService.hs ✨ Enhancement +44/-2

Implement inventory service with stock operations

• Replaced placeholder implementation with comprehensive inventory service module
• Added InventoryService accepting Pool for database operations
• Implemented stub functions for stock receipt, issue, transfer, and balance queries
• Added validateStockOperation function for operation validation

src/Service/InventoryService.hs


23. src/DAL/Repository/AccTurn.hs 🐞 Bug fix +10/-4

Fix repository pattern implementation for accounting turns

• Fixed Repository typeclass implementation to return Int64 from create and Maybe entity
 from update and delete
• Removed unused toDecimal import
• Standardized error handling patterns across CRUD operations

src/DAL/Repository/AccTurn.hs


24. src/DAL/Repository/Order.hs 🐞 Bug fix +7/-3

Fix repository pattern implementation for orders

• Fixed Repository typeclass implementation to return proper types from CRUD operations
• Updated update method to return Maybe entity instead of bare entity
• Standardized error handling patterns

src/DAL/Repository/Order.hs


25. src/DAL/Repository/Payment.hs 🐞 Bug fix +9/-3

Fix repository pattern implementation for payments

• Fixed Repository typeclass implementation to return proper types from CRUD operations
• Updated update and delete methods to return Maybe entity and Nothing respectively
• Standardized error handling patterns

src/DAL/Repository/Payment.hs


26. src/DAL/Repository/Tax.hs 🐞 Bug fix +9/-3

Fix repository pattern implementation for taxes

• Fixed Repository typeclass implementation to return proper types from CRUD operations
• Updated update and delete methods to return Maybe entity and Nothing respectively
• Standardized error handling patterns

src/DAL/Repository/Tax.hs


27. src/APIServer.hs 📝 Documentation +22/-0

Add API server module documentation

• Added comprehensive module documentation explaining API server purpose and usage
• Included example code for server configuration and startup
• Documented the server's role in exposing RESTful endpoints and middleware support

src/APIServer.hs


28. test/Test.hs Formatting +1/-2

Clean up test imports and add type annotations

• Removed unused Test.QuickCheck import
• Added explicit type annotations to template count test for clarity

test/Test.hs


29. src/DAL/Queries.hs Formatting +1/-1

Remove unused import

• Removed unused unpreparable import from Hasql.Statement

src/DAL/Queries.hs


30. Surypus.cabal Dependencies +65/-2

Update cabal file with new modules and dependencies

• Added extensive list of new modules to library exposed-modules including DAL, Domain, and Service
 modules
• Added new dependencies: mtl, scientific, jose, wai-websockets, websockets, warp,
 wai, http-types
• Updated executable dependencies to include servant, servant-server, and jose
• Reorganized test suite with explicit other-modules list and added test dependencies
• Changed ghc-options from -Wall -Werror to -Wall to allow warnings
• Added ghc-options -Wwarn to test suite

Surypus.cabal


31. stack.yaml Dependencies +2/-0

Add JWT and WebSocket dependencies to stack configuration

• Added jose-0.10 and websockets-0.13.0.0 to extra-deps for JWT and WebSocket support

stack.yaml


32. .hlint.yaml ⚙️ Configuration changes +4/-0

Add hlint configuration for APIServer module

• Added hlint ignore rules for APIServer module to suppress false positive warnings for Use <> and
 Redundant $

.hlint.yaml


33. prometheus/prometheus.yml ⚙️ Configuration changes +15/-0

Add Prometheus monitoring configuration

• Created new Prometheus configuration file for metrics collection
• Configured scrape targets for surypus API server on port 8080 and PostgreSQL exporter on port 9187
• Set global scrape interval to 15 seconds with 5-second interval for surypus metrics

prometheus/prometheus.yml


34. src/DAL/Queries.hs.before_sql_fmt ✨ Enhancement +1163/-0

Database query layer implementation with Hasql

• Introduces comprehensive database query layer with 60+ query functions for business entities
• Implements row decoders for mapping database results to Haskell types (Person, Goods, Bill, Order,
 etc.)
• Provides CRUD operations and specialized queries (search, pagination, aggregations)
• Includes pagination support with filtering and sorting capabilities for multiple entity types

src/DAL/Queries.hs.before_sql_fmt


35. src/DAL/Queries.hs.sqlformat ✨ Enhancement +1163/-0

Database query layer implementation with Hasql

• Identical content to before_sql_fmt file with same 60+ query functions
• Contains all row decoders and database operations for business entities
• Provides complete CRUD and specialized query functionality with pagination
• Represents formatted version of the query layer module

src/DAL/Queries.hs.sqlformat


36. src/DAL/Queries.hs.tmp ✨ Enhancement +1163/-0

Complete Data Access Layer with Query Functions

• Comprehensive database query module with 60+ functions for accessing business entities (persons,
 goods, locations, bills, stock, users, employees, orders, payments, etc.)
• Implements row decoders for converting database results into Haskell types using Hasql library
• Provides both simple fetch operations and advanced paginated queries with filtering and sorting
 capabilities
• Includes specialized queries for business analytics (sales summary, top-selling goods, inventory,
 dashboard stats, low stock alerts)

src/DAL/Queries.hs.tmp


37. src/Domain/Payroll.hs Additional files +0/-1

...

src/Domain/Payroll.hs


38. test/API/ServerSpec.hs Additional files +0/-70

...

test/API/ServerSpec.hs


39. test/DB/RepositoriesSpec.hs Additional files +0/-4

...

test/DB/RepositoriesSpec.hs


40. test/Domain/TypesSpec.hs Additional files +0/-2

...

test/Domain/TypesSpec.hs


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review Bot commented Mar 29, 2026

Code Review by Qodo

🐞 Bugs (7) 📘 Rule violations (3) 📎 Requirement gaps (0) 📐 Spec deviations (0)

Grey Divider


Action required

1. undefined in Haddock example 📘 Rule violation ⛯ Reliability
Description
undefined appears in newly added Haddock/example code, which violates the rule prohibiting
undefined in committed code and can normalize unsafe patterns. Remove or rewrite the examples to
avoid undefined placeholders.
Code

src/APIServer.hs[23]

+--         , scWebSocketHub = undefined -- TODO: Initialize WebSocket hub
Evidence
PR Compliance ID 9 forbids any undefined in changed code. The added Haddock example in
src/APIServer.hs contains scWebSocketHub = undefined, and similar example snippets exist in
repository docs as well.

AGENTS.md
src/APIServer.hs[23-23]
src/DAL/Repository/AccPlan.hs[22-22]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`undefined` is present in newly added Haddock/example snippets, which violates the project rule disallowing `undefined` in committed code.

## Issue Context
These are documentation examples; they should demonstrate safe/complete patterns (e.g., using a named `hub` value, or omitting the field and explaining how to obtain it) rather than `undefined` placeholders.

## Fix Focus Areas
- src/APIServer.hs[14-26]
- src/DAL/Repository/AccPlan.hs[15-70]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Tests missing T.pack 📘 Rule violation ⚙ Maintainability
Description
New test code uses raw string literals where Text is expected instead of T.pack, violating the
testing standard. This can cause type ambiguity/mismatches and reduces consistency across tests.
Code

test/Domain/PersonSpec.hs[R11-23]

      let p = Person
            { personId = Nothing
+            , personCode = Just "001"
            , personName = "Test Company"
-            , personFlags = 0
+            , personINN = Just "1234567890"
+            , personKPP = Nothing
+            , personKind = 1
            , personStatus = 0
+            , personPhone = Nothing
+            , personEmail = Nothing
+            , personAddress = Nothing
+            , personCredit = 0
+            , personDiscount = 0
Evidence
PR Compliance ID 14 requires using T.pack for string literals in tests when Text is expected.
The changed tests construct domain records with Text fields using raw literals like Just "001"
and Just "1234567890".

AGENTS.md
test/Domain/PersonSpec.hs[11-23]
test/Domain/GoodsSpec.hs[11-20]
test/Domain/BillSpec.hs[11-16]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Tests are using raw string literals for `Text` fields instead of `T.pack "..."`, violating the test text-handling standard.

## Issue Context
Update tests to explicitly pack string literals into `Text` by importing `qualified Data.Text as T` and replacing literals with `T.pack`.

## Fix Focus Areas
- test/Domain/PersonSpec.hs[1-25]
- test/Domain/GoodsSpec.hs[1-27]
- test/Domain/BillSpec.hs[1-27]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Data.Text import order wrong 📘 Rule violation ⚙ Maintainability
Description
Imports in DAL.Repository.AccPlan do not follow the documented grouping/ordering (qualified
imports should precede explicit imports, and groups should be ordered consistently). This reduces
readability and violates the import convention rule.
Code

src/DAL/Repository/AccPlan.hs[R85-92]

+import DAL.Mutations (createAccPlan, deleteAccPlan, updateAccPlan)
+import DAL.Queries (getAccPlanById, getAccPlans)
+import DAL.Repository
+import DAL.Types
import Data.Int (Int64)
-import Domain.Accounting (AccAccount (..))
+import Data.Text (Text)
+import qualified Data.Text as T
import Hasql.Pool (Pool)
Evidence
PR Compliance ID 2 requires documented import grouping/ordering, including placing qualified imports
first. In src/DAL/Repository/AccPlan.hs, import Data.Text (Text) appears before `import
qualified Data.Text as T`, contrary to the rule.

AGENTS.md
src/DAL/Repository/AccPlan.hs[85-92]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`src/DAL/Repository/AccPlan.hs` imports are not ordered/grouped per the documented convention (qualified imports should come before explicit imports).

## Issue Context
Reorder the `Data.Text` imports (and, if needed, regroup local vs external imports) to match the project's standard.

## Fix Focus Areas
- src/DAL/Repository/AccPlan.hs[83-93]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (5)
4. WebSocket subpath type mismatch 🐞 Bug ✓ Correctness
Description
In Surypus.WebSocket, the route case ["ws", subPath] binds subPath as a single Text, but the
code passes it to T.intercalate "/" which expects [Text]. This prevents the module from
compiling.
Code

src/Surypus/WebSocket.hs[R96-100]

+      ["ws", subPath] ->
+        websocketsOr
+          WS.defaultConnectionOptions
+          (jwtWebSocketAppWithPath hub mConfig (T.intercalate "/" subPath))
+          (\_ respond' -> respond' (responseLBS status400 [] "WebSocket endpoint expected"))
Evidence
Wai.pathInfo is pattern matched into two segments, making subPath a single segment, yet it is
used as if it were a list of segments in T.intercalate.

src/Surypus/WebSocket.hs[90-101]
src/Surypus/WebSocket.hs[90-100]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`T.intercalate "/" subPath` is applied to a `Text` path segment, causing a type error.

### Issue Context
The `Wai.pathInfo` branch `["ws", subPath]` binds `subPath :: Text`.

### Fix Focus Areas
- src/Surypus/WebSocket.hs[90-101]

### Suggested fix
Either:
- Change the match to capture multiple segments: `("ws" : subPathSegments)` and use `T.intercalate "/" subPathSegments`, or
- Keep `["ws", subPath]` and pass `subPath` directly without `intercalate`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. WebSocket relay won't compile 🐞 Bug ✓ Correctness
Description
Surypus.WebSocket.handleMessage uses forM_ without importing it and attempts `WS.sendTextData
connection (WS.DataMessage msg)`, which does not typecheck as written. This prevents compilation and
breaks message relay.
Code

src/Surypus/WebSocket.hs[R167-172]

+handleMessage :: WebSocketHub -> Int -> Text -> WS.DataMessage -> IO ()
+handleMessage hub clientId path msg = do
+  clients <- readTVarIO (wshClients hub)
+  let filteredClients = filter (\(_, _, mPayload) -> canReceive path mPayload) clients
+  forM_ filteredClients $ \(_, connection, _) ->
+    WS.sendTextData connection (WS.DataMessage msg) `catch` \(_ :: SomeException) -> pure ()
Evidence
The import list does not include forM_, yet handleMessage uses it; additionally it passes an
invalid expression to sendTextData using WS.DataMessage applied as a value.

src/Surypus/WebSocket.hs[17-21]
src/Surypus/WebSocket.hs[167-173]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`handleMessage` doesn't compile due to missing `forM_` import and incorrect use of `WS.sendTextData`.

### Issue Context
`msg` is already a `WS.DataMessage` from `receiveDataMessage`.

### Fix Focus Areas
- src/Surypus/WebSocket.hs[15-25]
- src/Surypus/WebSocket.hs[167-173]

### Suggested fix
- Import `forM_` (e.g., `import Data.Foldable (forM_)` or add it to the existing import list), and
- Forward the message using the correct WebSockets API (e.g., `WS.sendDataMessage connection msg`) or pattern-match `msg` and send appropriate text/binary payloads.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Removed token API breaks build 🐞 Bug ✓ Correctness
Description
Surypus.JWT no longer exports/defines generateSimpleToken, but APIServer still imports and calls
it. This causes a compile-time failure.
Code

src/Surypus/JWT.hs[R4-15]

+module Surypus.JWT
+  ( JWTPayload (..),
+    JWTConfig (..),
+    TokenPair (..),
+    defaultJWTConfig,
+    generateTokenPair,
+    generateAccessToken,
+    validateAccessToken,
+    generateRefreshToken,
+    validateRefreshToken,
+    refreshAccessToken,
+  )
Evidence
The JWT module export list does not include generateSimpleToken, while APIServer imports and calls
it in the login handler.

src/Surypus/JWT.hs[4-15]
src/APIServer.hs[60-72]
src/APIServer.hs[313-323]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`generateSimpleToken` is referenced by APIServer but is not exported/implemented by Surypus.JWT anymore.

### Issue Context
This is a hard compile error due to missing symbol.

### Fix Focus Areas
- src/Surypus/JWT.hs[4-15]
- src/APIServer.hs[60-72]
- src/APIServer.hs[313-323]

### Suggested fix
Either:
- Re-introduce `generateSimpleToken` in `Surypus.JWT` as a compatibility wrapper (and export it), or
- Update APIServer to use `generateAccessToken`/`generateTokenPair` and handle the `Either` error path (and update the import list accordingly).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Refresh expiry discarded 🐞 Bug ⛨ Security
Description
Surypus.JWT.validateRefreshToken ignores the parsed exp claim and always returns the Unix epoch as
the expiration time. Any refresh-token expiry logic using this result will be incorrect.
Code

src/Surypus/JWT.hs[R141-153]

validateRefreshToken :: JWTConfig -> Text -> AppResult (Int, UTCTime)
validateRefreshToken config token = do
-  case T.splitOn ":" token of
-    [_uuid, uid, expStr] ->
-      case reads (T.unpack uid) of
-        [(uId, "")] ->
-          case reads (T.unpack expStr) of
-            [(exp, "")] -> Right (uId, exp)
-            _ -> Left (AuthError "Invalid expiration")
-        _ -> Left (AuthError "Invalid user id")
-    _ -> Left (AuthError "Invalid refresh token format")
+  let secret = TE.encodeUtf8 (jwtSecret config)
+  case jwtDecode secret token of
+    Right claims -> case sub claims of
+      Nothing -> Left (AuthError "Missing subject")
+      Just subClaim ->
+        case reads (T.unpack subClaim) of
+          [(uId, "")] ->
+            case exp claims of
+              Nothing -> Left (AuthError "Missing expiration")
+              Just expClaim -> Right (uId, read "1970-01-01 00:00:00 UTC" :: UTCTime)
+          _ -> Left (AuthError "Invalid subject format")
Evidence
validateRefreshToken matches Just expClaim but returns read "1970-01-01 00:00:00 UTC" instead
of converting/using expClaim.

src/Surypus/JWT.hs[141-154]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`validateRefreshToken` discards `expClaim` and returns a constant UTCTime.

### Issue Context
The function signature promises to return the refresh token's expiration time.

### Fix Focus Areas
- src/Surypus/JWT.hs[141-154]

### Suggested fix
Convert the numeric `exp` claim to `UTCTime` (e.g., using `posixSecondsToUTCTime`) and return it. Also consider validating `iss` (e.g., require `iss == "surypus-refresh"`) so access tokens cannot be used as refresh tokens.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. JWT encode uses error 🐞 Bug ⛯ Reliability
Description
Surypus.JWT.generateTokenPair and refreshAccessToken call error when jwtEncode fails, crashing the
server process. Token generation should surface failures as values (e.g., Either/AppResult)
instead of terminating.
Code

src/Surypus/JWT.hs[R102-105]

+  case (jwtEncode (secret, HS256) accessClaims, jwtEncode (secret, HS256) refreshClaims) of
+    (Right accessToken, Right refreshToken) -> pure $ TokenPair accessToken refreshToken accessExpiration
+    (Left err, _) -> error (show err)
+    (_, Left err) -> error (show err)
Evidence
Both token generation paths pattern match on jwtEncode results and invoke error (show err) on
failure.

src/Surypus/JWT.hs[94-105]
src/Surypus/JWT.hs[156-164]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
JWT encoding failures crash the entire process via `error`.

### Issue Context
Even if failures are rare, this turns a recoverable error into a hard outage.

### Fix Focus Areas
- src/Surypus/JWT.hs[94-105]
- src/Surypus/JWT.hs[156-164]

### Suggested fix
Change these functions to return `IO (Either Text ...)` (or `AppResult ...`) and propagate the encoding error to callers. Update callers to handle the Left case (HTTP 500 / auth error response as appropriate).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

9. JWT claims parsed incorrectly 🐞 Bug ✓ Correctness
Description
Surypus.JWT.validateAccessToken sets both jwtUsername and jwtRole from the same jti claim,
while makeClaims stores the role in jti. This loses the username and makes token parsing
inconsistent and error-prone for downstream authorization/auditing.
Code

src/Surypus/JWT.hs[R115-118]

+          [(uId, "")] ->
+            let username = maybe "" id (jti claims)
+                role = maybe "user" id (jti claims)
+             in Right (JWTPayload uId username role)
Evidence
makeClaims writes jti = Just (jwtRole payload) but validateAccessToken reads jti twice (once
as username and once as role).

src/Surypus/JWT.hs[72-82]
src/Surypus/JWT.hs[107-119]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Username and role are conflated into the single `jti` claim, causing loss of username and confusing semantics.

### Issue Context
`jti` is typically a token identifier; using it to store role (and then also reading it as username) is inconsistent.

### Fix Focus Areas
- src/Surypus/JWT.hs[60-82]
- src/Surypus/JWT.hs[107-119]

### Suggested fix
Store username and role in distinct claims (preferably as unregistered/custom claims or a JSON payload via the JWT library), and update `validateAccessToken` to decode them accordingly. Avoid using `jti` for role/username unless it is strictly a token ID.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Advisory comments

10. Pool stripes/idle ignored 🐞 Bug ⚙ Maintainability
Description
DB.Connection adds pcStripes and pcIdleTime to PoolConfig, but createPoolWithTimeout never uses
these values when building the Hasql pool config. This makes the new configuration fields
ineffective and misleading.
Code

src/DB/Connection.hs[R26-31]

    pcUser :: String,
    pcPassword :: String,
    pcDatabase :: String,
-    pcConnections :: Int
+    pcConnections :: Int,
+    pcStripes :: Int,
+    pcIdleTime :: Int
Evidence
PoolConfig contains the new fields and poolConfigFromEnv sets them, but PoolConfig.settings only
uses pcConnections and timeout settings.

src/DB/Connection.hs[22-66]
src/DB/Connection.hs[82-95]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`pcStripes` and `pcIdleTime` are configured but not applied to the pool, so they have no effect.

### Issue Context
This increases configuration surface without changing runtime behavior.

### Fix Focus Areas
- src/DB/Connection.hs[22-66]
- src/DB/Connection.hs[82-95]

### Suggested fix
If Hasql pool supports stripes/idle-time options, add them to the PoolConfig.settings list. Otherwise, remove these fields to avoid misleading configuration.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@dominicusin dominicusin merged commit 6f2ddb0 into main Mar 29, 2026
2 of 4 checks passed
Comment thread src/APIServer.hs
-- { scHost = "127.0.0.1"
-- , scPort = 8080
-- , scPool = pool
-- , scWebSocketHub = undefined -- TODO: Initialize WebSocket hub
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. undefined in haddock example 📘 Rule violation ⛯ Reliability

undefined appears in newly added Haddock/example code, which violates the rule prohibiting
undefined in committed code and can normalize unsafe patterns. Remove or rewrite the examples to
avoid undefined placeholders.
Agent Prompt
## Issue description
`undefined` is present in newly added Haddock/example snippets, which violates the project rule disallowing `undefined` in committed code.

## Issue Context
These are documentation examples; they should demonstrate safe/complete patterns (e.g., using a named `hub` value, or omitting the field and explaining how to obtain it) rather than `undefined` placeholders.

## Fix Focus Areas
- src/APIServer.hs[14-26]
- src/DAL/Repository/AccPlan.hs[15-70]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread test/Domain/PersonSpec.hs
Comment on lines 11 to +23
let p = Person
{ personId = Nothing
, personCode = Just "001"
, personName = "Test Company"
, personFlags = 0
, personINN = Just "1234567890"
, personKPP = Nothing
, personKind = 1
, personStatus = 0
, personPhone = Nothing
, personEmail = Nothing
, personAddress = Nothing
, personCredit = 0
, personDiscount = 0
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

2. Tests missing t.pack 📘 Rule violation ⚙ Maintainability

New test code uses raw string literals where Text is expected instead of T.pack, violating the
testing standard. This can cause type ambiguity/mismatches and reduces consistency across tests.
Agent Prompt
## Issue description
Tests are using raw string literals for `Text` fields instead of `T.pack "..."`, violating the test text-handling standard.

## Issue Context
Update tests to explicitly pack string literals into `Text` by importing `qualified Data.Text as T` and replacing literals with `T.pack`.

## Fix Focus Areas
- test/Domain/PersonSpec.hs[1-25]
- test/Domain/GoodsSpec.hs[1-27]
- test/Domain/BillSpec.hs[1-27]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +85 to 92
import DAL.Mutations (createAccPlan, deleteAccPlan, updateAccPlan)
import DAL.Queries (getAccPlanById, getAccPlans)
import DAL.Repository
import DAL.Types
import Data.Int (Int64)
import Domain.Accounting (AccAccount (..))
import Data.Text (Text)
import qualified Data.Text as T
import Hasql.Pool (Pool)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

3. data.text import order wrong 📘 Rule violation ⚙ Maintainability

Imports in DAL.Repository.AccPlan do not follow the documented grouping/ordering (qualified
imports should precede explicit imports, and groups should be ordered consistently). This reduces
readability and violates the import convention rule.
Agent Prompt
## Issue description
`src/DAL/Repository/AccPlan.hs` imports are not ordered/grouped per the documented convention (qualified imports should come before explicit imports).

## Issue Context
Reorder the `Data.Text` imports (and, if needed, regroup local vs external imports) to match the project's standard.

## Fix Focus Areas
- src/DAL/Repository/AccPlan.hs[83-93]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread src/Surypus/WebSocket.hs
Comment on lines +96 to +100
["ws", subPath] ->
websocketsOr
WS.defaultConnectionOptions
(jwtWebSocketAppWithPath hub mConfig (T.intercalate "/" subPath))
(\_ respond' -> respond' (responseLBS status400 [] "WebSocket endpoint expected"))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

4. Websocket subpath type mismatch 🐞 Bug ✓ Correctness

In Surypus.WebSocket, the route case ["ws", subPath] binds subPath as a single Text, but the
code passes it to T.intercalate "/" which expects [Text]. This prevents the module from
compiling.
Agent Prompt
### Issue description
`T.intercalate "/" subPath` is applied to a `Text` path segment, causing a type error.

### Issue Context
The `Wai.pathInfo` branch `["ws", subPath]` binds `subPath :: Text`.

### Fix Focus Areas
- src/Surypus/WebSocket.hs[90-101]

### Suggested fix
Either:
- Change the match to capture multiple segments: `("ws" : subPathSegments)` and use `T.intercalate "/" subPathSegments`, or
- Keep `["ws", subPath]` and pass `subPath` directly without `intercalate`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread src/Surypus/WebSocket.hs
Comment on lines +167 to +172
handleMessage :: WebSocketHub -> Int -> Text -> WS.DataMessage -> IO ()
handleMessage hub clientId path msg = do
clients <- readTVarIO (wshClients hub)
let filteredClients = filter (\(_, _, mPayload) -> canReceive path mPayload) clients
forM_ filteredClients $ \(_, connection, _) ->
WS.sendTextData connection (WS.DataMessage msg) `catch` \(_ :: SomeException) -> pure ()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

5. Websocket relay won't compile 🐞 Bug ✓ Correctness

Surypus.WebSocket.handleMessage uses forM_ without importing it and attempts `WS.sendTextData
connection (WS.DataMessage msg)`, which does not typecheck as written. This prevents compilation and
breaks message relay.
Agent Prompt
### Issue description
`handleMessage` doesn't compile due to missing `forM_` import and incorrect use of `WS.sendTextData`.

### Issue Context
`msg` is already a `WS.DataMessage` from `receiveDataMessage`.

### Fix Focus Areas
- src/Surypus/WebSocket.hs[15-25]
- src/Surypus/WebSocket.hs[167-173]

### Suggested fix
- Import `forM_` (e.g., `import Data.Foldable (forM_)` or add it to the existing import list), and
- Forward the message using the correct WebSockets API (e.g., `WS.sendDataMessage connection msg`) or pattern-match `msg` and send appropriate text/binary payloads.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread src/Surypus/JWT.hs
Comment on lines +4 to +15
module Surypus.JWT
( JWTPayload (..),
JWTConfig (..),
TokenPair (..),
defaultJWTConfig,
generateTokenPair,
generateAccessToken,
validateAccessToken,
generateRefreshToken,
validateRefreshToken,
refreshAccessToken,
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

6. Removed token api breaks build 🐞 Bug ✓ Correctness

Surypus.JWT no longer exports/defines generateSimpleToken, but APIServer still imports and calls
it. This causes a compile-time failure.
Agent Prompt
### Issue description
`generateSimpleToken` is referenced by APIServer but is not exported/implemented by Surypus.JWT anymore.

### Issue Context
This is a hard compile error due to missing symbol.

### Fix Focus Areas
- src/Surypus/JWT.hs[4-15]
- src/APIServer.hs[60-72]
- src/APIServer.hs[313-323]

### Suggested fix
Either:
- Re-introduce `generateSimpleToken` in `Surypus.JWT` as a compatibility wrapper (and export it), or
- Update APIServer to use `generateAccessToken`/`generateTokenPair` and handle the `Either` error path (and update the import list accordingly).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread src/Surypus/JWT.hs
Comment on lines 141 to +153
validateRefreshToken :: JWTConfig -> Text -> AppResult (Int, UTCTime)
validateRefreshToken config token = do
case T.splitOn ":" token of
[_uuid, uid, expStr] ->
case reads (T.unpack uid) of
[(uId, "")] ->
case reads (T.unpack expStr) of
[(exp, "")] -> Right (uId, exp)
_ -> Left (AuthError "Invalid expiration")
_ -> Left (AuthError "Invalid user id")
_ -> Left (AuthError "Invalid refresh token format")
let secret = TE.encodeUtf8 (jwtSecret config)
case jwtDecode secret token of
Right claims -> case sub claims of
Nothing -> Left (AuthError "Missing subject")
Just subClaim ->
case reads (T.unpack subClaim) of
[(uId, "")] ->
case exp claims of
Nothing -> Left (AuthError "Missing expiration")
Just expClaim -> Right (uId, read "1970-01-01 00:00:00 UTC" :: UTCTime)
_ -> Left (AuthError "Invalid subject format")
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

7. Refresh expiry discarded 🐞 Bug ⛨ Security

Surypus.JWT.validateRefreshToken ignores the parsed exp claim and always returns the Unix epoch as
the expiration time. Any refresh-token expiry logic using this result will be incorrect.
Agent Prompt
### Issue description
`validateRefreshToken` discards `expClaim` and returns a constant UTCTime.

### Issue Context
The function signature promises to return the refresh token's expiration time.

### Fix Focus Areas
- src/Surypus/JWT.hs[141-154]

### Suggested fix
Convert the numeric `exp` claim to `UTCTime` (e.g., using `posixSecondsToUTCTime`) and return it. Also consider validating `iss` (e.g., require `iss == "surypus-refresh"`) so access tokens cannot be used as refresh tokens.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread src/Surypus/JWT.hs
Comment on lines +102 to +105
case (jwtEncode (secret, HS256) accessClaims, jwtEncode (secret, HS256) refreshClaims) of
(Right accessToken, Right refreshToken) -> pure $ TokenPair accessToken refreshToken accessExpiration
(Left err, _) -> error (show err)
(_, Left err) -> error (show err)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

8. Jwt encode uses error 🐞 Bug ⛯ Reliability

Surypus.JWT.generateTokenPair and refreshAccessToken call error when jwtEncode fails, crashing the
server process. Token generation should surface failures as values (e.g., Either/AppResult)
instead of terminating.
Agent Prompt
### Issue description
JWT encoding failures crash the entire process via `error`.

### Issue Context
Even if failures are rare, this turns a recoverable error into a hard outage.

### Fix Focus Areas
- src/Surypus/JWT.hs[94-105]
- src/Surypus/JWT.hs[156-164]

### Suggested fix
Change these functions to return `IO (Either Text ...)` (or `AppResult ...`) and propagate the encoding error to callers. Update callers to handle the Left case (HTTP 500 / auth error response as appropriate).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

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