Repository navigation
Minbar v2027 - #17
Minbar v2027#17
Conversation
…s, controllers Converts all MusallahBoard domain models from MongoDB to JPA entities, creates JPA repositories under repository/sql/, rewrites BoardService and PosterService for JPA, updates transformers, assembler, controllers. Applies all model normalization fixes from PLAN.md section 0. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- All 6 board domain models converted from MongoDB to JPA entities - New JPA repositories under repository/sql/ - BoardService rewritten for JPA; PosterService repo swap - WeeklyContent redesigned: UUID PK, year/weekNumber, @onetomany children - IslamicQuote and JummahPrayer promoted to owned @entity rows - CalculationMethod enum replaces raw int method in Location - All model cleanup items A-E from PLAN.md applied - EventView extracted to top-level DTO, frame config coupling resolved - AuditEvent.timestamp fixed to Instant - pom.xml: spring-boot-starter-data-jpa + postgresql driver added - @EnableJpaRepositories wired in LensBridgeApplication Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…Ed25519 Adds the persistence layer and onboarding handshake for the new agent control plane that lets the admin dashboard run commands on each Pi. Layer 1 - entities + repos: - Device extended with publicKey, agentVersion, hardwareModel, lastSeenIp, revokedAt - EnrollmentToken: one-time admin-issued pairing token, SHA-256 hash stored, plaintext shown once - DeviceTelemetry: append-only heartbeat history - DeviceCommand + DeviceCommandStatus: audit log + delivery queue with state machine (PENDING -> DELIVERED -> ACKED -> RUNNING -> terminal) - DeviceRepository.findByRevokedAtIsNullAndLastHeartbeatBefore for offline detection Layer 2 - Ed25519 + enrollment: - Ed25519Verifier wraps JDK 17+ native Ed25519; raw 32-byte public keys wrapped to X.509 SubjectPublicKeyInfo at verify time - EnrollmentTokenService issues 144-bit base64 tokens, atomic single-consume with expiry check, optional pepper - DeviceEnrollmentService orchestrates token consume + Device row creation with public key - POST /api/agent/enroll (public, token-authenticated) for agent side; admin endpoints under /api/admin/board/devices for token issuance, list, revoke - WebSecurityConfig permits /api/agent/enroll - 12 unit tests (Ed25519 round-trip + tamper + wrong-key + bad-input; token issue + consume happy/expired/used/unknown/blank) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Wraps up the PostgreSQL/normalization migration started in the prior two commits by deleting now-dead types and tightening DTOs/services around the Device-as-aggregate-root model. - Remove BoardLocation enum, StringToBoardLocationConverter, and the legacy Mongo-shaped BoardConfigRepository under repository/ in favour of the embedded Location + DeviceConfig under board/embedded - Rename BoardConfig.java -> board/embedded/DeviceConfig.java - Slim DTOs (Create/UpdatePosterRequest, Create/UpdateCalendarEventRequest, EventView, MusallahBoardPayload) - drop fields that no longer map to the JPA entities - BoardAdminController, MusallahBoardController: route everything through deviceId; drop boardLocation path params - BoardService, PosterService, BoardContext, BoardPayloadAssembler, DailyScheduleFrameTransformer, EventListFrameTransformer: align with the device-keyed lookup - Update docs/MUSALLAH_BOARD_API.md and add docs/musallahboard-api.md Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
… this is vibe coded :)
Co-authored-by: Copilot <copilot@github.com>
Frame revamp
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4e57474d95
ℹ️ 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".
| SPRING_PROFILES_ACTIVE: prod | ||
| POSTGRES_HOST: postgres | ||
| POSTGRES_PORT: "5432" | ||
| POSTGRES_DB: ${POSTGRES_DB} | ||
| POSTGRES_USER: ${POSTGRES_USER} | ||
| POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} |
There was a problem hiding this comment.
Add runnable dev and prod configuration
There is no tracked application-dev.properties or application-prod.properties; the only main resource is application.properties.example, which Spring does not load. This Compose service selects prod but supplies POSTGRES_* variables rather than a spring.datasource.url (or SPRING_DATASOURCE_URL), so DataSource auto-configuration fails before the health check. The same missing dev configuration also makes the inspected OpenAPI workflow's -P openapi boot step fail, since it explicitly starts the dev profile while required datasource, JWT, R2, and CORS properties have no values.
Useful? React with 👍 / 👎.
| # Flyway's classpath scan doesn't restrict to the resolved subdirectory -- it scans both | ||
| # db/migration/sqlite and db/migration/postgresql and fails startup with "Found more than | ||
| # one migration with version 1". Confirmed against prod on 2026-08-10; see docs/MIGRATIONS.md. | ||
| # spring.flyway.locations=classpath:db/migration/postgresql |
There was a problem hiding this comment.
Select one migration directory in the sample config
Following the README's documented copy step leaves this setting commented out, so Flyway falls back to classpath:db/migration and discovers both the SQLite and PostgreSQL V1__initial_schema.sql files. Since both have version 1, startup aborts with Flyway's duplicate-migration-version error; set the SQLite location in the base dev sample or provide the promised per-profile settings.
Useful? React with 👍 / 👎.
| EnrollmentToken row = found.get(); | ||
| Instant now = Instant.now(); | ||
| if (row.getConsumedAt() != null) return Optional.empty(); | ||
| if (row.getExpiresAt().isBefore(now)) return Optional.empty(); | ||
|
|
||
| row.setConsumedAt(now); | ||
| row.setConsumedByDeviceId(consumedByDeviceId); | ||
| return Optional.of(repository.save(row)); |
There was a problem hiding this comment.
Claim enrollment tokens atomically
When two agents submit the same valid token concurrently, each transaction can read consumedAt == null before either update commits. On PostgreSQL, the second update waits for the first and then still overwrites the row because there is no lock, version column, or conditional UPDATE ... WHERE consumed_at IS NULL; both calls return a token and proceed to create devices. Use a pessimistic lock or a conditional update whose affected-row count is checked before enrollment.
Useful? React with 👍 / 👎.
This PR serves as the initial version for Minbar v2027, supporting MusallahBoard as a first-class product and working towards deprecating LensBridge's existing API.
Eventually, the rest of Minbar's features (mobile app, etc) will be merged in (from branch minbar-features), however those still need more testing + cleaning up, so those will follow