Merge improvements/fork-harvest into main
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
Deploy Frontend / deploy (push) Has been cancelled

Fork-harvest brings: the ip-api.com geo fix, credential bundle scan,
mock gateways out of production, JIT compiler dropped (1.55->1.04 MB),
host hardening, provider-agnostic identity + VK/Yandex + account linking,
and the backend contracts consolidated into one BACKEND-INTEGRATION.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

# Conflicts:
#	docs/backend/BACKEND-HANDOFF.md
#	docs/backend/TRACK-S-SECURITY-RBAC-CONTRACT.md
This commit is contained in:
sdarbinyan
2026-08-22 16:19:55 +04:00
77 changed files with 1659 additions and 3023 deletions

View File

@@ -0,0 +1,10 @@
<button
type="button"
class="social-login-button"
[class]="'social-login-button--' + provider()"
[disabled]="loading()"
(click)="startLogin()"
>
<app-icon name="user" [size]="18" />
<span>{{ label() }}</span>
</button>

View File

@@ -1,4 +1,4 @@
.vk-id-login {
.social-login-button {
display: flex;
align-items: center;
justify-content: center;

View File

@@ -0,0 +1,56 @@
import { ChangeDetectionStrategy, Component, computed, inject, input, signal } from '@angular/core';
import { take } from 'rxjs/operators';
import { SOCIAL_IDENTITY_GATEWAY } from '../../core/identity/services/social-identity-gateway.token';
import { SocialProvider } from '../../core/identity/services/social-identity-gateway.interface';
import { IconComponent } from '../../shared/ui/icon/icon.component';
const PROVIDER_LABEL: Record<SocialProvider, string> = {
vk: 'Continue with VK ID',
yandex: 'Continue with Yandex ID',
};
/**
* One button per social provider, per v3.1 §14 (VK ID is the primary
* storefront social login; Yandex ID is the second instance of the same
* flow, not a separate integration).
*
* Deliberately not spliced into TelegramLoginComponent's dialog yet. That
* component is the live customer login surface, and adding providers to it
* belongs in the pass that also demotes Telegram to one ExternalIdentity
* among several (FH-4.6) - not before a real OAuth application exists to
* test against.
*/
@Component({
selector: 'app-social-login-button',
standalone: true,
imports: [IconComponent],
templateUrl: './social-login-button.component.html',
styleUrls: ['./social-login-button.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SocialLoginButtonComponent {
private readonly gateway = inject(SOCIAL_IDENTITY_GATEWAY);
readonly provider = input.required<SocialProvider>();
/** Where to land after the callback. Validated backend-side. */
readonly returnTo = input<string | undefined>(undefined);
readonly loading = signal(false);
readonly label = computed(() => PROVIDER_LABEL[this.provider()]);
startLogin(): void {
this.loading.set(true);
this.gateway
.getAuthorizeUrl(this.provider(), this.returnTo())
.pipe(take(1))
.subscribe({
next: url => {
this.loading.set(false);
if (typeof window !== 'undefined') {
window.location.href = url;
}
},
error: () => this.loading.set(false),
});
}
}

View File

@@ -1,4 +0,0 @@
<button type="button" class="vk-id-login" [disabled]="loading()" (click)="startLogin()">
<app-icon name="user" [size]="18" />
<span>Continue with VK ID</span>
</button>

View File

@@ -1,37 +0,0 @@
import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';
import { CommonModule } from '@angular/common';
import { take } from 'rxjs/operators';
import { VK_ID_GATEWAY } from '../../core/identity/services/vk-id-gateway.token';
import { IconComponent } from '../../shared/ui/icon/icon.component';
/**
* Standalone VK ID login button, per Sprint 0.1 ("do all after vk" - VK ID
* is the primary storefront social login going forward, per v3.1 §14).
* Deliberately not wired into TelegramLoginComponent's dialog yet - that
* component is the live, working customer/admin login surface, and
* splicing a second provider into it needs its own careful pass once a
* real VK OAuth app exists to test against, not a mock-backed bolt-on.
*/
@Component({
selector: 'app-vk-id-login',
standalone: true,
imports: [CommonModule, IconComponent],
templateUrl: './vk-id-login.component.html',
styleUrls: ['./vk-id-login.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class VkIdLoginComponent {
private readonly gateway = inject(VK_ID_GATEWAY);
readonly loading = signal(false);
startLogin(): void {
this.loading.set(true);
this.gateway.getAuthorizeUrl().pipe(take(1)).subscribe(url => {
this.loading.set(false);
if (typeof window !== 'undefined') {
window.location.href = url;
}
});
}
}