From 658dc07eaa774a9c58beaafb10408b7eba67e889 Mon Sep 17 00:00:00 2001 From: wellCh4n Date: Fri, 10 Apr 2026 11:32:09 +0800 Subject: [PATCH] Add owner support and include user IDs in auth tokens --- .../wellch4n/oops/config/JwtAuthFilter.java | 14 +++- .../controller/ApplicationController.java | 22 +++--- .../oops/controller/AuthController.java | 4 +- .../oops/controller/SearchController.java | 4 +- .../oops/controller/UserController.java | 4 +- .../wellch4n/oops/data/Application.java | 2 + .../oops/objects/ApplicationResponse.java | 26 +++++++ .../oops/objects/AuthUserPrincipal.java | 11 +++ .../wellch4n/oops/objects/LoginResponse.java | 2 +- .../oops/service/ApplicationService.java | 72 ++++++++++++++++--- .../wellch4n/oops/service/UserService.java | 11 +++ .../service/external/FeishuAuthStrategy.java | 2 +- .../github/wellch4n/oops/utils/JwtUtils.java | 7 +- web/app/apps/columns.tsx | 10 +++ .../components/application-basic-info.tsx | 36 ++++++++++ .../components/application-create-dialog.tsx | 14 ++-- web/app/apps/schema.ts | 1 + web/app/auth/feishu/callback/page.tsx | 4 +- web/components/command-palette.tsx | 16 ++++- web/lib/api/auth.ts | 3 +- web/lib/api/types.ts | 4 +- web/lib/auth.ts | 9 ++- web/locales/en.ts | 4 ++ web/locales/zh.ts | 4 ++ web/store/recent-app.ts | 1 + 25 files changed, 247 insertions(+), 40 deletions(-) create mode 100644 src/main/java/com/github/wellch4n/oops/objects/ApplicationResponse.java create mode 100644 src/main/java/com/github/wellch4n/oops/objects/AuthUserPrincipal.java diff --git a/src/main/java/com/github/wellch4n/oops/config/JwtAuthFilter.java b/src/main/java/com/github/wellch4n/oops/config/JwtAuthFilter.java index 6a226b2e..e41d6993 100644 --- a/src/main/java/com/github/wellch4n/oops/config/JwtAuthFilter.java +++ b/src/main/java/com/github/wellch4n/oops/config/JwtAuthFilter.java @@ -1,5 +1,8 @@ package com.github.wellch4n.oops.config; +import com.github.wellch4n.oops.data.User; +import com.github.wellch4n.oops.data.UserRepository; +import com.github.wellch4n.oops.objects.AuthUserPrincipal; import com.github.wellch4n.oops.utils.JwtUtils; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; @@ -18,9 +21,11 @@ public class JwtAuthFilter extends OncePerRequestFilter { private final JwtUtils jwtUtils; + private final UserRepository userRepository; - public JwtAuthFilter(JwtUtils jwtUtils) { + public JwtAuthFilter(JwtUtils jwtUtils, UserRepository userRepository) { this.jwtUtils = jwtUtils; + this.userRepository = userRepository; } @Override @@ -40,9 +45,14 @@ protected void doFilterInternal(@NonNull HttpServletRequest request, } if (token != null && jwtUtils.isValid(token)) { String username = jwtUtils.getUsername(token); + String userId = jwtUtils.getUserId(token); + if (userId == null || userId.isBlank()) { + userId = userRepository.findByUsername(username).map(User::getId).orElse(null); + } String role = jwtUtils.getRole(token); + AuthUserPrincipal principal = new AuthUserPrincipal(userId, username); UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken( - username, null, List.of(new SimpleGrantedAuthority("ROLE_" + role)) + principal, null, List.of(new SimpleGrantedAuthority("ROLE_" + role)) ); SecurityContextHolder.getContext().setAuthentication(auth); } diff --git a/src/main/java/com/github/wellch4n/oops/controller/ApplicationController.java b/src/main/java/com/github/wellch4n/oops/controller/ApplicationController.java index 859d15d6..7c2190d6 100644 --- a/src/main/java/com/github/wellch4n/oops/controller/ApplicationController.java +++ b/src/main/java/com/github/wellch4n/oops/controller/ApplicationController.java @@ -2,7 +2,9 @@ import com.github.wellch4n.oops.data.*; import com.github.wellch4n.oops.enums.DeployMode; +import com.github.wellch4n.oops.objects.AuthUserPrincipal; import com.github.wellch4n.oops.objects.ApplicationPodStatusResponse; +import com.github.wellch4n.oops.objects.ApplicationResponse; import com.github.wellch4n.oops.objects.ClusterDomainResponse; import com.github.wellch4n.oops.objects.Page; import com.github.wellch4n.oops.objects.Result; @@ -10,6 +12,8 @@ import com.github.wellch4n.oops.service.DeploymentService; import com.github.wellch4n.oops.service.PipelineService; import java.util.List; + +import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; /** @@ -32,22 +36,24 @@ public ApplicationController(ApplicationService applicationService, DeploymentSe } @GetMapping("/{name}") - public Result getApplication(@PathVariable String namespace, @PathVariable String name) { - return Result.success(applicationService.getApplication(namespace, name)); + public Result getApplication(@PathVariable String namespace, @PathVariable String name) { + return Result.success(applicationService.getApplicationResponse(namespace, name)); } @GetMapping - public Result> getApplications(@PathVariable String namespace, - @RequestParam(required = false) String keyword, - @RequestParam(defaultValue = "1") int page, - @RequestParam(defaultValue = "10") int size) { + public Result> getApplications(@PathVariable String namespace, + @RequestParam(required = false) String keyword, + @RequestParam(defaultValue = "1") int page, + @RequestParam(defaultValue = "10") int size) { return Result.success(applicationService.getApplications(namespace, keyword, page, size)); } @PostMapping public Result createApplication(@PathVariable String namespace, - @RequestBody Application application) { - return Result.success(applicationService.createApplication(namespace, application)); + @RequestBody Application application, + Authentication authentication) { + AuthUserPrincipal principal = (AuthUserPrincipal) authentication.getPrincipal(); + return Result.success(applicationService.createApplication(namespace, application, principal.userId())); } @PutMapping("/{name}") diff --git a/src/main/java/com/github/wellch4n/oops/controller/AuthController.java b/src/main/java/com/github/wellch4n/oops/controller/AuthController.java index 3d32c564..4106ee83 100644 --- a/src/main/java/com/github/wellch4n/oops/controller/AuthController.java +++ b/src/main/java/com/github/wellch4n/oops/controller/AuthController.java @@ -31,7 +31,7 @@ public Result login(@RequestBody LoginRequest request) { return Result.failure("用户名或密码错误"); } User user = userOpt.get(); - String token = jwtUtils.generateToken(user.getUsername(), user.getRole().name()); - return Result.success(new LoginResponse(token, user.getUsername(), user.getRole())); + String token = jwtUtils.generateToken(user.getId(), user.getUsername(), user.getRole().name()); + return Result.success(new LoginResponse(token, user.getId(), user.getUsername(), user.getRole())); } } diff --git a/src/main/java/com/github/wellch4n/oops/controller/SearchController.java b/src/main/java/com/github/wellch4n/oops/controller/SearchController.java index 9c150f48..685a2da5 100644 --- a/src/main/java/com/github/wellch4n/oops/controller/SearchController.java +++ b/src/main/java/com/github/wellch4n/oops/controller/SearchController.java @@ -1,6 +1,6 @@ package com.github.wellch4n.oops.controller; -import com.github.wellch4n.oops.data.Application; +import com.github.wellch4n.oops.objects.ApplicationResponse; import com.github.wellch4n.oops.objects.Result; import com.github.wellch4n.oops.service.ApplicationService; import java.util.List; @@ -25,7 +25,7 @@ public SearchController(ApplicationService applicationService) { } @GetMapping("/applications") - public Result> searchApplications(@RequestParam(required = false) String keyword, @RequestParam(defaultValue = "5") int size) { + public Result> searchApplications(@RequestParam(required = false) String keyword, @RequestParam(defaultValue = "5") int size) { return Result.success(applicationService.searchApplications(keyword, size)); } } diff --git a/src/main/java/com/github/wellch4n/oops/controller/UserController.java b/src/main/java/com/github/wellch4n/oops/controller/UserController.java index 0c97ed36..1b1d0bde 100644 --- a/src/main/java/com/github/wellch4n/oops/controller/UserController.java +++ b/src/main/java/com/github/wellch4n/oops/controller/UserController.java @@ -2,6 +2,7 @@ import com.github.wellch4n.oops.data.User; import com.github.wellch4n.oops.enums.UserRole; +import com.github.wellch4n.oops.objects.AuthUserPrincipal; import com.github.wellch4n.oops.objects.CreateUserRequest; import com.github.wellch4n.oops.objects.Result; import com.github.wellch4n.oops.objects.UpdateUserRequest; @@ -36,7 +37,8 @@ public Result> listUsers() { @GetMapping("/me") @PreAuthorize("isAuthenticated()") public Result me(org.springframework.security.core.Authentication authentication) { - return userService.findByUsername(authentication.getName()) + AuthUserPrincipal principal = (AuthUserPrincipal) authentication.getPrincipal(); + return userService.findById(principal.userId()) .map(Result::success) .orElse(Result.failure("用户不存在")); } diff --git a/src/main/java/com/github/wellch4n/oops/data/Application.java b/src/main/java/com/github/wellch4n/oops/data/Application.java index 30d11b8d..34380b3a 100644 --- a/src/main/java/com/github/wellch4n/oops/data/Application.java +++ b/src/main/java/com/github/wellch4n/oops/data/Application.java @@ -14,4 +14,6 @@ public class Application extends BaseDataObject { private String description; private String namespace; + + private String owner; } diff --git a/src/main/java/com/github/wellch4n/oops/objects/ApplicationResponse.java b/src/main/java/com/github/wellch4n/oops/objects/ApplicationResponse.java new file mode 100644 index 00000000..db64c220 --- /dev/null +++ b/src/main/java/com/github/wellch4n/oops/objects/ApplicationResponse.java @@ -0,0 +1,26 @@ +package com.github.wellch4n.oops.objects; + +import com.github.wellch4n.oops.data.Application; +import java.time.LocalDateTime; + +public record ApplicationResponse( + String id, + LocalDateTime createdTime, + String name, + String description, + String namespace, + String owner, + String ownerName +) { + public static ApplicationResponse from(Application application, String ownerName) { + return new ApplicationResponse( + application.getId(), + application.getCreatedTime(), + application.getName(), + application.getDescription(), + application.getNamespace(), + application.getOwner(), + ownerName + ); + } +} diff --git a/src/main/java/com/github/wellch4n/oops/objects/AuthUserPrincipal.java b/src/main/java/com/github/wellch4n/oops/objects/AuthUserPrincipal.java new file mode 100644 index 00000000..a99dc706 --- /dev/null +++ b/src/main/java/com/github/wellch4n/oops/objects/AuthUserPrincipal.java @@ -0,0 +1,11 @@ +package com.github.wellch4n.oops.objects; + +import java.security.Principal; + +public record AuthUserPrincipal(String userId, String username) implements Principal { + + @Override + public String getName() { + return username; + } +} diff --git a/src/main/java/com/github/wellch4n/oops/objects/LoginResponse.java b/src/main/java/com/github/wellch4n/oops/objects/LoginResponse.java index 8e590915..39d6931a 100644 --- a/src/main/java/com/github/wellch4n/oops/objects/LoginResponse.java +++ b/src/main/java/com/github/wellch4n/oops/objects/LoginResponse.java @@ -2,4 +2,4 @@ import com.github.wellch4n.oops.enums.UserRole; -public record LoginResponse(String token, String username, UserRole role) {} +public record LoginResponse(String token, String id, String username, UserRole role) {} diff --git a/src/main/java/com/github/wellch4n/oops/service/ApplicationService.java b/src/main/java/com/github/wellch4n/oops/service/ApplicationService.java index 71be2784..d1801338 100644 --- a/src/main/java/com/github/wellch4n/oops/service/ApplicationService.java +++ b/src/main/java/com/github/wellch4n/oops/service/ApplicationService.java @@ -3,6 +3,7 @@ import com.github.wellch4n.oops.data.*; import com.github.wellch4n.oops.enums.OopsTypes; import com.github.wellch4n.oops.objects.ApplicationPodStatusResponse; +import com.github.wellch4n.oops.objects.ApplicationResponse; import com.github.wellch4n.oops.objects.ClusterDomainResponse; import com.github.wellch4n.oops.objects.Page; import io.fabric8.kubernetes.api.model.Container; @@ -10,7 +11,9 @@ import io.fabric8.kubernetes.api.model.ResourceRequirementsBuilder; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Optional; +import java.util.Set; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.data.domain.PageRequest; @@ -36,39 +39,54 @@ public class ApplicationService { private final ApplicationEnvironmentRepository applicationEnvironmentRepository; private final ApplicationServiceConfigRepository applicationServiceConfigRepository; private final EnvironmentRepository environmentRepository; + private final UserService userService; public ApplicationService(ApplicationRepository applicationRepository, ApplicationBuildConfigRepository applicationBuildConfigRepository, ApplicationPerformanceConfigRepository applicationPerformanceConfigRepository, ApplicationEnvironmentRepository applicationEnvironmentRepository, ApplicationServiceConfigRepository applicationServiceConfigRepository, - EnvironmentRepository environmentRepository) { + EnvironmentRepository environmentRepository, + UserService userService) { this.applicationRepository = applicationRepository; this.applicationBuildConfigRepository = applicationBuildConfigRepository; this.applicationPerformanceConfigRepository = applicationPerformanceConfigRepository; this.applicationEnvironmentRepository = applicationEnvironmentRepository; this.applicationServiceConfigRepository = applicationServiceConfigRepository; this.environmentRepository = environmentRepository; + this.userService = userService; } public Application getApplication(String namespace, String name) { return applicationRepository.findByNamespaceAndName(namespace, name); } - public Page getApplications(String namespace, String keyword, int page, int size) { + public ApplicationResponse getApplicationResponse(String namespace, String name) { + return toApplicationResponse(applicationRepository.findByNamespaceAndName(namespace, name)); + } + + public Page getApplications(String namespace, String keyword, int page, int size) { Pageable pageable = PageRequest.of(Math.max(page - 1, 0), size); - return Page.of(applicationRepository.findByNamespaceAndNameContainingIgnoreCase( - namespace, StringUtils.defaultIfBlank(keyword, ""), pageable)); + org.springframework.data.domain.Page applicationPage = + applicationRepository.findByNamespaceAndNameContainingIgnoreCase( + namespace, StringUtils.defaultIfBlank(keyword, ""), pageable); + return new Page<>( + applicationPage.getTotalElements(), + toApplicationResponses(applicationPage.getContent()), + applicationPage.getSize(), + applicationPage.getTotalPages() + ); } - public List searchApplications(String keyword, int size) { + public List searchApplications(String keyword, int size) { List applications = applicationRepository.findByNameContainingIgnoreCase( StringUtils.defaultIfBlank(keyword, "")); - return applications.stream().limit(size).toList(); + return toApplicationResponses(applications.stream().limit(size).toList()); } @Transactional - public String createApplication(String namespace, Application application) { + public String createApplication(String namespace, Application application, String creatorUserId) { application.setNamespace(namespace); + application.setOwner(normalizeOwner(creatorUserId)); applicationRepository.save(application); return application.getId(); } @@ -79,11 +97,47 @@ public Boolean updateApplication(String namespace, String name, Application appl if (exist == null) { throw new RuntimeException("Application not found"); } - application.setDescription(application.getDescription()); - applicationRepository.save(application); + exist.setDescription(application.getDescription()); + exist.setOwner(normalizeOwner(application.getOwner())); + applicationRepository.save(exist); return true; } + private String normalizeOwner(String owner) { + if (StringUtils.isBlank(owner)) { + return null; + } + Optional user = userService.findById(owner); + if (user.isEmpty()) { + throw new RuntimeException("Owner user not found"); + } + return owner; + } + + private List toApplicationResponses(List applications) { + Set ownerIds = applications.stream() + .map(Application::getOwner) + .filter(StringUtils::isNotBlank) + .collect(java.util.stream.Collectors.toSet()); + Map ownerNameMap = userService.getUsernameMapByIds(ownerIds); + return applications.stream() + .map(application -> ApplicationResponse.from(application, ownerNameMap.get(application.getOwner()))) + .toList(); + } + + private ApplicationResponse toApplicationResponse(Application application) { + if (application == null) { + return null; + } + String ownerName = null; + if (StringUtils.isNotBlank(application.getOwner())) { + ownerName = userService.findById(application.getOwner()) + .map(User::getUsername) + .orElse(null); + } + return ApplicationResponse.from(application, ownerName); + } + @Transactional public Boolean updateApplicationBuildConfig(String namespace, String name, ApplicationBuildConfig request) { ApplicationBuildConfig buildConfig = applicationBuildConfigRepository.findByNamespaceAndApplicationName(namespace, name) diff --git a/src/main/java/com/github/wellch4n/oops/service/UserService.java b/src/main/java/com/github/wellch4n/oops/service/UserService.java index 5602ea3f..a6307b83 100644 --- a/src/main/java/com/github/wellch4n/oops/service/UserService.java +++ b/src/main/java/com/github/wellch4n/oops/service/UserService.java @@ -3,8 +3,11 @@ import com.github.wellch4n.oops.data.User; import com.github.wellch4n.oops.data.UserRepository; import com.github.wellch4n.oops.enums.UserRole; +import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.Optional; +import java.util.stream.Collectors; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; @@ -31,6 +34,14 @@ public Optional findByEmail(String email) { return userRepository.findByEmail(email); } + public Map getUsernameMapByIds(Collection ids) { + if (ids == null || ids.isEmpty()) { + return Map.of(); + } + return userRepository.findAllById(ids).stream() + .collect(Collectors.toMap(User::getId, User::getUsername, (left, right) -> left)); + } + public Optional findByUsernameOrEmail(String identifier) { Optional user = userRepository.findByUsername(identifier); if (user.isPresent()) return user; diff --git a/src/main/java/com/github/wellch4n/oops/service/external/FeishuAuthStrategy.java b/src/main/java/com/github/wellch4n/oops/service/external/FeishuAuthStrategy.java index 5f5cd715..872b89c8 100644 --- a/src/main/java/com/github/wellch4n/oops/service/external/FeishuAuthStrategy.java +++ b/src/main/java/com/github/wellch4n/oops/service/external/FeishuAuthStrategy.java @@ -117,7 +117,7 @@ public String authenticate(String code) throws IOException { externalAccountRepository.save(account); } - return jwtUtils.generateToken(user.getUsername(), user.getRole().name()); + return jwtUtils.generateToken(user.getId(), user.getUsername(), user.getRole().name()); } private User findOrCreateUser(String name, String email) { diff --git a/src/main/java/com/github/wellch4n/oops/utils/JwtUtils.java b/src/main/java/com/github/wellch4n/oops/utils/JwtUtils.java index 51299ecd..f0520f4b 100644 --- a/src/main/java/com/github/wellch4n/oops/utils/JwtUtils.java +++ b/src/main/java/com/github/wellch4n/oops/utils/JwtUtils.java @@ -22,9 +22,10 @@ private SecretKey getSigningKey() { return Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)); } - public String generateToken(String username, String role) { + public String generateToken(String userId, String username, String role) { return Jwts.builder() .subject(username) + .claim("userId", userId) .claim("role", role) .issuedAt(new Date()) .expiration(new Date(System.currentTimeMillis() + expiration)) @@ -44,6 +45,10 @@ public String getUsername(String token) { return parseToken(token).getSubject(); } + public String getUserId(String token) { + return parseToken(token).get("userId", String.class); + } + public String getRole(String token) { return parseToken(token).get("role", String.class); } diff --git a/web/app/apps/columns.tsx b/web/app/apps/columns.tsx index a5e0b678..dfeb1869 100644 --- a/web/app/apps/columns.tsx +++ b/web/app/apps/columns.tsx @@ -29,6 +29,16 @@ export const getColumns = (t: (key: string) => string): ColumnDef[] accessorKey: "namespace", header: t("apps.col.namespace"), }, + { + accessorKey: "owner", + header: t("apps.col.owner"), + cell: ({ row }) => { + if (!row.original.owner) { + return t("common.unassigned") + } + return row.original.ownerName || row.original.owner + }, + }, { id: "actions", cell: ({ row, table }) => { diff --git a/web/app/apps/components/application-basic-info.tsx b/web/app/apps/components/application-basic-info.tsx index e5aa55ee..cfc031a4 100644 --- a/web/app/apps/components/application-basic-info.tsx +++ b/web/app/apps/components/application-basic-info.tsx @@ -28,6 +28,7 @@ import { Application, Environment, ApplicationEnvironment } from "@/lib/api/type import { updateApplication, getApplicationEnvironments, updateApplicationEnvironments } from "@/lib/api/applications" import { fetchNamespaces } from "@/lib/api/namespaces" import { fetchEnvironments } from "@/lib/api/environments" +import { fetchUsers, User } from "@/lib/api/users" import { AppWindow, Layers, AlignLeft, Server } from "lucide-react" import { useLanguage } from "@/contexts/language-context" @@ -41,6 +42,7 @@ export function ApplicationBasicInfo({ const [namespaces, setNamespaces] = useState([]) const [environments, setEnvironments] = useState([]) const [selectedEnvNames, setSelectedEnvNames] = useState([]) + const [users, setUsers] = useState([]) const { t } = useLanguage() useEffect(() => { @@ -52,6 +54,8 @@ export function ApplicationBasicInfo({ const envRes = await fetchEnvironments() setEnvironments(envRes.data) + setUsers(await fetchUsers()) + if (initialData) { const appEnvRes = await getApplicationEnvironments(initialData.namespace, initialData.name) setSelectedEnvNames(appEnvRes.data.map(e => e.environmentName)) @@ -70,10 +74,12 @@ export function ApplicationBasicInfo({ name: initialData.name, namespace: initialData.namespace, description: initialData.description, + owner: initialData.owner ?? "", } : { name: "", namespace: "", description: "", + owner: "", }, mode: "onChange", }) @@ -155,6 +161,36 @@ export function ApplicationBasicInfo({ )} /> + ( + + {t("common.owner")} + + + + )} + /> + { - if (open) { - form.reset({ - name: "", - namespace: defaultNamespace || "", - description: "", - }) + if (!open) { + return } + + form.reset({ + name: "", + namespace: defaultNamespace || "", + description: "", + }) }, [open, defaultNamespace, form]) const onSubmitCreate = async (data: CreateApplicationFormValues) => { diff --git a/web/app/apps/schema.ts b/web/app/apps/schema.ts index b48dddd6..b1ca6426 100644 --- a/web/app/apps/schema.ts +++ b/web/app/apps/schema.ts @@ -5,6 +5,7 @@ export const applicationBasicSchema = z.object({ name: z.string().min(1, "Name is required"), namespace: z.string().min(1, "Namespace is required"), description: z.string().optional(), + owner: z.string().optional(), }) export const applicationBuildConfigSchema = z.object({ diff --git a/web/app/auth/feishu/callback/page.tsx b/web/app/auth/feishu/callback/page.tsx index 6436a8ae..bdafed69 100644 --- a/web/app/auth/feishu/callback/page.tsx +++ b/web/app/auth/feishu/callback/page.tsx @@ -23,7 +23,7 @@ export default function FeishuCallbackPage() { }) .then(({ token, user }) => { if (user) { - setAuth(token, user.username, user.role) + setAuth(token, user.id, user.username, user.role) } window.location.href = "/" }) @@ -38,4 +38,4 @@ export default function FeishuCallbackPage() {

处理中...

) -} \ No newline at end of file +} diff --git a/web/components/command-palette.tsx b/web/components/command-palette.tsx index 2e23d56c..fd883606 100644 --- a/web/components/command-palette.tsx +++ b/web/components/command-palette.tsx @@ -185,6 +185,7 @@ export function CommandPalette() { namespace: app.namespace, name: app.name, description: app.description, + ownerName: app.ownerName, }) setOpen(false) @@ -319,6 +320,7 @@ export function CommandPalette() { name: recentApp.name, description: recentApp.description, namespace: recentApp.namespace, + ownerName: recentApp.ownerName, }) } className="flex flex-col items-start gap-1 py-3 cursor-pointer" @@ -328,8 +330,13 @@ export function CommandPalette() { {recentApp.name} - {recentApp.namespace} + {t("common.namespace")}: {recentApp.namespace} + {recentApp.ownerName && ( + + {t("common.owner")}: {recentApp.ownerName} + + )} {recentApp.description && ( @@ -362,8 +369,13 @@ export function CommandPalette() { {app.name} - {app.namespace} + {t("common.namespace")}: {app.namespace} + {app.ownerName && ( + + {t("common.owner")}: {app.ownerName} + + )} {app.description && ( diff --git a/web/lib/api/auth.ts b/web/lib/api/auth.ts index 5418748c..ab7984f2 100644 --- a/web/lib/api/auth.ts +++ b/web/lib/api/auth.ts @@ -5,6 +5,7 @@ import { ApiResponse } from "./types" export interface LoginResult { token: string + id: string username: string role: string } @@ -58,7 +59,7 @@ export async function login(username: string, password: string): Promise