Skip to content

Commit 294e40a

Browse files
Merge pull request #124 from AET-DevOps26/bugfix/crypto-and-llm-fixes
Fixed crypto not working on azure
2 parents 70043c5 + ad9e8e2 commit 294e40a

5 files changed

Lines changed: 61 additions & 9 deletions

File tree

.github/workflows/deploy-azure.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ jobs:
1717
ARM_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
1818
ARM_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
1919
ARM_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
20+
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
2021

2122
steps:
2223
- name: Checkout Code

infra/iac/azure/playbook.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@
103103
line: 'DEFAULT_LLM_MODEL=gemini'
104104
state: present
105105

106+
- name: Inject hosted LLM API key into deployed .env
107+
ansible.builtin.lineinfile:
108+
path: /home/devops-admin/app/.env
109+
regexp: '^GEMINI_API_KEY=.*$'
110+
line: "GEMINI_API_KEY={{ lookup('env', 'GEMINI_API_KEY') }}"
111+
state: present
112+
106113
- name: Ensure init-db directory exists on VM
107114
ansible.builtin.file:
108115
path: /home/devops-admin/app/init-db

web-client/src/lib/utils.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,45 @@ import { twMerge } from 'tailwind-merge';
99
export function cn(...inputs: ClassValue[]) {
1010
return twMerge(clsx(inputs));
1111
}
12+
13+
/**
14+
* Generate a UUID v4 string for local-only entity IDs (e.g. checklist items
15+
* that haven't been persisted yet).
16+
*
17+
* Prefers `crypto.randomUUID()` (concise, native), but falls back to a
18+
* `crypto.getRandomValues()`-based implementation when running in a
19+
* non-secure context — `crypto.randomUUID` is only exposed on HTTPS or
20+
* localhost origins, so it throws on the plain-HTTP Azure deployment
21+
* (`http://20.91.193.39/`) while the AET cluster behind a TLS-terminating
22+
* ingress works fine. `crypto.getRandomValues` is available in every
23+
* context (HTTP, HTTPS, file://), so the fallback keeps the app functional
24+
* in any deployment.
25+
*/
26+
export function genId(): string {
27+
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
28+
return crypto.randomUUID();
29+
}
30+
if (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') {
31+
const bytes = new Uint8Array(16);
32+
crypto.getRandomValues(bytes);
33+
// RFC 4122 §4.4 — set version (4) and variant (10xx) bits.
34+
bytes[6] = (bytes[6] & 0x0f) | 0x40;
35+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
36+
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
37+
return (
38+
hex.slice(0, 8) +
39+
'-' +
40+
hex.slice(8, 12) +
41+
'-' +
42+
hex.slice(12, 16) +
43+
'-' +
44+
hex.slice(16, 20) +
45+
'-' +
46+
hex.slice(20, 32)
47+
);
48+
}
49+
// Last-resort fallback if `crypto` is somehow not available at all
50+
// (very old browsers, exotic test envs). Not RFC-compliant but unique
51+
// enough for local-item IDs in a single tab.
52+
return `local-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 11)}`;
53+
}

web-client/src/routes/_authenticated/chat/index.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
2323
import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism';
2424
import { useSendMessage, useDeleteConversation } from '#/lib/queries/chat.ts';
2525
import { classifyChatError } from '#/lib/utils/chat.ts';
26+
import { genId } from '#/lib/utils';
2627
import { getConversation } from '#/services/genai/gen-a-i/gen-a-i';
2728

2829
// ── Types ──────────────────────────────────────────────────────
@@ -372,7 +373,7 @@ export function ChatPage() {
372373
.then((conv) => {
373374
if (conv.messages && conv.messages.length > 0) {
374375
const restored: Message[] = conv.messages.map((m) => ({
375-
id: crypto.randomUUID(),
376+
id: genId(),
376377
role: m.role === 'USER' ? 'user' : 'agent',
377378
content: m.content ?? '',
378379
state: 'sent' as const,
@@ -418,7 +419,7 @@ export function ChatPage() {
418419

419420
setShowWelcome(false);
420421

421-
const userMsg: Message = { id: crypto.randomUUID(), role: 'user', content: trimmed, state: 'sent' };
422+
const userMsg: Message = { id: genId(), role: 'user', content: trimmed, state: 'sent' };
422423
setMessages((prev) => [...prev, userMsg]);
423424
setInput('');
424425
setIsLoading(true);
@@ -436,7 +437,7 @@ export function ChatPage() {
436437
setMessages((prev) => [
437438
...prev,
438439
{
439-
id: crypto.randomUUID(),
440+
id: genId(),
440441
role: 'agent',
441442
content: data.response ?? '',
442443
state: 'sent',

web-client/src/routes/_authenticated/notes/index.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import {
4545
} from '#/lib/queries/checklists.ts';
4646
import type { IdentifiedTimestampedNote as ApiNote } from '#/types/notes';
4747
import type { IdentifiedChecklist as ApiChecklist } from '#/types/checklist';
48+
import { genId } from '#/lib/utils';
4849

4950
// ── Types ──────────────────────────────────────────────────────
5051

@@ -54,7 +55,7 @@ type NoteType = 'note' | 'checklist';
5455
/**
5556
* A single item within a checklist.
5657
* `id` can be a **number** (persisted via API, used for toggling completion) or a
57-
* **string** (locally generated via `crypto.randomUUID()` for items not yet saved).
58+
* **string** (locally generated via `genId()` for items not yet saved).
5859
* The form uses this distinction: numeric IDs map to API items, string IDs are new
5960
* items that will be created via `addChecklistItem`.
6061
*/
@@ -438,7 +439,7 @@ function NoteDetail({
438439

439440
/**
440441
* Create / Edit form. Type is locked when editing (cannot convert note ↔ checklist).
441-
* New checklist items get `crypto.randomUUID()` string IDs; persisted items have
442+
* New checklist items get `genId()` string IDs; persisted items have
442443
* numeric IDs. Enter in "Add item" input triggers `addItem`.
443444
*/
444445
function NoteForm({
@@ -459,7 +460,7 @@ function NoteForm({
459460
/** Add a new checklist item with a local UUID string ID. */
460461
const addItem = () => {
461462
if (!newItemText.trim()) return;
462-
setItems([...items, { id: crypto.randomUUID(), text: newItemText.trim(), done: false }]);
463+
setItems([...items, { id: genId(), text: newItemText.trim(), done: false }]);
463464
setNewItemText('');
464465
};
465466

@@ -563,7 +564,7 @@ function NoteForm({
563564
* is derived via `useMemo` from the live list to avoid stale snapshots.
564565
*
565566
* Checklist save: computes diff between original and form items — numeric IDs
566-
* absent from form are deleted, string IDs (from `crypto.randomUUID()`) are created.
567+
* absent from form are deleted, string IDs (from `genId()`) are created.
567568
*/
568569
export function NotesPage() {
569570
const router = useRouter();
@@ -712,7 +713,7 @@ export function NotesPage() {
712713
* calls in parallel via `Promise.all`.
713714
* **Existing checklist update**: Computes a diff against the original items:
714715
* - Items with numeric IDs in the original but absent from the form → deleted.
715-
* - Items with string IDs (local `crypto.randomUUID()` → created via API.
716+
* - Items with string IDs (local `genId()` → created via API.
716717
* - The checklist title is updated unconditionally.
717718
* This diff-based approach avoids deleting and recreating unchanged items,
718719
* preserving their server-side IDs and creation timestamps.
@@ -781,7 +782,7 @@ export function NotesPage() {
781782
);
782783
}
783784

784-
// Add new items (string ids from crypto.randomUUID)
785+
// Add new items (string ids from genId)
785786
const newItems = note.checklist.filter((item) => typeof item.id === 'string');
786787
if (newItems.length > 0) {
787788
await Promise.all(

0 commit comments

Comments
 (0)