diff --git a/webapp/CLAUDE.md b/webapp/CLAUDE.md
index 436d40d..d66654b 100644
--- a/webapp/CLAUDE.md
+++ b/webapp/CLAUDE.md
@@ -26,7 +26,7 @@ The app follows a feature-based module organization with standalone components:
### Key Patterns
-**Authentication**: Uses a custom `SCN` token scheme. Credentials (user_id and admin_key) are stored in sessionStorage and attached via `authInterceptor`. The `authGuard` protects all routes except `/login`.
+**Authentication**: Uses a custom `SCN` token scheme. Credentials (user_id and admin_key) are stored in localStorage and attached via `authInterceptor`. The `authGuard` protects all routes except `/login`.
**API Communication**: All API calls go through `ApiService` (`src/app/core/services/api.service.ts`). The base URL is configured in `src/environments/environment.ts`.
diff --git a/webapp/src/app/core/models/channel.model.ts b/webapp/src/app/core/models/channel.model.ts
index 5517884..23b00a1 100644
--- a/webapp/src/app/core/models/channel.model.ts
+++ b/webapp/src/app/core/models/channel.model.ts
@@ -7,7 +7,6 @@ export interface Channel {
display_name: string;
description_name: string | null;
subscribe_key?: string;
- send_key?: string;
timestamp_created: string;
timestamp_lastsent: string | null;
messages_sent: number;
@@ -34,8 +33,7 @@ export interface CreateChannelRequest {
export interface UpdateChannelRequest {
display_name?: string;
description_name?: string;
- subscribe_key?: string;
- send_key?: string;
+ subscribe_key?: boolean; // RefreshSubscribeKey
}
export interface ChannelListResponse {
diff --git a/webapp/src/app/core/services/auth.service.ts b/webapp/src/app/core/services/auth.service.ts
index 8b8b362..ea80279 100644
--- a/webapp/src/app/core/services/auth.service.ts
+++ b/webapp/src/app/core/services/auth.service.ts
@@ -17,8 +17,8 @@ export class AuthService {
}
private loadFromStorage(): void {
- const userId = sessionStorage.getItem(USER_ID_KEY);
- const adminKey = sessionStorage.getItem(ADMIN_KEY_KEY);
+ const userId = localStorage.getItem(USER_ID_KEY);
+ const adminKey = localStorage.getItem(ADMIN_KEY_KEY);
if (userId && adminKey) {
this.userId.set(userId);
this.adminKey.set(adminKey);
@@ -26,15 +26,15 @@ export class AuthService {
}
login(userId: string, adminKey: string): void {
- sessionStorage.setItem(USER_ID_KEY, userId);
- sessionStorage.setItem(ADMIN_KEY_KEY, adminKey);
+ localStorage.setItem(USER_ID_KEY, userId);
+ localStorage.setItem(ADMIN_KEY_KEY, adminKey);
this.userId.set(userId);
this.adminKey.set(adminKey);
}
logout(): void {
- sessionStorage.removeItem(USER_ID_KEY);
- sessionStorage.removeItem(ADMIN_KEY_KEY);
+ localStorage.removeItem(USER_ID_KEY);
+ localStorage.removeItem(ADMIN_KEY_KEY);
this.userId.set(null);
this.adminKey.set(null);
}
diff --git a/webapp/src/app/core/services/settings.service.ts b/webapp/src/app/core/services/settings.service.ts
index ec53afd..83f92f2 100644
--- a/webapp/src/app/core/services/settings.service.ts
+++ b/webapp/src/app/core/services/settings.service.ts
@@ -16,9 +16,7 @@ export class SettingsService {
private loadFromStorage(): void {
const stored = localStorage.getItem(EXPERT_MODE_KEY);
- if (stored === 'true') {
- this._expertMode.set(true);
- }
+ this._expertMode.set(stored === 'true');
}
setExpertMode(enabled: boolean): void {
diff --git a/webapp/src/app/features/auth/login/login.component.html b/webapp/src/app/features/auth/login/login.component.html
index 6f5a3c0..8e794aa 100644
--- a/webapp/src/app/features/auth/login/login.component.html
+++ b/webapp/src/app/features/auth/login/login.component.html
@@ -14,59 +14,37 @@
>
}
-
+
@@ -110,42 +109,6 @@
}
- @if (isOwner() && channel()!.send_key) {
-
-
-
-
-
-
-
-
-
-
-
-
-
- }
@@ -238,18 +201,20 @@
} @else {
-
+ @if (expertMode()) {
+
+ }
}
diff --git a/webapp/src/app/features/channels/channel-detail/channel-detail.component.scss b/webapp/src/app/features/channels/channel-detail/channel-detail.component.scss
index 5b399c3..815c35f 100644
--- a/webapp/src/app/features/channels/channel-detail/channel-detail.component.scss
+++ b/webapp/src/app/features/channels/channel-detail/channel-detail.component.scss
@@ -12,7 +12,7 @@
.key-field {
display: flex;
- flex-direction: column;
+ flex-direction: row;
gap: 8px;
}
diff --git a/webapp/src/app/features/channels/channel-detail/channel-detail.component.ts b/webapp/src/app/features/channels/channel-detail/channel-detail.component.ts
index a0a9606..40e9551 100644
--- a/webapp/src/app/features/channels/channel-detail/channel-detail.component.ts
+++ b/webapp/src/app/features/channels/channel-detail/channel-detail.component.ts
@@ -280,7 +280,7 @@ export class ChannelDetailComponent implements OnInit {
if (!channel || !userId) return;
this.apiService.updateChannel(userId, channel.channel_id, {
- subscribe_key: 'true'
+ subscribe_key: true
}).subscribe({
next: (updated) => {
this.channel.set(updated);
@@ -289,21 +289,6 @@ export class ChannelDetailComponent implements OnInit {
});
}
- regenerateSendKey(): void {
- const channel = this.channel();
- const userId = this.authService.getUserId();
- if (!channel || !userId) return;
-
- this.apiService.updateChannel(userId, channel.channel_id, {
- send_key: 'true'
- }).subscribe({
- next: (updated) => {
- this.channel.set(updated);
- this.notification.success('Send key regenerated');
- }
- });
- }
-
getSubscriptionStatus(): { label: string; color: string } {
const channel = this.channel();
if (!channel) return { label: 'Unknown', color: 'default' };
diff --git a/webapp/src/app/features/subscriptions/subscription-list/subscription-list.component.html b/webapp/src/app/features/subscriptions/subscription-list/subscription-list.component.html
index b8dd074..183c1c6 100644
--- a/webapp/src/app/features/subscriptions/subscription-list/subscription-list.component.html
+++ b/webapp/src/app/features/subscriptions/subscription-list/subscription-list.component.html
@@ -161,18 +161,20 @@
}
}
-
+ @if (expertMode()) {
+
+ }
}
diff --git a/webapp/src/app/features/subscriptions/subscription-list/subscription-list.component.ts b/webapp/src/app/features/subscriptions/subscription-list/subscription-list.component.ts
index a49cf8e..f5b483e 100644
--- a/webapp/src/app/features/subscriptions/subscription-list/subscription-list.component.ts
+++ b/webapp/src/app/features/subscriptions/subscription-list/subscription-list.component.ts
@@ -18,6 +18,7 @@ import { NzPaginationModule } from 'ng-zorro-antd/pagination';
import { ApiService } from '../../../core/services/api.service';
import { AuthService } from '../../../core/services/auth.service';
import { NotificationService } from '../../../core/services/notification.service';
+import { SettingsService } from '../../../core/services/settings.service';
import { UserCacheService, ResolvedUser } from '../../../core/services/user-cache.service';
import { Subscription, SubscriptionFilter } from '../../../core/models';
import { RelativeTimePipe } from '../../../shared/pipes/relative-time.pipe';
@@ -67,8 +68,11 @@ export class SubscriptionListComponent implements OnInit {
private apiService = inject(ApiService);
private authService = inject(AuthService);
private notification = inject(NotificationService);
+ private settingsService = inject(SettingsService);
private userCacheService = inject(UserCacheService);
+ expertMode = this.settingsService.expertMode;
+
subscriptions = signal([]);
userNames = signal