Merge branch 'main' into instance-organization-breadcrumb

This commit is contained in:
Max Peintner
2025-07-16 10:05:42 +02:00
221 changed files with 35540 additions and 37654 deletions

2
console/.gitignore vendored
View File

@@ -36,7 +36,7 @@ speed-measure-plugin*.json
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
pnpm-debug.log
testem.log
/typings

View File

@@ -1,27 +1,137 @@
# Console
# Console Angular App
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.3.20.
This is the ZITADEL Console Angular application.
## Development server
## Development
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
### Prerequisites
## Code scaffolding
- Node.js 18 or later
- pnpm (latest)
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
### Installation
## Build
```bash
pnpm install
```
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
### Proto Generation
## Running unit tests
The Console app uses **dual proto generation** with Turbo dependency management:
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
1. **`@zitadel/proto` generation**: Modern ES modules with `@bufbuild/protobuf` for v2 APIs
2. **Local `buf.gen.yaml` generation**: Traditional protobuf JavaScript classes for v1 APIs
## Running end-to-end tests
The Console app's `turbo.json` ensures that `@zitadel/proto#generate` runs before the Console's own generation, providing both:
Please refer to the [contributing guide](../CONTRIBUTING.md#console)
- Modern schemas from `@zitadel/proto` (e.g., `UserSchema`, `DetailsSchema`)
- Legacy classes from `src/app/proto/generated` (e.g., `User`, `Project`)
## Further help
Generated files:
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
- **`@zitadel/proto`**: Modern ES modules in `login/packages/zitadel-proto/`
- **Local generation**: Traditional protobuf files in `src/app/proto/generated/`
- TypeScript definition files (`.d.ts`)
- JavaScript files (`.js`)
- gRPC client files (`*ServiceClientPb.ts`)
- OpenAPI/Swagger JSON files (`.swagger.json`)
To generate proto files:
```bash
pnpm run generate
```
This automatically runs both generations in the correct order via Turbo dependencies.
### Development Server
To start the development server:
```bash
pnpm start
```
This will:
1. Fetch the environment configuration from the server
2. Serve the app on the default port
### Building
To build for production:
```bash
pnpm run build
```
This will:
1. Generate proto files (via `prebuild` script)
2. Build the Angular app with production optimizations
### Linting
To run linting and formatting checks:
```bash
pnpm run lint
```
To auto-fix formatting issues:
```bash
pnpm run lint:fix
```
## Project Structure
- `src/app/proto/generated/` - Generated proto files (Angular-specific format)
- `buf.gen.yaml` - Local proto generation configuration
- `turbo.json` - Turbo dependency configuration for proto generation
- `prebuild.development.js` - Development environment configuration script
## Proto Generation Details
The Console app uses **dual proto generation** managed by Turbo dependencies:
### Dependency Chain
The Console app has the following build dependencies managed by Turbo:
1. `@zitadel/proto#generate` - Generates modern protobuf files
2. `@zitadel/client#build` - Builds the TypeScript gRPC client library
3. `console#generate` - Generates Console-specific protobuf files
4. `console#build` - Builds the Angular application
This ensures that the Console always has access to the latest client library and protobuf definitions.
### Legacy v1 API (Traditional Protobuf)
- Uses local `buf.gen.yaml` configuration
- Generates traditional Google protobuf JavaScript classes extending `jspb.Message`
- Uses plugins: `protocolbuffers/js`, `grpc/web`, `grpc-ecosystem/openapiv2`
- Output: `src/app/proto/generated/`
- Used for: Most existing Console functionality
### Modern v2 API (ES Modules)
- Uses `@zitadel/proto` package generation
- Generates modern ES modules with `@bufbuild/protobuf`
- Uses plugin: `@bufbuild/es` with ES modules and JSON types
- Output: `login/packages/zitadel-proto/`
- Used for: New user v2 API and services
### Dependency Management
The Console's `turbo.json` ensures proper execution order:
1. `@zitadel/proto#generate` runs first (modern ES modules)
2. Console's local generation runs second (traditional protobuf)
3. Build/lint/start tasks depend on both generations being complete
This approach allows the Console app to use both v1 and v2 APIs while maintaining proper build dependencies.
## Legacy Information
This project was originally generated with Angular CLI version 8.3.20 and has been updated over time.

View File

@@ -3,12 +3,12 @@
"version": "0.0.0",
"scripts": {
"ng": "ng",
"dev": "node prebuild.development.js && ng serve",
"start": "node prebuild.development.js && ng serve",
"build": "ng build --configuration production --base-href=/ui/console/",
"prelint": "npm run generate",
"lint": "ng lint && prettier --check src",
"lint:fix": "prettier --write src",
"generate": "buf generate ../proto --include-imports --include-wkt"
"generate": "pnpm exec buf generate ../proto --include-imports --include-wkt"
},
"private": true,
"dependencies": {
@@ -24,6 +24,7 @@
"@angular/platform-browser-dynamic": "^16.2.12",
"@angular/router": "^16.2.12",
"@angular/service-worker": "^16.2.12",
"@bufbuild/protobuf": "^2.2.2",
"@connectrpc/connect": "^2.0.0",
"@connectrpc/connect-web": "^2.0.0",
"@ctrl/ngx-codemirror": "^6.1.0",
@@ -34,8 +35,8 @@
"@ng-icons/heroicons": "^25.0.0",
"@ngx-translate/core": "^15.0.0",
"@tanstack/angular-query-experimental": "^5.75.4",
"@zitadel/client": "1.2.0",
"@zitadel/proto": "1.2.0",
"@zitadel/client": "workspace:*",
"@zitadel/proto": "workspace:*",
"angular-oauth2-oidc": "^15.0.1",
"angularx-qrcode": "^16.0.2",
"buffer": "^6.0.3",

View File

@@ -143,7 +143,7 @@ export class FeaturesComponent {
}, {});
// to save special flags they have to be handled here
req.loginV2 = {
req['loginV2'] = {
required: newToggleStates.loginV2.enabled,
baseUri: newToggleStates.loginV2.baseUri,
};

View File

@@ -89,7 +89,7 @@ export class ActionTwoAddTargetDialogComponent {
nanos: 0,
};
const targetType: Extract<MessageInitShape<typeof CreateTargetRequestSchema>['targetType'], { case: TargetTypes }> =
const targetType: MessageInitShape<typeof CreateTargetRequestSchema>['targetType'] =
type === 'restWebhook'
? { case: type, value: { interruptOnError } }
: type === 'restCall'

View File

@@ -22,9 +22,8 @@ const CACHE_WARNING_MS = 5 * 60 * 1000; // 5 minutes
templateUrl: './oidc-webkeys.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class OidcWebKeysComponent implements OnInit {
export class OidcWebKeysComponent {
protected readonly refresh = new Subject<true>();
protected readonly webKeysEnabled$: Observable<boolean>;
protected readonly webKeys$: Observable<WebKey[]>;
protected readonly inactiveWebKeys$: Observable<WebKey[]>;
protected readonly nextWebKeyCandidate$: Observable<WebKey | undefined>;
@@ -34,17 +33,12 @@ export class OidcWebKeysComponent implements OnInit {
constructor(
private readonly webKeysService: WebKeysService,
private readonly featureService: NewFeatureService,
private readonly toast: ToastService,
private readonly timestampToDatePipe: TimestampToDatePipe,
private readonly dialog: MatDialog,
private readonly destroyRef: DestroyRef,
private readonly router: Router,
private readonly route: ActivatedRoute,
) {
this.webKeysEnabled$ = this.getWebKeysEnabled().pipe(shareReplay({ refCount: true, bufferSize: 1 }));
const webKeys$ = this.getWebKeys(this.webKeysEnabled$).pipe(shareReplay({ refCount: true, bufferSize: 1 }));
const webKeys$ = this.getWebKeys().pipe(shareReplay({ refCount: true, bufferSize: 1 }));
this.webKeys$ = webKeys$.pipe(map((webKeys) => webKeys.filter((webKey) => webKey.state !== State.INACTIVE)));
this.inactiveWebKeys$ = webKeys$.pipe(map((webKeys) => webKeys.filter((webKey) => webKey.state === State.INACTIVE)));
@@ -52,34 +46,7 @@ export class OidcWebKeysComponent implements OnInit {
this.nextWebKeyCandidate$ = this.getNextWebKeyCandidate(this.webKeys$);
}
ngOnInit(): void {
// redirect away from this page if web keys are not enabled
// this also preloads the web keys enabled state
this.webKeysEnabled$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(async (webKeysEnabled) => {
if (webKeysEnabled) {
return;
}
await this.router.navigate([], {
relativeTo: this.route,
queryParamsHandling: 'merge',
queryParams: {
id: null,
},
});
});
}
private getWebKeysEnabled() {
return defer(() => this.featureService.getInstanceFeatures()).pipe(
map((features) => features.webKey?.enabled ?? false),
catchError((err) => {
this.toast.showError(err);
return of(false);
}),
);
}
private getWebKeys(webKeysEnabled$: Observable<boolean>) {
private getWebKeys() {
return this.refresh.pipe(
startWith(true),
switchMap(() => {
@@ -87,12 +54,6 @@ export class OidcWebKeysComponent implements OnInit {
}),
map(({ webKeys }) => webKeys),
catchError(async (err) => {
const webKeysEnabled = await firstValueFrom(webKeysEnabled$);
// suppress errors if web keys are not enabled
if (!webKeysEnabled) {
return [];
}
this.toast.showError(err);
return [];
}),

View File

@@ -206,7 +206,7 @@ export class UserCreateV2Component implements OnInit {
if (authenticationFactor.factor === 'initialPassword') {
const { password } = authenticationFactor.form.getRawValue();
humanReq.passwordType = {
humanReq['passwordType'] = {
case: 'password',
value: {
password,

View File

@@ -8,12 +8,14 @@ import { Gender, HumanProfile, HumanProfileSchema } from '@zitadel/proto/zitadel
import { filter, startWith } from 'rxjs/operators';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { Profile } from '@zitadel/proto/zitadel/user_pb';
import { create } from '@bufbuild/protobuf';
//@ts-ignore
import { create } from '@zitadel/client';
function toHumanProfile(profile: HumanProfile | Profile): HumanProfile {
if (profile.$typeName === 'zitadel.user.v2.HumanProfile') {
return profile;
}
return create(HumanProfileSchema, {
givenName: profile.firstName,
familyName: profile.lastName,

View File

@@ -44,10 +44,10 @@ import { ListUsersRequestSchema, ListUsersResponse } from '@zitadel/proto/zitade
import { UserState as UserStateV1 } from 'src/app/proto/generated/zitadel/user_pb';
import { NewOrganizationService } from 'src/app/services/new-organization.service';
type Query = Exclude<
Exclude<MessageInitShape<typeof ListUsersRequestSchema>['queries'], undefined>[number]['query'],
undefined
>;
type ListUsersRequest = MessageInitShape<typeof ListUsersRequestSchema>;
type QueriesArray = NonNullable<ListUsersRequest['queries']>;
type QueryWrapper = QueriesArray extends readonly (infer T)[] ? T : never;
type Query = NonNullable<QueryWrapper extends { query?: infer Q } ? Q : never>;
@Component({
selector: 'cnsl-user-table',

28
console/turbo.json Normal file
View File

@@ -0,0 +1,28 @@
{
"$schema": "https://turbo.build/schema.json",
"extends": ["//"],
"tasks": {
"generate": {
"dependsOn": ["@zitadel/proto#generate"],
"outputs": ["src/app/proto/generated/**"]
},
"build": {
"dependsOn": ["generate", "@zitadel/client#build"],
"outputs": ["dist/**"]
},
"dev": {
"dependsOn": ["generate", "@zitadel/client#build"],
"cache": false,
"persistent": true
},
"start": {
"dependsOn": ["generate", "@zitadel/client#build"],
"cache": false,
"persistent": true
},
"lint": {
"dependsOn": ["generate"],
"outputs": []
}
}
}

File diff suppressed because it is too large Load Diff