Fix scan submit: imageRef signal binding + API URL path
Bug: canSubmit computed() used plain string imageRef which computed()
can't track. Submit button was permanently disabled.
Fix: Changed imageRef to signal(''), updated template to use
[ngModel]="imageRef()" (ngModelChange)="imageRef.set($event)",
updated all class references to imageRef() for reads and .set() for writes.
Bug: Scan submit called /scanner/api/v1/scans/ (console nginx prefix)
instead of /api/v1/scans/ (gateway route).
Fix: Removed /scanner/ prefix from all API URLs.
KNOWN ISSUE: Scanner service needs identity envelope middleware (same
as Concelier fix) — ReverseProxy strips JWT, scanner returns 400
"tenant required". The scan submission reaches the scanner but auth
fails. Fix requires adding the same pre-auth envelope middleware
pattern to the Scanner service.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -57,7 +57,8 @@ interface ScanStatusResponse {
|
|||||||
Image reference *
|
Image reference *
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
[(ngModel)]="imageRef"
|
[ngModel]="imageRef()"
|
||||||
|
(ngModelChange)="imageRef.set($event)"
|
||||||
placeholder="e.g., registry.stella-ops.local/myapp:v1.2.3"
|
placeholder="e.g., registry.stella-ops.local/myapp:v1.2.3"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
@@ -160,7 +161,7 @@ interface ScanStatusResponse {
|
|||||||
</div>
|
</div>
|
||||||
<div class="detail-row">
|
<div class="detail-row">
|
||||||
<span class="detail-label">Image</span>
|
<span class="detail-label">Image</span>
|
||||||
<span class="detail-value">{{ imageRef }}</span>
|
<span class="detail-value">{{ imageRef() }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-row">
|
<div class="detail-row">
|
||||||
<span class="detail-label">Status</span>
|
<span class="detail-label">Status</span>
|
||||||
@@ -471,7 +472,7 @@ export class ScanSubmitComponent implements OnDestroy {
|
|||||||
private readonly destroyRef = inject(DestroyRef);
|
private readonly destroyRef = inject(DestroyRef);
|
||||||
|
|
||||||
/** Form state */
|
/** Form state */
|
||||||
imageRef = '';
|
imageRef = signal('');
|
||||||
forceRescan = false;
|
forceRescan = false;
|
||||||
showMetadata = false;
|
showMetadata = false;
|
||||||
metadataEntries: MetadataEntry[] = [];
|
metadataEntries: MetadataEntry[] = [];
|
||||||
@@ -487,7 +488,7 @@ export class ScanSubmitComponent implements OnDestroy {
|
|||||||
|
|
||||||
/** Derive image name (strip tag/digest for triage link) */
|
/** Derive image name (strip tag/digest for triage link) */
|
||||||
readonly imageName = computed(() => {
|
readonly imageName = computed(() => {
|
||||||
const ref = this.imageRef.trim();
|
const ref = this.imageRef().trim();
|
||||||
const atIdx = ref.indexOf('@');
|
const atIdx = ref.indexOf('@');
|
||||||
if (atIdx > 0) return ref.substring(0, atIdx);
|
if (atIdx > 0) return ref.substring(0, atIdx);
|
||||||
const colonIdx = ref.lastIndexOf(':');
|
const colonIdx = ref.lastIndexOf(':');
|
||||||
@@ -496,7 +497,7 @@ export class ScanSubmitComponent implements OnDestroy {
|
|||||||
});
|
});
|
||||||
|
|
||||||
readonly canSubmit = computed(() => {
|
readonly canSubmit = computed(() => {
|
||||||
return this.imageRef.trim().length > 0 && !this.submitting();
|
return this.imageRef().trim().length > 0 && !this.submitting();
|
||||||
});
|
});
|
||||||
|
|
||||||
addMetadataEntry(): void {
|
addMetadataEntry(): void {
|
||||||
@@ -523,7 +524,7 @@ export class ScanSubmitComponent implements OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const body: Record<string, unknown> = {
|
const body: Record<string, unknown> = {
|
||||||
image: { reference: this.imageRef.trim() },
|
image: { reference: this.imageRef().trim() },
|
||||||
force: this.forceRescan,
|
force: this.forceRescan,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -531,7 +532,7 @@ export class ScanSubmitComponent implements OnDestroy {
|
|||||||
body['metadata'] = metadata;
|
body['metadata'] = metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.http.post<ScanSubmitResponse>('/scanner/api/v1/scans/', body).pipe(
|
this.http.post<ScanSubmitResponse>('/api/v1/scans/', body).pipe(
|
||||||
takeUntilDestroyed(this.destroyRef),
|
takeUntilDestroyed(this.destroyRef),
|
||||||
).subscribe({
|
).subscribe({
|
||||||
next: (response) => {
|
next: (response) => {
|
||||||
@@ -552,7 +553,7 @@ export class ScanSubmitComponent implements OnDestroy {
|
|||||||
this.scanId.set(null);
|
this.scanId.set(null);
|
||||||
this.scanStatus.set('queued');
|
this.scanStatus.set('queued');
|
||||||
this.submitError.set(null);
|
this.submitError.set(null);
|
||||||
this.imageRef = '';
|
this.imageRef.set('');
|
||||||
this.forceRescan = false;
|
this.forceRescan = false;
|
||||||
this.showMetadata = false;
|
this.showMetadata = false;
|
||||||
this.metadataEntries = [];
|
this.metadataEntries = [];
|
||||||
@@ -567,7 +568,7 @@ export class ScanSubmitComponent implements OnDestroy {
|
|||||||
|
|
||||||
this.pollSubscription = timer(0, 3000).pipe(
|
this.pollSubscription = timer(0, 3000).pipe(
|
||||||
switchMap(() =>
|
switchMap(() =>
|
||||||
this.http.get<ScanStatusResponse>(`/scanner/api/v1/scans/${encodeURIComponent(scanId)}`)
|
this.http.get<ScanStatusResponse>(`/api/v1/scans/${encodeURIComponent(scanId)}`)
|
||||||
),
|
),
|
||||||
tap((response) => {
|
tap((response) => {
|
||||||
this.scanStatus.set(response.status);
|
this.scanStatus.set(response.status);
|
||||||
|
|||||||
Reference in New Issue
Block a user