From 92e74585e4d204bd36d2a4843756255edcda41cf Mon Sep 17 00:00:00 2001 From: JayChase Date: Sun, 14 Sep 2025 01:46:33 +0000 Subject: [PATCH] Missing the step of running `az login` before running az commands (added az login to command hint in the UI) Fixes #4 --- README.md | 8 ++- infra/main.bicep | 1 - infra/replace-placeholders.sh | 56 +++++++++++++------ ng-web/src/app/api-key/api-key.html | 10 ++++ ng-web/src/app/api-key/api-key.ts | 5 +- .../src/environments/environment-template.ts | 2 +- ng-web/src/environments/environment.ts | 2 +- ng-web/src/styles.scss | 11 ++++ 8 files changed, 74 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 73337c1..1c32c34 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,13 @@ A streaming chat function app demonstrating real-time AI conversations using Azu ![azd up completed](./docs/azd-up-result.png) -5. Copy and run the az command from the UI and run it in the terminal to get the func app key. Paste the key into the UI and start chatting. +5. Login the **Azure CLI** + +```bash +az login --tenant $AZURE_TENANT_ID +``` + +6. Copy and run the az command from the UI and run it in the terminal to get the func app key. Paste the key into the UI and start chatting. ![UI](./docs/ui.png) diff --git a/infra/main.bicep b/infra/main.bicep index c796a2d..4925ab3 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -355,5 +355,4 @@ output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applica output NG_API_URL string = 'https://${functionApp.outputs.defaultHostname}' output NG_FUNCTION_APP_NAME string = functionApp.outputs.name output NG_RG_NAME string = resourceGroup.name - output WEBAPP_URL string = webApp.outputs.defaultHostname diff --git a/infra/replace-placeholders.sh b/infra/replace-placeholders.sh index 68c4eb8..e88c7ee 100755 --- a/infra/replace-placeholders.sh +++ b/infra/replace-placeholders.sh @@ -4,10 +4,9 @@ set -e # Exit on any error # Function to display usage usage() { - echo "Usage: $0 [env_prefix]" - echo " template_file: Path to the template file with placeholders" + echo "Usage: $0 " + echo " template_file: Path to the template file with placeholders (e.g., %KEY%)" echo " output_file: Path where the processed file will be saved" - echo " env_prefix: Environment variable prefix to filter (default: NG_)" exit 1 } @@ -18,7 +17,6 @@ fi TEMPLATE_FILE="$1" OUTPUT_FILE="$2" -ENV_PREFIX="${3:-NG_}" # Check if template file exists if [ ! -f "$TEMPLATE_FILE" ]; then @@ -47,24 +45,50 @@ if [ -z "$ENV_VARS" ]; then echo "Make sure the environment has been provisioned with: azd provision" fi -# Create a temporary file to store filtered environment variables +# Create a temporary file to store environment variables (all keys) TEMP_ENV_FILE=$(mktemp) -echo "$ENV_VARS" | grep "^${ENV_PREFIX}" > "$TEMP_ENV_FILE" || true +# Keep only KEY=VALUE lines (ignore blanks/comments) +echo "$ENV_VARS" | grep -E '^[A-Za-z_][A-Za-z0-9_]*=' > "$TEMP_ENV_FILE" || true # Read the template file echo "Processing template file: $TEMPLATE_FILE" CONTENT=$(cat "$TEMPLATE_FILE") -# Process each environment variable that starts with the prefix -while IFS='=' read -r key value; do +# Process each environment variable; replace only if %KEY% exists in the template +while IFS= read -r line; do + # Skip empty lines just in case + [ -z "$line" ] && continue + + # Split into key and value at the first '=' only + key=${line%%=*} + value=${line#*=} + if [ -n "$key" ] && [ -n "$value" ]; then - # Remove quotes from value if present - clean_value=$(echo "$value" | sed 's/^["'\'']\|["'\'']$//g') - - echo "Replacing %${key}% with: $clean_value" - - # Replace all occurrences of %KEY% with the value - CONTENT=$(echo "$CONTENT" | sed "s|%${key}%|${clean_value}|g") + # Replace only if the placeholder exists + if printf '%s' "$CONTENT" | grep -q "%${key}%"; then + clean_value="$value" + # Strip matching surrounding quotes symmetrically ("..." or '...') + if [ "${clean_value#\"}" != "$clean_value" ] && [ "${clean_value%\"}" != "$clean_value" ]; then + clean_value=${clean_value:1:${#clean_value}-2} + elif [ "${clean_value#\'}" != "$clean_value" ] && [ "${clean_value%\'}" != "$clean_value" ]; then + clean_value=${clean_value:1:${#clean_value}-2} + else + # If there's a stray trailing quote with no leading one, drop it + if [ "${clean_value%\"}" != "$clean_value" ] && [ "${clean_value#\"}" = "$clean_value" ]; then + clean_value=${clean_value%\"} + fi + if [ "${clean_value%\'}" != "$clean_value" ] && [ "${clean_value#\'}" = "$clean_value" ]; then + clean_value=${clean_value%\'} + fi + fi + + # Escape sed replacement specials: /, &, | + esc_value=$(printf '%s' "$clean_value" | sed -e 's/[\/&|]/\\&/g') + + echo "Replacing %${key}%" + # Replace all occurrences of %KEY% with the escaped value + CONTENT=$(printf '%s' "$CONTENT" | sed "s|%${key}%|${esc_value}|g") + fi fi done < "$TEMP_ENV_FILE" @@ -74,4 +98,4 @@ echo "$CONTENT" > "$OUTPUT_FILE" # Clean up temporary file rm -f "$TEMP_ENV_FILE" -echo "Successfully processed template and saved to: $OUTPUT_FILE" \ No newline at end of file +echo "Successfully processed template and saved to: $OUTPUT_FILE" diff --git a/ng-web/src/app/api-key/api-key.html b/ng-web/src/app/api-key/api-key.html index 69e7f99..51dc387 100644 --- a/ng-web/src/app/api-key/api-key.html +++ b/ng-web/src/app/api-key/api-key.html @@ -2,6 +2,7 @@ If you are using the deployed API paste the function key here ({{getKeyCommand}}) + diff --git a/ng-web/src/app/api-key/api-key.ts b/ng-web/src/app/api-key/api-key.ts index b52ca3a..4c77b10 100644 --- a/ng-web/src/app/api-key/api-key.ts +++ b/ng-web/src/app/api-key/api-key.ts @@ -1,3 +1,4 @@ +import { ClipboardModule } from '@angular/cdk/clipboard'; import { CommonModule } from '@angular/common'; import { Component, @@ -9,6 +10,7 @@ import { } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; @@ -19,11 +21,12 @@ import { environment } from '../../environments/environment'; selector: 'app-api-key', imports: [ CommonModule, - + ClipboardModule, ReactiveFormsModule, MatFormFieldModule, MatInputModule, MatIconModule, + MatButtonModule, MatCardModule ], templateUrl: './api-key.html', diff --git a/ng-web/src/environments/environment-template.ts b/ng-web/src/environments/environment-template.ts index 529b000..266d484 100644 --- a/ng-web/src/environments/environment-template.ts +++ b/ng-web/src/environments/environment-template.ts @@ -2,7 +2,7 @@ export const environment = { aiApi: { uri: '%NG_API_URL%', getKeyCommand: - 'az functionapp function keys list --resource-group %NG_RG_NAME% --name %NG_FUNCTION_APP_NAME% --function-name chat', + 'az login --tenant %AZURE_TENANT_ID% && az functionapp function keys list --resource-group %NG_RG_NAME% --name %NG_FUNCTION_APP_NAME% --function-name chat', endpoints: { chat: 'api/chat' } diff --git a/ng-web/src/environments/environment.ts b/ng-web/src/environments/environment.ts index a177c63..2cea3e4 100644 --- a/ng-web/src/environments/environment.ts +++ b/ng-web/src/environments/environment.ts @@ -2,7 +2,7 @@ export const environment = { aiApi: { uri: 'https://func-lxevwr7a4vipw.azurewebsites.net', getKeyCommand: - 'az functionapp function keys list --resource-group rg-skazd --name func-lxevwr7a4vipw --function-name chat', + 'az login --tenant 8e8dee8b-d10c-4894-8d43-cae3f9ed549b && az functionapp function keys list --resource-group rg-skazd --name func-lxevwr7a4vipw --function-name chat', endpoints: { chat: 'api/chat' } diff --git a/ng-web/src/styles.scss b/ng-web/src/styles.scss index 6d2129c..33b8ca6 100644 --- a/ng-web/src/styles.scss +++ b/ng-web/src/styles.scss @@ -22,6 +22,17 @@ html { height: 100%; font-family: Roboto, 'Helvetica Neue', sans-serif; margin: 0; + + mat-hint { + .mdc-icon-button { + @include mat.icon-button-overrides( + ( + icon-size: 18px + ) + ); + } + } + app-root { height: 100%; }