Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
248 commits
Select commit Hold shift + click to select a range
c7a510d
feat(ai-calling): let a workflow re-call leads a counsellor already owns
Manshu48 Aug 21, 2026
8717a19
fix(voice-bot): stop the filler firing into the caller's own speech
Manshu48 Aug 24, 2026
9848b25
Merge pull request #2647 from Vacademy-io/feature/tts-speech-cache
Manshu48 Aug 24, 2026
13a191b
feat(super-admin): cost TTS from the characters actually synthesised
Manshu48 Aug 24, 2026
fd6d4c1
Surface backend SCORM upload error message in Add SCORM dialog
neerajhariyale Aug 24, 2026
cbb016a
Merge pull request #2649 from Vacademy-io/feature/tts-speech-cache
Manshu48 Aug 24, 2026
416dfbc
Merge branch 'main' of https://github.com/Vacademy-io/vacademy_platform
neerajhariyale Aug 24, 2026
ebd2c82
feat: institute-scoped app-status visibility + live App Store Connect…
neerajhariyale Aug 24, 2026
40c4d60
Merge pull request #2650 from Vacademy-io/feat/app-status-institute-v…
neerajhariyale Aug 24, 2026
1e4a1f4
feat: register DumBee white-label app (Android flavor + iOS target)
neerajhariyale Aug 24, 2026
f2d082b
Make the speed indicator debuggable, not just descriptive
Aug 24, 2026
d964b16
added spped accuracy measurement
Aug 24, 2026
60ed8b3
Merge pull request #2652 from Vacademy-io/feature/tts-speech-cache
Manshu48 Aug 24, 2026
79ef7f4
Merge pull request #2653 from Vacademy-io/feature/tts-speech-cache
Manshu48 Aug 24, 2026
f638c3a
feat(voice-bot): pronunciation fixes that apply to every engine, not …
Manshu48 Aug 25, 2026
3cbaa8e
fix(voice-bot): a smallest_pro agent was being sent to the STANDARD m…
Manshu48 Aug 25, 2026
6c2fbb6
fix(white-label): serve the institute favicon, not the Vacademy mark
samark231 Aug 25, 2026
a0a7451
Merge pull request #2654 from Vacademy-io/fix/white-label-favicon
samark231 Aug 25, 2026
2a53572
fix(learner-app): route Shiksha Nation Android/Electron app through t…
neerajhariyale Aug 25, 2026
8004b3f
Merge branch 'main' of https://github.com/Vacademy-io/vacademy_platform
neerajhariyale Aug 25, 2026
81eba56
fix(voice-bot): the preview model cap was shorter than its own longes…
Manshu48 Aug 25, 2026
b169ab3
fix(app-status): wire community.server.baseurl to the running COMMUNI…
neerajhariyale Aug 25, 2026
83e187d
Merge pull request #2655 from Vacademy-io/fix/app-status-community-se…
neerajhariyale Aug 25, 2026
87d0533
fix(attendance-widget): make the period windows honest about what the…
Manshu48 Aug 25, 2026
5d74114
Merge pull request #2656 from Vacademy-io/feature/tts-speech-cache
Manshu48 Aug 25, 2026
1905796
fix(super-admin): summary read the wrong two columns for measured TTS…
Manshu48 Aug 25, 2026
97f824c
added bbb new apis
Aug 25, 2026
af3b7e2
Merge origin/main into feature/onboarding-flow
Manshu48 Aug 25, 2026
cfb4cfe
Merge pull request #2657 from Vacademy-io/fix/onboarding-merge
Manshu48 Aug 25, 2026
d84bd4c
Merge pull request #2658 from Vacademy-io/feature/tts-speech-cache
Manshu48 Aug 25, 2026
a48f318
updated bbb workflow
Aug 25, 2026
0c1c03b
Merge remote-tracking branch 'origin/main'
Aug 25, 2026
e661492
added bbb start issues fix
Aug 25, 2026
52ab9d1
feat(tts-cache): render a sentence on its first sighting, not its second
Manshu48 Aug 25, 2026
9d15d6a
Merge pull request #2659 from Vacademy-io/feat/cache-min-seen-1
Manshu48 Aug 25, 2026
b007a02
fix(tts-cache): a cache hit now emits a TTSTextFrame, so it counts as…
Manshu48 Aug 25, 2026
6781888
fix(tts-cache): only emit our TTSTextFrame where pipecat emits none
Manshu48 Aug 25, 2026
eb2aaa3
Merge pull request #2660 from Vacademy-io/feat/cache-min-seen-1
Manshu48 Aug 25, 2026
3980d4f
added fixes for bbb issues
Aug 25, 2026
412efa5
Merge remote-tracking branch 'origin/main'
Aug 25, 2026
a6d39ba
fix(chat): make delete/flag reachable, add message editing
neerajhariyale Aug 25, 2026
c234c47
Merge pull request #2661 from Vacademy-io/fix/chat-message-actions
neerajhariyale Aug 25, 2026
337dd0a
fix(tts-cache): a hit now creates its attribution row instead of vani…
Manshu48 Aug 25, 2026
89834c8
feat(chat): learner message edit/delete, gated by institute settings
neerajhariyale Aug 25, 2026
376a380
Merge pull request #2662 from Vacademy-io/feat/chat-student-message-s…
neerajhariyale Aug 25, 2026
3ac3827
Serve institute custom live-class domains end to end
Aug 25, 2026
925f6f4
Merge remote-tracking branch 'origin/main'
Aug 25, 2026
4c24879
feat(evaluation): rework answer sheet evaluation tool for faster grading
neerajhariyale Aug 25, 2026
e8dff98
fix(evaluation): close three grading-correctness gaps from the rotate…
neerajhariyale Aug 25, 2026
389461a
Merge pull request #2663 from Vacademy-io/feat/answer-sheet-evaluatio…
neerajhariyale Aug 25, 2026
9deeba9
Merge pull request #2664 from Vacademy-io/feat/cache-min-seen-1
Manshu48 Aug 25, 2026
e91f1fd
fix(tts-cache): one audio context per sentence, so a hit can serve mi…
Manshu48 Aug 25, 2026
2bdd548
fix(chat): learner /chat crashed on load — handler used before declar…
neerajhariyale Aug 25, 2026
5bc3be7
Merge pull request #2665 from Vacademy-io/feat/cache-min-seen-1
Manshu48 Aug 25, 2026
254ee87
Fix submission list reshuffling on save; add batch not-attempted list
neerajhariyale Aug 25, 2026
32ac461
Merge pull request #2666 from Vacademy-io/fix/submission-list-orderin…
neerajhariyale Aug 25, 2026
be58593
Fix Provide Reattempt on PRIVATE assessments
neerajhariyale Aug 25, 2026
d1ee048
Merge pull request #2667 from Vacademy-io/fix/reattempt
neerajhariyale Aug 25, 2026
b439402
Order the assessment list by exam start date, not creation date
neerajhariyale Aug 25, 2026
8a6436a
Merge pull request #2668 from Vacademy-io/fix/assessment-list-order-b…
neerajhariyale Aug 25, 2026
0526c33
fix(html-page): body styles cannot match inside a shadow root either
Aug 25, 2026
5ee05b4
Merge remote-tracking branch 'origin/main'
Aug 25, 2026
9227e3c
Add a not-attempted CSV and contact columns to the participant exports
neerajhariyale Aug 25, 2026
c271c24
Merge pull request #2669 from Vacademy-io/feat/not-attempted-csv-and-…
neerajhariyale Aug 25, 2026
9228bf0
Redesign the assessment list filter bar and fix its responsive layout
neerajhariyale Aug 25, 2026
b9420d5
Merge pull request #2670 from Vacademy-io/fix/assessment-list-filter-…
neerajhariyale Aug 25, 2026
6ebae56
fix(study-library): scope quiz correct-answer radios per block
neerajhariyale Aug 25, 2026
3259841
Merge pull request #2671 from Vacademy-io/fix/quiz-radio-group-collision
neerajhariyale Aug 25, 2026
a2084b0
ZOE: rename app to "ZOE Online School" across all four platforms
neerajhariyale Aug 25, 2026
6295eab
ZOE: stop committing the build-patched electron/package.json
neerajhariyale Aug 25, 2026
b21f4f9
ZOE iOS: sign against the team that now owns the app
neerajhariyale Aug 25, 2026
80a550c
feat(live-session): time attendance to the second, and explain every …
Manshu48 Aug 25, 2026
ec439db
Merge pull request #2672 from Vacademy-io/feature/onboarding-flow
Manshu48 Aug 25, 2026
8b7c659
feat(live-session): put the attended time in the present mail too
Manshu48 Aug 25, 2026
ab8d0f9
chore(live-session): move the attendance-seconds migration to V469 ->…
Manshu48 Aug 25, 2026
9bc6333
chore(live-session): renumber the attendance-seconds migration to V471
Manshu48 Aug 25, 2026
6dfc91d
Merge pull request #2673 from Vacademy-io/feature/onboarding-flow
Manshu48 Aug 25, 2026
f576377
feat(enroll-invite): notify the team when someone fills an invite form
Manshu48 Aug 25, 2026
2d720d9
Merge pull request #2674 from Vacademy-io/feature/onboarding-flow
Manshu48 Aug 26, 2026
8800239
ZOE Windows: stop shipping the package with Shiksha Nation's identity
neerajhariyale Aug 26, 2026
ad5412c
ZOE Windows: give ZOE its own version line
neerajhariyale Aug 26, 2026
f4f60fc
Fix: drop the _comment key that broke the AppX build
neerajhariyale Aug 26, 2026
cba64c5
ZOE iOS: stop claiming other brands' applinks domains
neerajhariyale Aug 26, 2026
f542886
feat(html-page): let a page hide the site header and footer
Aug 26, 2026
79db924
Merge remote-tracking branch 'origin/main'
Aug 26, 2026
c9b9705
ZOE: settle the Electron app name now, while the migration is already…
neerajhariyale Aug 26, 2026
6622485
Assessment: knowledge-base integration + grading and flow fixes
Manshu48 Aug 26, 2026
0647d19
Merge pull request #2620 from Vacademy-io/feature/ai-recall-on-lead-s…
Manshu48 Aug 26, 2026
b582e41
Mac App Store: stop packaging electron-builder's toolchain into shipp…
neerajhariyale Aug 26, 2026
c5b1ea3
Merge pull request #2675 from Vacademy-io/feature/zoe-online-school-r…
neerajhariyale Aug 26, 2026
105df0b
fix(ai-calling): never rotate a lead away from the counsellor who own…
Manshu48 Aug 26, 2026
90f7779
Ignore the remaining electron-builder output directories
neerajhariyale Aug 26, 2026
c3ec93e
Merge pull request #2676 from Vacademy-io/feature/ai-recall-on-lead-s…
Manshu48 Aug 26, 2026
c2dd4de
Merge remote-tracking branch 'origin/main'
neerajhariyale Aug 26, 2026
bf0d386
feat(ai-calling): fleet-wide call queue in front of every AI dial
Manshu48 Aug 26, 2026
3ed036e
Merge remote-tracking branch 'origin/main' into feature/ai-call-queue
Manshu48 Aug 26, 2026
70c74ab
Merge pull request #2677 from Vacademy-io/feature/ai-call-queue
Manshu48 Aug 26, 2026
be41c8a
Merge origin/main into assessment KB + grading work
Manshu48 Aug 26, 2026
8b8320f
feat(ai-calling): gate the call Queue tab behind Display Settings, of…
Manshu48 Aug 26, 2026
d826db6
Revert "feat(ai-calling): gate the call Queue tab behind Display Sett…
Manshu48 Aug 26, 2026
5d6796c
Merge pull request #2678 from Vacademy-io/feature/onboarding-flow
Manshu48 Aug 26, 2026
eb5b84b
feat(ai-calling): move the call queue to its own Calling tab, hide th…
Manshu48 Aug 26, 2026
a8ab31f
Merge remote-tracking branch 'origin/main' into feature/ai-call-queue
Manshu48 Aug 26, 2026
a75320e
Merge pull request #2679 from Vacademy-io/feature/ai-call-queue
Manshu48 Aug 26, 2026
10d00dc
Fix Zoom live classes that could never start, and so never recorded
neerajhariyale Aug 26, 2026
f6133a3
The Learning Bridge iOS: compile MainViewController into the app target
neerajhariyale Aug 26, 2026
433eed4
Google Meet: stop the "UNKNOWN" placeholder sticking to provisioned o…
neerajhariyale Aug 26, 2026
1ed9dfd
Merge pull request #2680 from Vacademy-io/fix/zoom-live-session-recor…
neerajhariyale Aug 26, 2026
916ea82
fix(ai-calling): queue primary keys are VARCHAR, not UUID
Manshu48 Aug 26, 2026
e0479ce
Merge pull request #2681 from Vacademy-io/feature/ai-call-queue
Manshu48 Aug 26, 2026
5c14a89
fix(ai-calling): queue page defaults to Active, and shows the number …
Manshu48 Aug 26, 2026
fb60d9e
Merge pull request #2682 from Vacademy-io/feature/ai-call-queue
Manshu48 Aug 26, 2026
a6c63f9
refactor(ai-calling): drop the internal queue endpoint, serve the hea…
Manshu48 Aug 26, 2026
07db153
Merge pull request #2683 from Vacademy-io/feature/ai-call-queue
Manshu48 Aug 26, 2026
076a3b4
fix(product-page): provision enrollment before Razorpay checkout
Manshu48 Aug 26, 2026
082f2ac
added localization
Aug 26, 2026
50dcaea
Price product-page baskets, add offers, and verify fields by OTP
neerajhariyale Aug 26, 2026
cf330df
fix(assessment): send reattempt status filter without brackets
neerajhariyale Aug 26, 2026
aa7f5be
fix(evaluation): stop a failed submit losing the evaluator
neerajhariyale Aug 26, 2026
3779c11
feat(assessment): contact columns and Manage Column on submissions
neerajhariyale Aug 26, 2026
0cc86ed
Merge pull request #2684 from Vacademy-io/feat/product-page-pricing-o…
neerajhariyale Aug 26, 2026
fc631d2
feat: per-institute store credentials, replacing the single shared Ap…
neerajhariyale Aug 26, 2026
cb49d9b
Merge pull request #2685 from Vacademy-io/feat/store-credentials-mult…
neerajhariyale Aug 26, 2026
a32e2ee
fix: add the signup-availability hook the catalogue already imports
neerajhariyale Aug 26, 2026
7450d1a
fix: render the checkout's order summary, which PR #2684 left orphaned
neerajhariyale Aug 26, 2026
052ec2e
fix: stop the cart promising a plan choice that is not on screen
neerajhariyale Aug 26, 2026
8449fbb
feat: price a full grade pack per class, not by subject count
neerajhariyale Aug 26, 2026
e80d662
feat: make the cart the content of the cart step
neerajhariyale Aug 26, 2026
faee711
Merge pull request #2686 from Vacademy-io/fix/product-page-cart-ui
neerajhariyale Aug 26, 2026
ffe8fc9
i18n rollout (audience-manager, video-api-studio, admissions, manage-…
Aug 27, 2026
1e537fd
Merge branch 'main' of https://github.com/Vacademy-io/vacademy_platform
Aug 27, 2026
348906e
chore: regenerate routeTree.gen.ts after merge
Aug 27, 2026
a49a157
fix(frontend-admin-dashboard): repair broken pnpm-workspace.yaml + st…
Aug 27, 2026
7d7808a
fix(live-session): surface "Start as Host" on recurring sessions
neerajhariyale Aug 27, 2026
624e64c
Merge pull request #2687 from Vacademy-io/fix/recurring-zoom-start-as…
neerajhariyale Aug 27, 2026
d834dad
fix(frontend-admin-dashboard): make pnpm config valid for pnpm 10 (CI…
Aug 27, 2026
412bb6a
Merge branch 'main' of https://github.com/Vacademy-io/vacademy_platform
Aug 27, 2026
1162f49
loadtest: k6 exam-journey harness + capacity runbook
Aug 27, 2026
e93b3e0
perf(auth): exam-burst Hikari sizing + loadtest boundary hardening
Aug 27, 2026
a89ef79
perf(frontend-admin-dashboard): stop building canvas natively in CI
Aug 27, 2026
5a1a37b
perf(frontend-admin-dashboard): serve locale catalogs statically, not…
Aug 27, 2026
912de61
feat: price baskets by discounting the enroll-invite price
neerajhariyale Aug 27, 2026
b42f198
Assessment: default the learner preview to off
Manshu48 Aug 27, 2026
589ff71
fix: keep the approval switch usable when editing a plan
neerajhariyale Aug 27, 2026
7e4e3d5
Merge remote-tracking branch 'origin/main' into feat/basket-discount-…
neerajhariyale Aug 27, 2026
641e635
Assessment: default the learner preview to off
Manshu48 Aug 27, 2026
c40d4f9
Merge pull request #2689 from Vacademy-io/fix/assessment-preview-defa…
Manshu48 Aug 27, 2026
a301522
Merge pull request #2688 from Vacademy-io/feat/basket-discount-from-e…
neerajhariyale Aug 27, 2026
468dca7
perf(frontend-admin-dashboard): run tsc and vite serially — concurren…
Aug 27, 2026
386444a
Merge branch 'main' of https://github.com/Vacademy-io/vacademy_platform
Aug 27, 2026
5ff9665
fix(settings): forward approval props to the edit-plan dialog's editor
Aug 27, 2026
2dde081
MAS: drop the unused camera entitlement (Guideline 2.4.5(i))
neerajhariyale Aug 27, 2026
63a5084
Merge pull request #2690 from Vacademy-io/fix/mas-remove-unused-camer…
neerajhariyale Aug 27, 2026
af595f9
added fixes
neerajhariyale Aug 27, 2026
162ab96
Merge pull request #2691 from Vacademy-io/fix/slide-naming
neerajhariyale Aug 27, 2026
d2e7d9c
feat(ai-calling): ops control for simultaneous AI calls, and rename t…
Manshu48 Aug 27, 2026
bd6f167
fix(frontend-admin-dashboard): evict stale PWA service workers pinnin…
Aug 27, 2026
1c19246
Merge branch 'main' of https://github.com/Vacademy-io/vacademy_platform
Aug 27, 2026
48542a1
fix(frontend-admin-dashboard): seed merged locale catalogs before fir…
Aug 27, 2026
63ccd5b
fix(custom-css): the banned-CSS guard was eating scroll-behavior
Aug 27, 2026
47810c1
fix: honour showFooter on the product page's catalogue
neerajhariyale Aug 27, 2026
d5ce7e7
Merge pull request #2692 from Vacademy-io/fix/product-page-footer
neerajhariyale Aug 27, 2026
5d20d8c
fix: show the basket saving on the catalogue bar too
neerajhariyale Aug 27, 2026
06d7254
Merge pull request #2693 from Vacademy-io/fix/cc-pg
neerajhariyale Aug 27, 2026
96d2d57
fix(custom-css): removing only the @import KEYWORD deleted the page's…
Aug 27, 2026
26c21b3
Merge remote-tracking branch 'origin/main'
Aug 27, 2026
e90a5b9
fix(live-session): judge attendance against the class that actually h…
Manshu48 Aug 27, 2026
40b3c76
fix(html-page): hide-site-chrome never ran on the route that renders …
Aug 27, 2026
4affccb
fix(html-page): drop the header-clearance padding with the hidden header
Aug 27, 2026
78c37d8
fix(product-page): credit the children, not the parent, on a provisio…
Manshu48 Aug 27, 2026
812bb69
fix: translate the cart flow and send Back to the catalogue
neerajhariyale Aug 27, 2026
6d71e5e
Merge pull request #2695 from Vacademy-io/fix/cart-i18n-and-back-to-c…
neerajhariyale Aug 27, 2026
a5e7d49
fix(live-session): stop inviting learners to a class they were marked…
Manshu48 Aug 27, 2026
5069e51
feat(assessment): rework submissions tab, export and preview UI
neerajhariyale Aug 27, 2026
447b157
Merge pull request #2696 from Vacademy-io/feat/assessment-submissions…
neerajhariyale Aug 27, 2026
4869817
Merge origin/main into feature/onboarding-flow
Manshu48 Aug 27, 2026
5c915bc
Merge origin/feature/onboarding-flow
Manshu48 Aug 27, 2026
7f8a70a
fix(live-session): keep the attendance changes inside the opted-in cl…
Manshu48 Aug 27, 2026
5d9b997
Merge pull request #2694 from Vacademy-io/feature/onboarding-flow
Manshu48 Aug 27, 2026
4411683
feat: discount a basket by what it is worth, not only how many
neerajhariyale Aug 27, 2026
a07b6f1
fix(live-session): import AttendanceEmailBody so admin-core compiles
Manshu48 Aug 27, 2026
050e43b
Merge pull request #2697 from Vacademy-io/feature/onboarding-flow
Manshu48 Aug 27, 2026
1a6ba23
fix(live-class): stop the calendar crashing before its i18n catalog l…
neerajhariyale Aug 27, 2026
0284c0a
fix(assessment): scope reattempt request badge to the assessment
neerajhariyale Aug 27, 2026
7a5bbde
Merge pull request #2698 from Vacademy-io/feat/basket-amount-tiers
neerajhariyale Aug 27, 2026
723a30d
addded fixes
neerajhariyale Aug 27, 2026
f8bc8da
Merge pull request #2699 from Vacademy-io/fix/fix
neerajhariyale Aug 27, 2026
3405814
copy(live-session): point learners at the faculty on a disputed atten…
Manshu48 Aug 27, 2026
b7f10f8
docs(crm): AI call queue system reference
Manshu48 Aug 27, 2026
332bead
revert(live-session): drop the bespoke attendance email template
Manshu48 Aug 27, 2026
db3e832
feat(live-session): note the faculty contact on a criteria-driven att…
Manshu48 Aug 27, 2026
31a0a46
Merge pull request #2700 from Vacademy-io/feature/onboarding-flow
Manshu48 Aug 27, 2026
1a0ff4e
chore(ai-calling): drop V474, the fleet limit needs no migration
Manshu48 Aug 27, 2026
3f4bb7f
Merge pull request #2701 from Vacademy-io/feature/ai-call-queue
Manshu48 Aug 27, 2026
dc9a0f4
feat(html-page): point pasted buttons anywhere from the editor — no H…
Aug 27, 2026
9d83c81
HR & Payroll Wave 1: security hardening + payroll crash/state-machine…
Aug 27, 2026
9542ed2
loadtest: harness validated end-to-end against prod; tenant seeded
Aug 27, 2026
1863347
hotfix(flyway): renumber HR hardening migration V200 -> V480
Aug 27, 2026
4591c2e
loadtest: learner emails -> @vacademy.com (filtered domain, no outbou…
Aug 27, 2026
573e9ed
fix: wizard label, cart Back destination, and the Electron appId
neerajhariyale Aug 27, 2026
ad5559e
Merge pull request #2703 from Vacademy-io/fix/product-page-checkout-f…
neerajhariyale Aug 27, 2026
40e7de8
HR & Payroll Waves 2-3 + Phases D-E: correct engine, complete pipelin…
Aug 27, 2026
41e12fe
fix: render product-page logos for the visitors who are logged out
neerajhariyale Aug 27, 2026
968278c
Merge pull request #2704 from Vacademy-io/fix/pp
neerajhariyale Aug 27, 2026
84a7d33
fix: give checkout the site's own header, and its logos
neerajhariyale Aug 27, 2026
343a1c2
Merge pull request #2705 from Vacademy-io/fix/product-page-site-chrome
neerajhariyale Aug 27, 2026
76c0af3
perf(assessment): make post-submit LLM analytics truly async — the 10…
Aug 27, 2026
b52a9ed
fix(assessment): stop the live-sync recalc from clobbering a concurre…
Aug 27, 2026
27503bb
loadtest: record the 2026-08-27 measured ramp results and both bugs f…
Aug 27, 2026
21af16a
chore(devops): make assessment-service replicas overridable per service
Aug 27, 2026
9df9776
chore(devops): pin assessment-service to 2 replicas in the prod overlay
Aug 27, 2026
2ea70bd
ERP Phase F: connected layer — GL journal, teaching pay, CRM incentiv…
Aug 28, 2026
2a6a08b
ERP UI U1: new ERP sidebar category + People and Payroll screens
Aug 28, 2026
50fe4e9
fix(assessment): restore JPA no-arg constructors — announcements brok…
Aug 28, 2026
c73ae9b
loadtest: PDF answer-sheet (MANUAL evaluation) exam journey
Aug 28, 2026
420ccb2
ERP UI U2 (partial): payslips, bank files, and the finance journal
Aug 28, 2026
0b860e2
feat(html-page): "Enrol / invite link" as a link destination
Aug 28, 2026
3881bbc
ERP UI: compliance module — statutory filings, challans, provisions
Aug 28, 2026
fdce9ad
feat(reporting): CRM sections — admissions pipeline and calling activity
Aug 28, 2026
1fd5827
feat(analytics): first-party analytics for catalogue sites — storage …
Aug 28, 2026
437d83e
ERP UI U3: attendance and leave
Aug 28, 2026
4d2aea8
feat(analytics): send first-party page views from catalogue sites
Aug 28, 2026
a55c97d
feat(analytics): Traffic tab — the admin-facing half
Aug 28, 2026
88bc8b9
ERP U4 foundation: self-service plumbing + My HR nav
Aug 28, 2026
e9ff9a4
ERP UI U4 + U5: My HR self-service, teaching pay and CRM incentives
Aug 28, 2026
0a2af76
fix: show the wizard on every step, and let a catalogue name its own …
neerajhariyale Aug 28, 2026
62f96c0
Merge origin/main into fix/wizard-rail-and-catalogue-naming
neerajhariyale Aug 28, 2026
eee093b
Merge pull request #2706 from Vacademy-io/fix/wizard-rail-and-catalog…
neerajhariyale Aug 28, 2026
6bc2289
feat(dashboard): one standalone widget per enrolled course
neerajhariyale Aug 28, 2026
a406e0f
Merge remote-tracking branch 'origin/main'
Aug 28, 2026
1507c5b
Merge pull request #2707 from Vacademy-io/feat/enrolled-courses-widget
neerajhariyale Aug 28, 2026
1a7c8d6
fix(html-page): derive the link rows and page list the editor already…
Aug 28, 2026
f0006fa
Merge remote-tracking branch 'origin/main'
Aug 28, 2026
d93dd15
fixed build error
neerajhariyale Aug 28, 2026
fdf6c69
Merge remote-tracking branch 'origin/main'
Aug 28, 2026
88dc9d6
fix: keep the wizard with a visitor who is mid-purchase
neerajhariyale Aug 28, 2026
d1b0f67
Merge pull request #2708 from Vacademy-io/fix/wizard-follows-the-basket
neerajhariyale Aug 28, 2026
41d9d5e
fix(erp): make the ERP modules reachable — settings tab + single opt-…
Aug 28, 2026
ee1843e
Merge remote-tracking branch 'origin/main'
Aug 28, 2026
174d2a9
fix(hr): import EmployeeProfile in EmployeeController — unbreaks the …
Aug 28, 2026
d4b6447
fix: put the catalogue step rail below the header, not under it
neerajhariyale Aug 28, 2026
476e5a6
Merge pull request #2709 from Vacademy-io/fix/catalogue-rail-under-he…
neerajhariyale Aug 28, 2026
4ec1e5c
fix(hr): unbreak the five ERP endpoints failing on a fresh institute
Aug 28, 2026
bcb41ef
revert: take the checkout step rail off the public catalogue
neerajhariyale Aug 28, 2026
6b94023
Merge pull request #2710 from Vacademy-io/revert/catalogue-step-rail
neerajhariyale Aug 28, 2026
17d2b52
fix(catalogue): reopen Course Finder on "Back to courses" without wip…
neerajhariyale Aug 28, 2026
60271c2
Merge pull request #2711 from Vacademy-io/fix/catalogue-finder-basket…
neerajhariyale Aug 28, 2026
0d41c3f
fix(hr): source the staff roster entirely from auth_service
Aug 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
The diff you're trying to view is too large. We only load the first 3000 changed files.
148 changes: 137 additions & 11 deletions .github/workflows/bbb-pool-manage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,34 @@ jobs:
sed -i "s|defaultUploadedPresentation=https://[^/]*/|defaultUploadedPresentation=https://$DOMAIN/|g" \
/etc/bigbluebutton/bbb-web.properties 2>/dev/null || true

# Step 6: TURN / coturn — NOT covered by the sweep in Step 2.
# That sweep walks directories and filters by extension; /etc/turnserver.conf
# is under none of those directories and /etc/haproxy/haproxy.cfg matches none
# of those extensions, so the snapshot's build-time IP survived here on EVERY
# restore. coturn then cannot bind a foreign address and exits, which silently
# kills audio/video for every learner behind a UDP-blocking firewall while
# everyone on a permissive network is unaffected.
if [ -f /etc/turnserver.conf ]; then
for old in $(grep -oP '^\s*(listening-ip|relay-ip|allowed-peer-ip)=\K[\d.]+' \
/etc/turnserver.conf 2>/dev/null | grep -v '^127\.' | sort -u); do
if [ "$old" != "$NEW_IP" ]; then
echo " TURN: $old -> $NEW_IP"
sed -i "s/\b$old\b/$NEW_IP/g" /etc/turnserver.conf
fi
done
grep -q '^listening-ip=127\.0\.0\.1' /etc/turnserver.conf || \
sed -i "/^listening-ip=$NEW_IP/a listening-ip=127.0.0.1" /etc/turnserver.conf
systemctl restart coturn 2>/dev/null || true
echo " coturn: $(systemctl is-active coturn 2>/dev/null)"
fi

# Pin haproxy's TURN backend to loopback — IP-proof from here on.
if [ -f /etc/haproxy/haproxy.cfg ]; then
sed -i 's|^\(\s*server localhost \)[0-9.]\+:3478|\1127.0.0.1:3478|' /etc/haproxy/haproxy.cfg
haproxy -c -f /etc/haproxy/haproxy.cfg >/dev/null 2>&1 \
&& systemctl reload haproxy 2>/dev/null || echo " WARNING: haproxy config test failed"
fi

echo "BBB IP fix complete: $DOMAIN ($NEW_IP)"
SCRIPT

Expand Down Expand Up @@ -368,17 +396,16 @@ jobs:
SSH_OPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=15"

echo "[${{ matrix.slug }}] Shipping recording hook + heal service + health dashboard files to $IP"
ssh $SSH_OPTS root@$IP "mkdir -p /tmp/vacademy-bbb"
scp $SSH_OPTS \
bigbluebutton-server/post-publish-s3-upload.sh \
bigbluebutton-server/install-recording-hook.sh \
bigbluebutton-server/bbb-heal-service.py \
bigbluebutton-server/bbb-heal-service.service \
bigbluebutton-server/vacademy-heal.nginx \
bigbluebutton-server/bbb-health-dashboard.py \
bigbluebutton-server/bbb-health-dashboard.service \
bigbluebutton-server/vacademy-health.nginx \
root@$IP:/tmp/vacademy-bbb/
# Copy the WHOLE directory rather than a hand-maintained file list.
# install-recording-hook.sh reads several paths relative to its own
# directory — systemd/bbb-rap-resque-worker.service.d/lowprio.conf, the
# highprio.conf drop-ins, bbb-recording-drain.sh, bbb-rap-autotune.sh and
# their units — none of which were in the old explicit list, so step 9/10
# died on `cp: cannot stat` and took the whole start job down with it.
# A recursive copy means adding a file to the repo can never again break
# the daily start.
ssh $SSH_OPTS root@$IP "rm -rf /tmp/vacademy-bbb && mkdir -p /tmp/vacademy-bbb"
scp -r $SSH_OPTS bigbluebutton-server/* root@$IP:/tmp/vacademy-bbb/

# Read BBB secret from the running server — no need to store per-server secrets in GitHub
echo "[${{ matrix.slug }}] Reading BBB secret from server"
Expand All @@ -404,6 +431,105 @@ jobs:

echo "[${{ matrix.slug }}] Recording hook + heal service + health dashboard synced successfully"

- name: Sync per-institute custom live-class domains
# Aliases resolve to the PRIMARY pool server only (BbbServerRouter.isPrimary),
# so there is nothing to do on any other server in the pool.
#
# `!cancelled()` deliberately decouples this from the recording-hook step:
# those are unrelated concerns, and a recording-pipeline problem should not
# leave an institute's live-class domain pointing at a dead IP. The job still
# reports failure if the earlier step failed.
if: "!cancelled() && steps.check.outputs.skip != 'true' && matrix.priority == 1"
env:
SSH_PRIVATE_KEY: ${{ secrets.BBB_SSH_PRIVATE_KEY }}
INTERNAL_SERVICE_TOKEN: ${{ secrets.INTERNAL_SERVICE_TOKEN }}
CF_TOKEN: ${{ secrets.BBB_CLOUDFLARE_API_TOKEN }}
VACADEMY_BACKEND_URL: ${{ vars.VACADEMY_BACKEND_URL }}
run: |
set -uo pipefail
# GitHub runs this step as `bash -e {0}`, so ANY failing command — a jq
# parse error, a transient curl — aborts before the explicit guards below
# can turn it into a warning. This step does its own error handling and
# decides deliberately what is fatal, so -e is turned off here.
set +e
IP="${{ steps.poweron.outputs.server_ip }}"
[ -z "$IP" ] && IP="${{ steps.create.outputs.server_ip }}"
BACKEND_URL="${VACADEMY_BACKEND_URL:-https://backend-stage.vacademy.io}"

mkdir -p ~/.ssh; echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa; chmod 600 ~/.ssh/id_rsa
SSH_OPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=15"

# ── Fetch the configured hostnames from admin_core ──────────────
RESP=$(curl -s --max-time 30 -w '\n%{http_code}' \
-H "X-Internal-Service-Token: $INTERNAL_SERVICE_TOKEN" \
"$BACKEND_URL/admin-core-service/bbb/custom-domains" || true)
CODE=$(echo "$RESP" | tail -1)
BODY=$(echo "$RESP" | sed '$d')

if [ "$CODE" != "200" ]; then
# Deliberately a SKIP, not a failure and not an empty list. Running the
# installer with no aliases would reset server_name to canonical-only and
# take working institute domains offline just because the API blipped.
echo "::warning::Could not fetch custom domains (HTTP $CODE) — leaving the snapshot's existing domain config untouched."
exit 0
fi

DOMAINS=$(echo "$BODY" | jq -r '.domains[]?' | tr '\n' ' ')
echo "[${{ matrix.slug }}] custom domains: ${DOMAINS:-<none>}"

# ── Point each alias at this server's new IP ────────────────────
if [ -n "$DOMAINS" ]; then
ZONES=$(curl -s --max-time 30 -H "Authorization: Bearer $CF_TOKEN" \
"${{ env.CF_API }}/zones?per_page=50" | jq -c '[.result[] | {name, id}]')
for HOST in $DOMAINS; do
# Longest-suffix match, so sub.school.co.in picks school.co.in.
# `. as $z` matters: inside `$h | endswith(...)` the input is the
# STRING $h, so a bare .name there indexes a string and jq errors out,
# which would silently send every host down the warn-and-skip path.
ZONE_ID=$(echo "$ZONES" | jq -r --arg h "$HOST" \
'[.[] | . as $z | select(($h == $z.name) or ($h | endswith("." + $z.name)))]
| sort_by(.name | length) | last | .id // empty')
if [ -z "$ZONE_ID" ]; then
echo "::warning::No Cloudflare zone for $HOST — skipping its DNS record."
continue
fi
REC=$(curl -s -H "Authorization: Bearer $CF_TOKEN" \
"${{ env.CF_API }}/zones/$ZONE_ID/dns_records?type=A&name=$HOST" | jq -r '.result[0].id // empty')
# proxied:false is required — WebRTC negotiates media against the real
# origin IP, which an orange-cloud record would hide.
DATA="{\"type\":\"A\",\"name\":\"$HOST\",\"content\":\"$IP\",\"ttl\":120,\"proxied\":false}"
if [ -n "$REC" ]; then
OK=$(curl -s -X PUT "${{ env.CF_API }}/zones/$ZONE_ID/dns_records/$REC" \
-H "Authorization: Bearer $CF_TOKEN" -H "Content-Type: application/json" -d "$DATA" | jq -r '.success')
else
OK=$(curl -s -X POST "${{ env.CF_API }}/zones/$ZONE_ID/dns_records" \
-H "Authorization: Bearer $CF_TOKEN" -H "Content-Type: application/json" -d "$DATA" | jq -r '.success')
fi
if [ "$OK" = "true" ]; then
echo " $HOST -> $IP"
else
echo "::warning::DNS update failed for $HOST (Cloudflare success=${OK:-<no response>})"
fi
done
fi

# ── Refresh the DNS-01 credentials, then apply on the box ───────
printf 'dns_cloudflare_api_token = %s\n' "$CF_TOKEN" | \
ssh $SSH_OPTS root@$IP 'umask 077; cat > /etc/letsencrypt/cloudflare.ini && chmod 600 /etc/letsencrypt/cloudflare.ini' \
|| echo "::warning::Could not refresh cloudflare.ini — certificate expansion may be skipped on the box."

if ! scp $SSH_OPTS bigbluebutton-server/install-custom-domains.sh root@$IP:/tmp/; then
echo "::error::Could not copy install-custom-domains.sh to $IP"
exit 1
fi
CSV=$(echo "$DOMAINS" | tr ' ' ',' | sed 's/,$//')
# This one IS fatal: nginx server_name and the certificate are what make
# the alias reachable at all, and the script rolls itself back on failure.
if ! ssh $SSH_OPTS root@$IP "bash /tmp/install-custom-domains.sh '${{ matrix.domain }}' '$CSV'"; then
echo "::error::install-custom-domains.sh failed on $IP — the canonical domain is unaffected, but institute domains may not be served."
exit 1
fi

- name: Summary
if: steps.check.outputs.skip != 'true'
run: |
Expand Down
38 changes: 34 additions & 4 deletions .github/workflows/build-zoe-appx.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Build ZOE Edtech AppX (Microsoft Store)
name: Build ZOE Online School AppX (Microsoft Store)

# Builds the ZOE Edtech Windows Store package (.appx, x64 + ia32) on a Windows
# Builds the ZOE Online School Windows Store package (.appx, x64 + ia32) on a Windows
# runner and uploads it as a downloadable artifact. AppX packaging needs the
# Windows SDK (makeappx), which only exists on Windows — hence windows-latest.
#
Expand Down Expand Up @@ -71,7 +71,37 @@ jobs:
run: |
rm -rf electron/app
cp -r dist electron/app
echo '{"flavor":"zoe"}' > electron/electron-flavor.json
# otaAppId must be the STORE bundle id — ota.ts falls back to
# app.getName() without it, which never matches an OTA target_app_ids.
echo '{"flavor":"zoe","otaAppId":"com.zoeedtech.app"}' > electron/electron-flavor.json
# Which web bundle is baked in. Without this ota.ts falls back to the
# ELECTRON package version (1.0.x), a different numbering space, so
# every OTA bundle looks newer and the fresh builtin bundle is
# immediately replaced by an older one on first launch.
node -p "require('./package.json').version" | tr -d '\n' > electron/app/ota-bundle-version.txt
cat electron/app/ota-bundle-version.txt

# electron/package.json is checked in with Shiksha Nation's identity and is
# rewritten per flavor at build time — build-windows-zoe.sh does this and CI
# used to skip it, so the ZOE package shipped as name "Shiksha_Nation".
# That is not cosmetic: on Windows app.getName() resolves to package.json
# productName ?? name, and userData is %APPDATA%\<that name> — so ZOE and
# Shiksha Nation shared one localStorage, cookie jar and OTA cache on any PC
# with both installed. Keep this identical to build-windows-zoe.sh.
- name: Patch package.json for ZOE
working-directory: frontend-learner-dashboard-app/electron
shell: bash
run: |
node -e "
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
pkg.name = 'ZOE_Online_School';
pkg.productName = 'ZOE Online School';
pkg.description = 'ZOE Global Online School — AI-Powered Learning Platform';
pkg.author = { name: 'ZOE Global Online School', email: 'support@zoeedtech.com' };
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
"
node -p "'name=' + require('./package.json').name + ' productName=' + require('./package.json').productName + ' author=' + require('./package.json').author.name"

- name: Install electron dependencies
working-directory: frontend-learner-dashboard-app/electron
Expand All @@ -93,7 +123,7 @@ jobs:
- name: Upload AppX artifacts
uses: actions/upload-artifact@v4
with:
name: zoe-edtech-appx
name: zoe-online-school-appx
path: |
frontend-learner-dashboard-app/electron/dist-store/*.appx
frontend-learner-dashboard-app/electron/dist-store/*.appxbundle
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/maven-publish-admin-core-service.yml
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ jobs:
META_LOGIN_CONFIG_ENABLED=${{ secrets.META_LOGIN_CONFIG_ENABLED || 'false' }} \
OAUTH_TOKEN_ENCRYPTION_KEY=${{ secrets.OAUTH_TOKEN_ENCRYPTION_KEY }} \
AI_SERVICE_INTERNAL_TOKEN=${{ secrets.INTERNAL_SERVICE_TOKEN }} \
INTERNAL_SERVICE_TOKEN=${{ secrets.INTERNAL_SERVICE_TOKEN }} \
YOUTUBE_OAUTH_CLIENT_ID=${{ secrets.ORG_YOUTUBE_OAUTH_CLIENT_ID }} \
YOUTUBE_OAUTH_CLIENT_SECRET=${{ secrets.ORG_YOUTUBE_OAUTH_CLIENT_SECRET }} \
YOUTUBE_OAUTH_REDIRECT_URI=${{ secrets.ORG_YOUTUBE_OAUTH_REDIRECT_URI }} \
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,4 @@ vacademy_devops/hetzner/values-prod-hetzner.yaml

# Local dev config per service (hardcoded secrets for terminal runs) — never commit
**/src/main/resources/application-local.properties
loadtest/answer-sheet.pdf
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@ public class ApplicationSecurityConfig {
"/admin-core-service/live-sessions/provider/meeting/recording/complete",
// BBB server pool management (server-to-server from community_service, no JWT)
"/admin-core-service/bbb/pool/**",
// BBB custom live-class domains, read by the pool start workflow.
// No JWT, but NOT unauthenticated: BbbCustomDomainController itself
// requires the shared X-Internal-Service-Token. Note the path must not
// contain the word "internal" — InternalAuthFilter substring-matches the
// URI and would demand clientName + Signature instead.
"/admin-core-service/bbb/custom-domains",
// Zoom webhook callback (no JWT — verified by per-account HMAC signature)
"/admin-core-service/live-sessions/provider/meeting/zoom-callback/**",
// "Connect with Zoom" OAuth redirect (no JWT — CSRF-protected by the state record)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package vacademy.io.admin_core_service.core.crypto;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
import vacademy.io.common.exceptions.VacademyException;

import java.util.Map;

/**
* JPA converter storing a {@code Map<String,Object>} as an encrypted JSON TEXT
* column (used for hr_employee_profile.statutory_info, which V480 converted
* from jsonb to TEXT). Legacy rows holding plaintext JSON parse through
* unchanged and are encrypted on next write.
*/
@Converter
public class EncryptedJsonMapConverter implements AttributeConverter<Map<String, Object>, String> {

private static final ObjectMapper MAPPER = new ObjectMapper();
private static final TypeReference<Map<String, Object>> MAP_TYPE = new TypeReference<>() {};

@Override
public String convertToDatabaseColumn(Map<String, Object> attribute) {
if (attribute == null) {
return null;
}
try {
String json = MAPPER.writeValueAsString(attribute);
HrFieldCipher cipher = HrFieldCipher.instance();
return cipher != null ? cipher.encryptField(json) : json;
} catch (Exception e) {
throw new VacademyException("Failed to serialize statutory info: " + e.getMessage());
}
}

@Override
public Map<String, Object> convertToEntityAttribute(String dbData) {
if (dbData == null || dbData.isBlank()) {
return null;
}
try {
HrFieldCipher cipher = HrFieldCipher.instance();
String json = cipher != null ? cipher.decryptField(dbData) : dbData;
return MAPPER.readValue(json, MAP_TYPE);
} catch (Exception e) {
throw new VacademyException("Failed to read statutory info: " + e.getMessage());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package vacademy.io.admin_core_service.core.crypto;

import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;

/**
* JPA converter encrypting a String column at rest via {@link HrFieldCipher}.
* Apply explicitly ({@code @Convert(converter = EncryptedStringConverter.class)})
* on PII fields — never autoApply. Legacy plaintext rows (no ENCv1: prefix)
* read through unchanged and are encrypted on next write.
*/
@Converter
public class EncryptedStringConverter implements AttributeConverter<String, String> {

@Override
public String convertToDatabaseColumn(String attribute) {
HrFieldCipher cipher = HrFieldCipher.instance();
return cipher != null ? cipher.encryptField(attribute) : attribute;
}

@Override
public String convertToEntityAttribute(String dbData) {
HrFieldCipher cipher = HrFieldCipher.instance();
return cipher != null ? cipher.decryptField(dbData) : dbData;
}
}
Loading
Loading