Skip to content

feat: Add application owners#13

Merged
wellCh4n merged 1 commit into
mainfrom
codex/applicationowner
Apr 10, 2026
Merged

feat: Add application owners#13
wellCh4n merged 1 commit into
mainfrom
codex/applicationowner

Conversation

@wellCh4n
Copy link
Copy Markdown
Owner

Summary

  • add owner to applications and set it automatically from the authenticated user when creating an application
  • extend JWT/auth handling to carry userId alongside username, and expose the user id in login responses and frontend auth state
  • introduce ApplicationResponse with owner and ownerName, and return it from application detail, list, and search endpoints
  • add owner editing to the application basic info form and show owner information in the app list and command palette
  • keep JWT parsing backward-compatible by resolving missing userId from username in the auth filter

Testing

  • ./mvnw -q -DskipTests compile
  • pnpm exec eslint lib/api/auth.ts lib/auth.ts app/auth/feishu/callback/page.tsx
  • pnpm exec eslint app/apps/page.tsx app/apps/columns.tsx lib/api/types.ts components/command-palette.tsx 'app/apps/[namespace]/[name]/page.tsx' app/apps/components/application-basic-info.tsx lib/api/applications.ts
  • pnpm exec eslint components/command-palette.tsx store/recent-app.ts
  • ./mvnw -q spring-boot:run

@wellCh4n wellCh4n changed the title Add application owners to auth, API responses, and UI feat: Add application owners Apr 10, 2026
@wellCh4n wellCh4n requested a review from Copilot April 10, 2026 03:33
@wellCh4n wellCh4n marked this pull request as ready for review April 10, 2026 03:33
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class “application owner” support end-to-end (DB/API/UI) and extends authentication/JWT plumbing to propagate userId alongside username so ownership can be set automatically and displayed consistently.

Changes:

  • Add owner to applications and expose ownerName via a new ApplicationResponse returned by app detail/list/search APIs.
  • Extend auth/JWT to include userId, expose it in login responses and frontend auth state, and provide backward-compatible JWT parsing.
  • Update the web UI to view/edit owner (basic info form, apps list, command palette, recent apps) with new i18n strings.

Reviewed changes

Copilot reviewed 25 out of 25 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
web/store/recent-app.ts Persists ownerName for recent app entries.
web/locales/zh.ts Adds owner-related i18n strings (ZH).
web/locales/en.ts Adds owner-related i18n strings (EN).
web/lib/auth.ts Stores auth_user_id and exposes getUserId().
web/lib/api/types.ts Extends Application type with owner/ownerName; relaxes workspaceId.
web/lib/api/auth.ts Includes id in login result and passes it into setAuth.
web/components/command-palette.tsx Displays owner in palette results and persists it in recent app state.
web/app/auth/feishu/callback/page.tsx Updates Feishu callback flow to store userId in auth state.
web/app/apps/schema.ts Allows owner field in the application basic schema.
web/app/apps/components/application-create-dialog.tsx Refactors reset logic when dialog opens.
web/app/apps/components/application-basic-info.tsx Adds owner selector populated from users API; wires owner into form defaults.
web/app/apps/columns.tsx Adds “Owner” column to applications table.
src/main/java/com/github/wellch4n/oops/utils/JwtUtils.java Adds userId claim to JWT and helper to read it.
src/main/java/com/github/wellch4n/oops/service/UserService.java Adds helper to map user IDs to usernames in bulk.
src/main/java/com/github/wellch4n/oops/service/external/FeishuAuthStrategy.java Generates JWTs with userId claim.
src/main/java/com/github/wellch4n/oops/service/ApplicationService.java Sets owner on create; normalizes/validates owner; returns ApplicationResponse with ownerName for list/search/detail.
src/main/java/com/github/wellch4n/oops/objects/LoginResponse.java Extends login response to include id.
src/main/java/com/github/wellch4n/oops/objects/AuthUserPrincipal.java Introduces principal carrying userId + username.
src/main/java/com/github/wellch4n/oops/objects/ApplicationResponse.java New response DTO including owner + ownerName.
src/main/java/com/github/wellch4n/oops/data/Application.java Adds owner field to application entity.
src/main/java/com/github/wellch4n/oops/controller/UserController.java Updates /me to resolve current user by userId.
src/main/java/com/github/wellch4n/oops/controller/SearchController.java Returns ApplicationResponse from app search endpoint.
src/main/java/com/github/wellch4n/oops/controller/AuthController.java Returns id in login response and embeds userId in JWT.
src/main/java/com/github/wellch4n/oops/controller/ApplicationController.java Returns ApplicationResponse for app detail/list; sets owner from authenticated principal on create.
src/main/java/com/github/wellch4n/oops/config/JwtAuthFilter.java Builds AuthUserPrincipal from JWT userId (fallback to username lookup for legacy tokens).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 46 to 56
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))
);
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

userId can still end up null here (e.g., legacy token without userId claim + username not found). The filter still authenticates the request with an AuthUserPrincipal(null, username), which will later cause failures (e.g., /api/users/me calling findById(null) throws) and may allow creating apps with an unverified owner. Consider treating the token as invalid when userId cannot be resolved (skip setting authentication), or explicitly fail the request, rather than authenticating with a null userId.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

Comment on lines 39 to 43
public Result<User> 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("用户不存在"));
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

principal.userId() may be null (e.g., legacy JWT missing userId where username→id lookup fails). Passing null into userService.findById will throw in Spring Data, resulting in a 500 for /me. Please guard against a missing userId (e.g., fall back to authentication.getName()/principal.username() lookup, or return an auth failure) to keep backward compatibility safe.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

Comment on lines 54 to +58
const envRes = await fetchEnvironments()
setEnvironments(envRes.data)

setUsers(await fetchUsers())

Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new owner selector loads the full /api/users list into the app edit form. Since the backend currently returns User entities directly (which include sensitive fields like email and password hash), this makes it easy for any user who can access this screen to retrieve sensitive user data via the network response. Consider switching this call to a dedicated “user lookup for owner selection” endpoint/DTO that only returns id + username (and enforce appropriate authorization on /api/users).

Copilot uses AI. Check for mistakes.
Copilot stopped work on behalf of wellCh4n due to an error April 10, 2026 03:44
@wellCh4n wellCh4n merged commit 20625ae into main Apr 10, 2026
4 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants