feat(desktop): implement workspace foundation + desktop-client skeleton
Plan 0 (workspaces): add workspaces + workspace_memberships schema, extend JWT/Actor/claims with primary_workspace_id, seed default workspace per tenant, thread workspace_id through tenant monitoring quota. Plan A (desktop skeleton): new Electron app (apps/desktop-client) with main/ preload/renderer, shared Vue component package (packages/ui-shared), and server surface — desktop client registration + token rotation + heartbeat, SSE task event stream, desktop accounts/tasks/content handlers, publish job endpoint, and supporting repositories, services, sqlc queries, and migrations. Hard cutover per plan: remove browser-extension monitoring callback endpoints, stub legacy media API in admin-web, and delete monitoring_callback_handler.go.
This commit is contained in:
@@ -49,15 +49,16 @@ type LoginResponse struct {
|
||||
}
|
||||
|
||||
type UserInfo struct {
|
||||
ID int64 `json:"id"`
|
||||
Email string `json:"email"`
|
||||
Name string `json:"name"`
|
||||
AvatarURL *string `json:"avatar_url"`
|
||||
TenantID int64 `json:"tenant_id"`
|
||||
TenantRole string `json:"tenant_role"`
|
||||
Permissions []string `json:"permissions"`
|
||||
Membership *MembershipInfo `json:"membership"`
|
||||
KolProfile *KolProfileBrief `json:"kol_profile"`
|
||||
ID int64 `json:"id"`
|
||||
Email string `json:"email"`
|
||||
Name string `json:"name"`
|
||||
AvatarURL *string `json:"avatar_url"`
|
||||
TenantID int64 `json:"tenant_id"`
|
||||
PrimaryWorkspaceID int64 `json:"primary_workspace_id"`
|
||||
TenantRole string `json:"tenant_role"`
|
||||
Permissions []string `json:"permissions"`
|
||||
Membership *MembershipInfo `json:"membership"`
|
||||
KolProfile *KolProfileBrief `json:"kol_profile"`
|
||||
}
|
||||
|
||||
type MembershipInfo struct {
|
||||
@@ -97,7 +98,7 @@ func (s *AuthService) Login(ctx context.Context, req LoginRequest) (*LoginRespon
|
||||
return nil, response.ErrForbidden(40301, "no_tenant", "user has no active tenant membership")
|
||||
}
|
||||
|
||||
pair, err := s.jwt.Issue(user.ID, membership.TenantID, membership.TenantRole)
|
||||
pair, err := s.jwt.Issue(user.ID, membership.TenantID, membership.PrimaryWorkspaceID, membership.TenantRole)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("issue token: %w", err)
|
||||
}
|
||||
@@ -121,15 +122,16 @@ func (s *AuthService) Login(ctx context.Context, req LoginRequest) (*LoginRespon
|
||||
RefreshToken: pair.RefreshToken,
|
||||
ExpiresAt: pair.ExpiresAt,
|
||||
User: UserInfo{
|
||||
ID: user.ID,
|
||||
Email: user.Email,
|
||||
Name: user.Name,
|
||||
AvatarURL: user.AvatarURL,
|
||||
TenantID: membership.TenantID,
|
||||
TenantRole: membership.TenantRole,
|
||||
Permissions: auth.PermissionsForRole(membership.TenantRole),
|
||||
Membership: memberInfo,
|
||||
KolProfile: kolProfile,
|
||||
ID: user.ID,
|
||||
Email: user.Email,
|
||||
Name: user.Name,
|
||||
AvatarURL: user.AvatarURL,
|
||||
TenantID: membership.TenantID,
|
||||
PrimaryWorkspaceID: membership.PrimaryWorkspaceID,
|
||||
TenantRole: membership.TenantRole,
|
||||
Permissions: auth.PermissionsForRole(membership.TenantRole),
|
||||
Membership: memberInfo,
|
||||
KolProfile: kolProfile,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -150,7 +152,7 @@ func (s *AuthService) Refresh(ctx context.Context, req RefreshRequest) (*Refresh
|
||||
return nil, response.ErrUnauthorized(40120, "invalid_refresh_token", "refresh token is invalid or expired")
|
||||
}
|
||||
|
||||
pair, err := s.jwt.Issue(claims.UserID, claims.TenantID, claims.Role)
|
||||
pair, err := s.jwt.Issue(claims.UserID, claims.TenantID, claims.PrimaryWorkspaceID, claims.Role)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("issue token: %w", err)
|
||||
}
|
||||
@@ -196,15 +198,16 @@ func (s *AuthService) Me(ctx context.Context, actor auth.Actor) (*UserInfo, erro
|
||||
}
|
||||
|
||||
return &UserInfo{
|
||||
ID: user.ID,
|
||||
Email: user.Email,
|
||||
Name: user.Name,
|
||||
AvatarURL: user.AvatarURL,
|
||||
TenantID: actor.TenantID,
|
||||
TenantRole: actor.Role,
|
||||
Permissions: auth.PermissionsForRole(actor.Role),
|
||||
Membership: memberInfo,
|
||||
KolProfile: kolProfile,
|
||||
ID: user.ID,
|
||||
Email: user.Email,
|
||||
Name: user.Name,
|
||||
AvatarURL: user.AvatarURL,
|
||||
TenantID: actor.TenantID,
|
||||
PrimaryWorkspaceID: actor.PrimaryWorkspaceID,
|
||||
TenantRole: actor.Role,
|
||||
Permissions: auth.PermissionsForRole(actor.Role),
|
||||
Membership: memberInfo,
|
||||
KolProfile: kolProfile,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user