migrate from github

This commit is contained in:
2026-07-15 22:42:31 +08:00
commit 3ad559f05e
262 changed files with 21637 additions and 0 deletions
@@ -0,0 +1,781 @@
package connectserver
import (
"context"
"net/http"
"connectrpc.com/connect"
"github.com/XingfenD/yoresee_doc/internal/repository"
"github.com/XingfenD/yoresee_doc/internal/transport/grpcserver"
pb "github.com/XingfenD/yoresee_doc/pkg/gen/yoresee_doc/v1"
)
func registerHandlers(mux *http.ServeMux, opts []connect.HandlerOption, repos *repository.Repositories) {
authSvc := grpcserver.NewAuthServiceServer()
docSvc := grpcserver.NewDocumentServiceServer()
kbSvc := grpcserver.NewKnowledgeBaseServiceServer()
sysSvc := grpcserver.NewSystemServiceServer()
settingSvc := grpcserver.NewSettingServiceServer()
memberSvc := grpcserver.NewMembershipServiceServer()
inviteSvc := grpcserver.NewInvitationServiceServer(repos.User)
notifySvc := grpcserver.NewNotificationServiceServer()
commentSvc := grpcserver.NewCommentServiceServer(repos.User, repos.Document)
mux.Handle(pb.AuthService_Login_FullMethodName, connect.NewUnaryHandler(
pb.AuthService_Login_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.AuthLoginRequest]) (*connect.Response[pb.AuthLoginResponse], error) {
resp, err := authSvc.Login(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.AuthService_Register_FullMethodName, connect.NewUnaryHandler(
pb.AuthService_Register_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.AuthRegisterRequest]) (*connect.Response[pb.AuthRegisterResponse], error) {
resp, err := authSvc.Register(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.AuthService_UpdateProfile_FullMethodName, connect.NewUnaryHandler(
pb.AuthService_UpdateProfile_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.UpdateProfileRequest]) (*connect.Response[pb.UpdateProfileResponse], error) {
resp, err := authSvc.UpdateProfile(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.AuthService_QuerySideBarDisplay_FullMethodName, connect.NewUnaryHandler(
pb.AuthService_QuerySideBarDisplay_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.QuerySideBarDisplayRequest]) (*connect.Response[pb.QuerySideBarDisplayResponse], error) {
resp, err := authSvc.QuerySideBarDisplay(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.AuthService_QueryTopNavDisplay_FullMethodName, connect.NewUnaryHandler(
pb.AuthService_QueryTopNavDisplay_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.QueryTopNavDisplayRequest]) (*connect.Response[pb.QueryTopNavDisplayResponse], error) {
resp, err := authSvc.QueryTopNavDisplay(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.DocumentService_ListDocuments_FullMethodName, connect.NewUnaryHandler(
pb.DocumentService_ListDocuments_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.ListDocumentsRequest]) (*connect.Response[pb.ListDocumentsResponse], error) {
resp, err := docSvc.ListDocuments(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.DocumentService_GetDocumentContent_FullMethodName, connect.NewUnaryHandler(
pb.DocumentService_GetDocumentContent_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.GetDocumentContentRequest]) (*connect.Response[pb.GetDocumentContentResponse], error) {
resp, err := docSvc.GetDocumentContent(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.DocumentService_GetDocumentSettings_FullMethodName, connect.NewUnaryHandler(
pb.DocumentService_GetDocumentSettings_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.GetDocumentSettingsRequest]) (*connect.Response[pb.GetDocumentSettingsResponse], error) {
resp, err := docSvc.GetDocumentSettings(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.DocumentService_RecordRecentDocument_FullMethodName, connect.NewUnaryHandler(
pb.DocumentService_RecordRecentDocument_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.RecordRecentDocumentRequest]) (*connect.Response[pb.RecordRecentDocumentResponse], error) {
resp, err := docSvc.RecordRecentDocument(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.DocumentService_ListRecentDocuments_FullMethodName, connect.NewUnaryHandler(
pb.DocumentService_ListRecentDocuments_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.ListRecentDocumentsRequest]) (*connect.Response[pb.ListRecentDocumentsResponse], error) {
resp, err := docSvc.ListRecentDocuments(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.DocumentService_GetDocumentYjsSnapshot_FullMethodName, connect.NewUnaryHandler(
pb.DocumentService_GetDocumentYjsSnapshot_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.GetDocumentYjsSnapshotRequest]) (*connect.Response[pb.GetDocumentYjsSnapshotResponse], error) {
resp, err := docSvc.GetDocumentYjsSnapshot(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.DocumentService_SaveDocumentYjsSnapshot_FullMethodName, connect.NewUnaryHandler(
pb.DocumentService_SaveDocumentYjsSnapshot_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.SaveDocumentYjsSnapshotRequest]) (*connect.Response[pb.SaveDocumentYjsSnapshotResponse], error) {
resp, err := docSvc.SaveDocumentYjsSnapshot(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.DocumentService_GetOwnDocuments_FullMethodName, connect.NewUnaryHandler(
pb.DocumentService_GetOwnDocuments_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.GetOwnDocumentsRequest]) (*connect.Response[pb.GetOwnDocumentsResponse], error) {
resp, err := docSvc.GetOwnDocuments(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.DocumentService_CreateDocument_FullMethodName, connect.NewUnaryHandler(
pb.DocumentService_CreateDocument_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.CreateDocumentRequest]) (*connect.Response[pb.CreateDocumentResponse], error) {
resp, err := docSvc.CreateDocument(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.DocumentService_UpdateDocumentMeta_FullMethodName, connect.NewUnaryHandler(
pb.DocumentService_UpdateDocumentMeta_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.UpdateDocumentMetaRequest]) (*connect.Response[pb.UpdateDocumentMetaResponse], error) {
resp, err := docSvc.UpdateDocumentMeta(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.DocumentService_UpdateDocumentSettings_FullMethodName, connect.NewUnaryHandler(
pb.DocumentService_UpdateDocumentSettings_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.UpdateDocumentSettingsRequest]) (*connect.Response[pb.UpdateDocumentSettingsResponse], error) {
resp, err := docSvc.UpdateDocumentSettings(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.DocumentService_UploadDocumentAttachment_FullMethodName, connect.NewUnaryHandler(
pb.DocumentService_UploadDocumentAttachment_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.UploadDocumentAttachmentRequest]) (*connect.Response[pb.UploadDocumentAttachmentResponse], error) {
resp, err := docSvc.UploadDocumentAttachment(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.DocumentService_ListDocumentAttachments_FullMethodName, connect.NewUnaryHandler(
pb.DocumentService_ListDocumentAttachments_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.ListDocumentAttachmentsRequest]) (*connect.Response[pb.ListDocumentAttachmentsResponse], error) {
resp, err := docSvc.ListDocumentAttachments(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.DocumentService_DeleteDocumentAttachment_FullMethodName, connect.NewUnaryHandler(
pb.DocumentService_DeleteDocumentAttachment_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.DeleteDocumentAttachmentRequest]) (*connect.Response[pb.DeleteDocumentAttachmentResponse], error) {
resp, err := docSvc.DeleteDocumentAttachment(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.DocumentService_ListDocumentVersions_FullMethodName, connect.NewUnaryHandler(
pb.DocumentService_ListDocumentVersions_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.ListDocumentVersionsRequest]) (*connect.Response[pb.ListDocumentVersionsResponse], error) {
resp, err := docSvc.ListDocumentVersions(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.DocumentService_GetDocumentVersionContent_FullMethodName, connect.NewUnaryHandler(
pb.DocumentService_GetDocumentVersionContent_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.GetDocumentVersionContentRequest]) (*connect.Response[pb.GetDocumentVersionContentResponse], error) {
resp, err := docSvc.GetDocumentVersionContent(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.KnowledgeBaseService_ListKnowledgeBases_FullMethodName, connect.NewUnaryHandler(
pb.KnowledgeBaseService_ListKnowledgeBases_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.ListKnowledgeBasesRequest]) (*connect.Response[pb.ListKnowledgeBasesResponse], error) {
resp, err := kbSvc.ListKnowledgeBases(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.KnowledgeBaseService_GetKnowledgeBase_FullMethodName, connect.NewUnaryHandler(
pb.KnowledgeBaseService_GetKnowledgeBase_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.GetKnowledgeBaseRequest]) (*connect.Response[pb.GetKnowledgeBaseResponse], error) {
resp, err := kbSvc.GetKnowledgeBase(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.KnowledgeBaseService_CreateKnowledgeBase_FullMethodName, connect.NewUnaryHandler(
pb.KnowledgeBaseService_CreateKnowledgeBase_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.CreateKnowledgeBaseRequest]) (*connect.Response[pb.CreateKnowledgeBaseResponse], error) {
resp, err := kbSvc.CreateKnowledgeBase(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.KnowledgeBaseService_ListRecentKnowledgeBases_FullMethodName, connect.NewUnaryHandler(
pb.KnowledgeBaseService_ListRecentKnowledgeBases_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.ListRecentKnowledgeBasesRequest]) (*connect.Response[pb.ListRecentKnowledgeBasesResponse], error) {
resp, err := kbSvc.ListRecentKnowledgeBases(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.SystemService_Health_FullMethodName, connect.NewUnaryHandler(
pb.SystemService_Health_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.HealthRequest]) (*connect.Response[pb.HealthResponse], error) {
resp, err := sysSvc.Health(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.SystemService_SystemInfo_FullMethodName, connect.NewUnaryHandler(
pb.SystemService_SystemInfo_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.SystemInfoRequest]) (*connect.Response[pb.SystemInfoResponse], error) {
resp, err := sysSvc.SystemInfo(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.SettingService_GetSettings_FullMethodName, connect.NewUnaryHandler(
pb.SettingService_GetSettings_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.GetSettingsRequest]) (*connect.Response[pb.GetSettingsResponse], error) {
resp, err := settingSvc.GetSettings(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.SettingService_UpdateSettings_FullMethodName, connect.NewUnaryHandler(
pb.SettingService_UpdateSettings_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.UpdateSettingsRequest]) (*connect.Response[pb.UpdateSettingsResponse], error) {
resp, err := settingSvc.UpdateSettings(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.MembershipService_ListUserGroups_FullMethodName, connect.NewUnaryHandler(
pb.MembershipService_ListUserGroups_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.ListUserGroupsRequest]) (*connect.Response[pb.ListUserGroupsResponse], error) {
resp, err := memberSvc.ListUserGroups(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.MembershipService_GetUserGroup_FullMethodName, connect.NewUnaryHandler(
pb.MembershipService_GetUserGroup_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.GetUserGroupRequest]) (*connect.Response[pb.GetUserGroupResponse], error) {
resp, err := memberSvc.GetUserGroup(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.MembershipService_CreateUserGroup_FullMethodName, connect.NewUnaryHandler(
pb.MembershipService_CreateUserGroup_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.CreateUserGroupRequest]) (*connect.Response[pb.CreateUserGroupResponse], error) {
resp, err := memberSvc.CreateUserGroup(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.MembershipService_UpdateUserGroup_FullMethodName, connect.NewUnaryHandler(
pb.MembershipService_UpdateUserGroup_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.UpdateUserGroupRequest]) (*connect.Response[pb.UpdateUserGroupResponse], error) {
resp, err := memberSvc.UpdateUserGroup(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.MembershipService_DeleteUserGroup_FullMethodName, connect.NewUnaryHandler(
pb.MembershipService_DeleteUserGroup_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.DeleteUserGroupRequest]) (*connect.Response[pb.DeleteUserGroupResponse], error) {
resp, err := memberSvc.DeleteUserGroup(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.MembershipService_ListUsers_FullMethodName, connect.NewUnaryHandler(
pb.MembershipService_ListUsers_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.ListUsersRequest]) (*connect.Response[pb.ListUsersResponse], error) {
resp, err := memberSvc.ListUsers(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.MembershipService_UpdateUser_FullMethodName, connect.NewUnaryHandler(
pb.MembershipService_UpdateUser_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.UpdateUserRequest]) (*connect.Response[pb.UpdateUserResponse], error) {
resp, err := memberSvc.UpdateUser(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.MembershipService_ListUserGroupMembers_FullMethodName, connect.NewUnaryHandler(
pb.MembershipService_ListUserGroupMembers_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.ListUserGroupMembersRequest]) (*connect.Response[pb.ListUserGroupMembersResponse], error) {
resp, err := memberSvc.ListUserGroupMembers(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.MembershipService_ListOrgNodes_FullMethodName, connect.NewUnaryHandler(
pb.MembershipService_ListOrgNodes_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.ListOrgNodesRequest]) (*connect.Response[pb.ListOrgNodesResponse], error) {
resp, err := memberSvc.ListOrgNodes(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.MembershipService_GetOrgNode_FullMethodName, connect.NewUnaryHandler(
pb.MembershipService_GetOrgNode_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.GetOrgNodeRequest]) (*connect.Response[pb.GetOrgNodeResponse], error) {
resp, err := memberSvc.GetOrgNode(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.MembershipService_CreateOrgNode_FullMethodName, connect.NewUnaryHandler(
pb.MembershipService_CreateOrgNode_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.CreateOrgNodeRequest]) (*connect.Response[pb.CreateOrgNodeResponse], error) {
resp, err := memberSvc.CreateOrgNode(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.MembershipService_UpdateOrgNode_FullMethodName, connect.NewUnaryHandler(
pb.MembershipService_UpdateOrgNode_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.UpdateOrgNodeRequest]) (*connect.Response[pb.UpdateOrgNodeResponse], error) {
resp, err := memberSvc.UpdateOrgNode(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.MembershipService_DeleteOrgNode_FullMethodName, connect.NewUnaryHandler(
pb.MembershipService_DeleteOrgNode_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.DeleteOrgNodeRequest]) (*connect.Response[pb.DeleteOrgNodeResponse], error) {
resp, err := memberSvc.DeleteOrgNode(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.MembershipService_MoveOrgNode_FullMethodName, connect.NewUnaryHandler(
pb.MembershipService_MoveOrgNode_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.MoveOrgNodeRequest]) (*connect.Response[pb.MoveOrgNodeResponse], error) {
resp, err := memberSvc.MoveOrgNode(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.MembershipService_ListOrgNodeMembers_FullMethodName, connect.NewUnaryHandler(
pb.MembershipService_ListOrgNodeMembers_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.ListOrgNodeMembersRequest]) (*connect.Response[pb.ListOrgNodeMembersResponse], error) {
resp, err := memberSvc.ListOrgNodeMembers(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.InvitationService_CreateInvitation_FullMethodName, connect.NewUnaryHandler(
pb.InvitationService_CreateInvitation_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.CreateInvitationRequest]) (*connect.Response[pb.CreateInvitationResponse], error) {
resp, err := inviteSvc.CreateInvitation(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.InvitationService_ListInvitations_FullMethodName, connect.NewUnaryHandler(
pb.InvitationService_ListInvitations_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.ListInvitationsRequest]) (*connect.Response[pb.ListInvitationsResponse], error) {
resp, err := inviteSvc.ListInvitations(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.InvitationService_UpdateInvitation_FullMethodName, connect.NewUnaryHandler(
pb.InvitationService_UpdateInvitation_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.UpdateInvitationRequest]) (*connect.Response[pb.UpdateInvitationResponse], error) {
resp, err := inviteSvc.UpdateInvitation(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.InvitationService_DeleteInvitation_FullMethodName, connect.NewUnaryHandler(
pb.InvitationService_DeleteInvitation_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.DeleteInvitationRequest]) (*connect.Response[pb.DeleteInvitationResponse], error) {
resp, err := inviteSvc.DeleteInvitation(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.InvitationService_ListInvitationRecords_FullMethodName, connect.NewUnaryHandler(
pb.InvitationService_ListInvitationRecords_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.ListInvitationRecordsRequest]) (*connect.Response[pb.ListInvitationRecordsResponse], error) {
resp, err := inviteSvc.ListInvitationRecords(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.NotificationService_CreateNotification_FullMethodName, connect.NewUnaryHandler(
pb.NotificationService_CreateNotification_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.CreateNotificationRequest]) (*connect.Response[pb.CreateNotificationResponse], error) {
resp, err := notifySvc.CreateNotification(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.NotificationService_ListNotifications_FullMethodName, connect.NewUnaryHandler(
pb.NotificationService_ListNotifications_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.ListNotificationsRequest]) (*connect.Response[pb.ListNotificationsResponse], error) {
resp, err := notifySvc.ListNotifications(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.NotificationService_MarkNotificationsRead_FullMethodName, connect.NewUnaryHandler(
pb.NotificationService_MarkNotificationsRead_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.MarkNotificationsReadRequest]) (*connect.Response[pb.MarkNotificationsReadResponse], error) {
resp, err := notifySvc.MarkNotificationsRead(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.NotificationService_MarkAllNotificationsRead_FullMethodName, connect.NewUnaryHandler(
pb.NotificationService_MarkAllNotificationsRead_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.MarkAllNotificationsReadRequest]) (*connect.Response[pb.MarkAllNotificationsReadResponse], error) {
resp, err := notifySvc.MarkAllNotificationsRead(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.CommentService_CreateDocumentComment_FullMethodName, connect.NewUnaryHandler(
pb.CommentService_CreateDocumentComment_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.CreateDocumentCommentRequest]) (*connect.Response[pb.CreateDocumentCommentResponse], error) {
resp, err := commentSvc.CreateDocumentComment(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.CommentService_ListDocumentComments_FullMethodName, connect.NewUnaryHandler(
pb.CommentService_ListDocumentComments_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.ListDocumentCommentsRequest]) (*connect.Response[pb.ListDocumentCommentsResponse], error) {
resp, err := commentSvc.ListDocumentComments(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.CommentService_DeleteDocumentComment_FullMethodName, connect.NewUnaryHandler(
pb.CommentService_DeleteDocumentComment_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.DeleteDocumentCommentRequest]) (*connect.Response[pb.DeleteDocumentCommentResponse], error) {
resp, err := commentSvc.DeleteDocumentComment(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.CommentService_UpdateDocumentComment_FullMethodName, connect.NewUnaryHandler(
pb.CommentService_UpdateDocumentComment_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.UpdateDocumentCommentRequest]) (*connect.Response[pb.UpdateDocumentCommentResponse], error) {
resp, err := commentSvc.UpdateDocumentComment(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.DocumentService_UpdateDocument_FullMethodName, connect.NewUnaryHandler(
pb.DocumentService_UpdateDocument_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.UpdateDocumentRequest]) (*connect.Response[pb.UpdateDocumentResponse], error) {
resp, err := docSvc.UpdateDocument(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.DocumentService_CreateTemplate_FullMethodName, connect.NewUnaryHandler(
pb.DocumentService_CreateTemplate_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.CreateTemplateRequest]) (*connect.Response[pb.CreateTemplateResponse], error) {
resp, err := docSvc.CreateTemplate(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.DocumentService_GetTemplate_FullMethodName, connect.NewUnaryHandler(
pb.DocumentService_GetTemplate_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.GetTemplateRequest]) (*connect.Response[pb.GetTemplateResponse], error) {
resp, err := docSvc.GetTemplate(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.DocumentService_ListTemplates_FullMethodName, connect.NewUnaryHandler(
pb.DocumentService_ListTemplates_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.ListTemplatesRequest]) (*connect.Response[pb.ListTemplatesResponse], error) {
resp, err := docSvc.ListTemplates(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.DocumentService_ListRecentTemplates_FullMethodName, connect.NewUnaryHandler(
pb.DocumentService_ListRecentTemplates_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.ListRecentTemplatesRequest]) (*connect.Response[pb.ListRecentTemplatesResponse], error) {
resp, err := docSvc.ListRecentTemplates(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.DocumentService_UpdateTemplateSettings_FullMethodName, connect.NewUnaryHandler(
pb.DocumentService_UpdateTemplateSettings_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.UpdateTemplateSettingsRequest]) (*connect.Response[pb.UpdateTemplateSettingsResponse], error) {
resp, err := docSvc.UpdateTemplateSettings(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
mux.Handle(pb.DocumentService_DeleteDocument_FullMethodName, connect.NewUnaryHandler(
pb.DocumentService_DeleteDocument_FullMethodName,
func(ctx context.Context, req *connect.Request[pb.DeleteDocumentRequest]) (*connect.Response[pb.DeleteDocumentResponse], error) {
resp, err := docSvc.DeleteDocument(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(resp), nil
},
opts...,
))
}
@@ -0,0 +1,39 @@
package connectserver
import (
"context"
"connectrpc.com/connect"
"github.com/XingfenD/yoresee_doc/internal/config"
"github.com/XingfenD/yoresee_doc/internal/middleware"
)
func UnaryAuthInterceptor(allowUnauth map[string]struct{}) connect.UnaryInterceptorFunc {
return connect.UnaryInterceptorFunc(func(next connect.UnaryFunc) connect.UnaryFunc {
return func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) {
if _, ok := allowUnauth[req.Spec().Procedure]; ok {
return next(ctx, req)
}
internalKey := req.Header().Get("x-internal-key")
if internalKey != "" && config.GlobalConfig.Backend.InternalRPCKey != "" && internalKey == config.GlobalConfig.Backend.InternalRPCKey {
return next(ctx, req)
}
authHeader := req.Header().Get("Authorization")
if authHeader == "" {
authHeader = req.Header().Get("authorization")
}
claims, err := middleware.JWTAuth.ValidateAuthorizationHeader(authHeader)
if err != nil {
return nil, connect.NewError(connect.CodeUnauthenticated, err)
}
ctx = context.WithValue(ctx, "user_external_id", claims.ExternalID)
ctx = context.WithValue(ctx, "username", claims.Username)
return next(ctx, req)
}
})
}
+72
View File
@@ -0,0 +1,72 @@
package connectserver
import (
"context"
"encoding/json"
"fmt"
"net/http"
"time"
)
type probeResponse struct {
Status string `json:"status"`
Detail string `json:"detail,omitempty"`
}
func registerProbeRoutes(mux *http.ServeMux) {
mux.HandleFunc("/health", handleHealthProbe)
mux.HandleFunc("/readyz", handleReadinessProbe)
mux.HandleFunc("/livez", handleLivenessProbe)
}
func handleHealthProbe(w http.ResponseWriter, _ *http.Request) {
writeProbeResponse(w, http.StatusOK, "ok", "")
}
func handleLivenessProbe(w http.ResponseWriter, _ *http.Request) {
writeProbeResponse(w, http.StatusOK, "ok", "")
}
func handleReadinessProbe(w http.ResponseWriter, _ *http.Request) {
if err := readinessErr(); err != nil {
writeProbeResponse(w, http.StatusServiceUnavailable, "not_ready", err.Error())
return
}
writeProbeResponse(w, http.StatusOK, "ok", "")
}
func readinessErr() error {
if draining.Load() {
return fmt.Errorf("server is draining")
}
if readyDB == nil {
return fmt.Errorf("database is not initialized")
}
if readyKVS == nil {
return fmt.Errorf("redis is not initialized")
}
ctx, cancel := context.WithTimeout(context.Background(), 1500*time.Millisecond)
defer cancel()
sqlDB, err := readyDB.DB()
if err != nil {
return fmt.Errorf("database handle unavailable: %w", err)
}
if err := sqlDB.PingContext(ctx); err != nil {
return fmt.Errorf("database ping failed: %w", err)
}
if err := readyKVS.Ping(ctx).Err(); err != nil {
return fmt.Errorf("redis ping failed: %w", err)
}
return nil
}
func writeProbeResponse(w http.ResponseWriter, statusCode int, status, detail string) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(statusCode)
_ = json.NewEncoder(w).Encode(probeResponse{
Status: status,
Detail: detail,
})
}
@@ -0,0 +1,91 @@
package connectserver
import (
"errors"
"fmt"
"net/http"
"sync/atomic"
"connectrpc.com/connect"
"github.com/XingfenD/yoresee_doc/internal/repository"
"github.com/redis/go-redis/v9"
pb "github.com/XingfenD/yoresee_doc/pkg/gen/yoresee_doc/v1"
"github.com/sirupsen/logrus"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
"gorm.io/gorm"
)
var (
draining atomic.Bool
readyDB *gorm.DB
readyKVS *redis.Client
)
func SetDraining(v bool) {
draining.Store(v)
}
func Start(grpcPort, grpcWebPort int, db *gorm.DB, kvs *redis.Client, repos *repository.Repositories) (*http.Server, *http.Server, error) {
readyDB = db
readyKVS = kvs
allowUnauth := map[string]struct{}{
pb.AuthService_Login_FullMethodName: {},
pb.AuthService_Register_FullMethodName: {},
pb.SystemService_Health_FullMethodName: {},
pb.SystemService_SystemInfo_FullMethodName: {},
}
interceptor := UnaryAuthInterceptor(allowUnauth)
opts := []connect.HandlerOption{
connect.WithInterceptors(interceptor),
}
mux := http.NewServeMux()
registerHandlers(mux, opts, repos)
registerProbeRoutes(mux)
handler := withCORS(mux)
h2cHandler := h2c.NewHandler(handler, &http2.Server{})
grpcAddr := fmt.Sprintf(":%d", grpcPort)
grpcServer := &http.Server{Addr: grpcAddr, Handler: h2cHandler}
go func() {
if err := grpcServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
logrus.Errorf("grpc server exited unexpectedly: %v", err)
}
}()
grpcWebAddr := fmt.Sprintf(":%d", grpcWebPort)
grpcWebServer := &http.Server{Addr: grpcWebAddr, Handler: h2cHandler}
go func() {
if err := grpcWebServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
logrus.Errorf("grpc-web server exited unexpectedly: %v", err)
}
}()
return grpcServer, grpcWebServer, nil
}
func withCORS(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
setConnectCorsHeaders(w, r)
if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusOK)
return
}
next.ServeHTTP(w, r)
})
}
func setConnectCorsHeaders(w http.ResponseWriter, r *http.Request) {
origin := r.Header.Get("Origin")
if origin == "" {
origin = "*"
}
w.Header().Set("Access-Control-Allow-Origin", origin)
w.Header().Set("Access-Control-Allow-Credentials", "true")
w.Header().Set("Access-Control-Allow-Headers", "Accept,Accept-Language,Content-Type,Content-Length,X-Grpc-Web,X-User-Agent,Grpc-Timeout,Authorization,Connect-Protocol-Version")
w.Header().Set("Access-Control-Allow-Methods", "POST,GET,OPTIONS")
w.Header().Set("Access-Control-Expose-Headers", "Grpc-Status,Grpc-Message,Grpc-Status-Details-Bin,Connect-Protocol-Version")
}
@@ -0,0 +1,133 @@
package grpcserver
import (
"context"
"github.com/XingfenD/yoresee_doc/internal/dto"
"github.com/XingfenD/yoresee_doc/internal/service/auth_service"
"github.com/XingfenD/yoresee_doc/internal/status"
pb "github.com/XingfenD/yoresee_doc/pkg/gen/yoresee_doc/v1"
)
type AuthServiceServer struct {
pb.UnimplementedAuthServiceServer
}
func NewAuthServiceServer() *AuthServiceServer {
return &AuthServiceServer{}
}
func (s *AuthServiceServer) Login(ctx context.Context, req *pb.AuthLoginRequest) (*pb.AuthLoginResponse, error) {
if req == nil || req.Email == "" || req.Password == "" {
return &pb.AuthLoginResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
token, user, err := auth_service.AuthSvc.Login(req.Email, req.Password)
if err != nil {
return &pb.AuthLoginResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.AuthLoginResponse{
Base: baseResponseFromErr(nil),
Token: token,
User: toUserResponse(user),
}, nil
}
func (s *AuthServiceServer) Register(ctx context.Context, req *pb.AuthRegisterRequest) (*pb.AuthRegisterResponse, error) {
if req == nil || req.Username == "" || req.Password == "" || req.Email == "" {
return &pb.AuthRegisterResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
userCreate := &dto.UserCreate{
Username: req.Username,
Email: req.Email,
Password: req.Password,
}
if req.InvitationCode != nil {
userCreate.InvitationCode = req.InvitationCode
}
err := auth_service.AuthSvc.Register(ctx, userCreate)
if err != nil {
return &pb.AuthRegisterResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.AuthRegisterResponse{Base: baseResponseFromErr(nil)}, nil
}
func (s *AuthServiceServer) UpdateProfile(ctx context.Context, req *pb.UpdateProfileRequest) (*pb.UpdateProfileResponse, error) {
if req == nil {
return &pb.UpdateProfileResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return &pb.UpdateProfileResponse{Base: baseResponseFromStatus(status.StatusTokenInvalid)}, nil
}
serviceReq := &dto.UpdateProfileRequest{
Username: req.Username,
Email: req.Email,
Nickname: req.Nickname,
Password: req.Password,
AvatarFile: req.AvatarFile,
AvatarFilename: req.AvatarFilename,
AvatarContentType: req.AvatarContentType,
}
user, err := auth_service.AuthSvc.UpdateProfile(userExternalID, serviceReq)
if err != nil {
return &pb.UpdateProfileResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.UpdateProfileResponse{
Base: baseResponseFromErr(nil),
User: toUserResponse(user),
}, nil
}
func (s *AuthServiceServer) QuerySideBarDisplay(ctx context.Context, req *pb.QuerySideBarDisplayRequest) (*pb.QuerySideBarDisplayResponse, error) {
if req == nil || req.Scene == "" {
return &pb.QuerySideBarDisplayResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return &pb.QuerySideBarDisplayResponse{Base: baseResponseFromStatus(status.StatusTokenInvalid)}, nil
}
isAdmin, err := auth_service.AuthSvc.IsAdmin(userExternalID)
if err != nil {
return &pb.QuerySideBarDisplayResponse{Base: baseResponseFromErr(err)}, nil
}
displayTabs, err := auth_service.AuthSvc.QuerySideBarDisplay(req.Scene, isAdmin)
if err != nil {
return &pb.QuerySideBarDisplayResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.QuerySideBarDisplayResponse{
Base: baseResponseFromErr(nil),
DisplayTabs: displayTabs,
}, nil
}
func (s *AuthServiceServer) QueryTopNavDisplay(ctx context.Context, req *pb.QueryTopNavDisplayRequest) (*pb.QueryTopNavDisplayResponse, error) {
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return &pb.QueryTopNavDisplayResponse{Base: baseResponseFromStatus(status.StatusTokenInvalid)}, nil
}
isAdmin, err := auth_service.AuthSvc.IsAdmin(userExternalID)
if err != nil {
return &pb.QueryTopNavDisplayResponse{Base: baseResponseFromErr(err)}, nil
}
menus, err := auth_service.AuthSvc.QueryTopNavDisplay(isAdmin)
if err != nil {
return &pb.QueryTopNavDisplayResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.QueryTopNavDisplayResponse{
Base: baseResponseFromErr(nil),
DisplayMenus: menus,
}, nil
}
@@ -0,0 +1,184 @@
package grpcserver
import (
"context"
"strings"
"github.com/XingfenD/yoresee_doc/internal/dto"
"github.com/XingfenD/yoresee_doc/internal/media"
"github.com/XingfenD/yoresee_doc/internal/model"
"github.com/XingfenD/yoresee_doc/internal/repository/document_repo"
"github.com/XingfenD/yoresee_doc/internal/repository/user_repo"
"github.com/XingfenD/yoresee_doc/internal/service/auth_service"
"github.com/XingfenD/yoresee_doc/internal/service/comment_service"
"github.com/XingfenD/yoresee_doc/internal/status"
pb "github.com/XingfenD/yoresee_doc/pkg/gen/yoresee_doc/v1"
)
type CommentServiceServer struct {
pb.UnimplementedCommentServiceServer
userRepo *user_repo.UserRepository
docRepo *document_repo.DocumentRepository
}
func NewCommentServiceServer(userRepo *user_repo.UserRepository, docRepo *document_repo.DocumentRepository) *CommentServiceServer {
return &CommentServiceServer{
userRepo: userRepo,
docRepo: docRepo,
}
}
func (s *CommentServiceServer) CreateDocumentComment(ctx context.Context, req *pb.CreateDocumentCommentRequest) (*pb.CreateDocumentCommentResponse, error) {
if req == nil {
return &pb.CreateDocumentCommentResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || strings.TrimSpace(userExternalID) == "" {
return &pb.CreateDocumentCommentResponse{Base: baseResponseFromStatus(status.StatusTokenInvalid)}, nil
}
var parentExternalID *string
if req.ParentExternalId != nil && strings.TrimSpace(req.GetParentExternalId()) != "" {
parentExternalID = req.ParentExternalId
}
var anchorID *string
if req.AnchorId != nil && strings.TrimSpace(req.GetAnchorId()) != "" {
anchorID = req.AnchorId
}
var quote *string
if req.Quote != nil && strings.TrimSpace(req.GetQuote()) != "" {
quote = req.Quote
}
comment, err := comment_service.CommentSvc.CreateComment(&dto.CreateCommentRequest{
DocumentExternalID: req.DocumentExternalId,
Content: req.Content,
ParentExternalID: parentExternalID,
AnchorID: anchorID,
Quote: quote,
CreatorExternalID: userExternalID,
MentionedUserExternalIDs: req.MentionedUserExternalIds,
})
if err != nil {
return &pb.CreateDocumentCommentResponse{Base: baseResponseFromErr(err)}, nil
}
creator, _ := s.userRepo.GetByExternalID(userExternalID).Exec()
resp := toCommentResponse(comment, req.DocumentExternalId, req.GetParentExternalId(), creator)
return &pb.CreateDocumentCommentResponse{Base: baseResponseFromErr(nil), Comment: resp}, nil
}
func (s *CommentServiceServer) ListDocumentComments(ctx context.Context, req *pb.ListDocumentCommentsRequest) (*pb.ListDocumentCommentsResponse, error) {
if req == nil {
return &pb.ListDocumentCommentsResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
comments, total, err := comment_service.CommentSvc.ListComments(&dto.ListCommentsRequest{
DocumentExternalID: req.DocumentExternalId,
Page: int(req.Page),
PageSize: int(req.PageSize),
})
if err != nil {
return &pb.ListDocumentCommentsResponse{Base: baseResponseFromErr(err)}, nil
}
creatorIDs := make([]int64, 0, len(comments))
for _, item := range comments {
creatorIDs = append(creatorIDs, item.CreatorID)
}
creatorMap, _ := s.userRepo.MGetUserByID(creatorIDs).Exec()
parentMap := make(map[int64]string, len(comments))
for _, item := range comments {
parentMap[item.ID] = item.ExternalID
}
respItems := make([]*pb.DocumentComment, 0, len(comments))
for _, item := range comments {
parentExternalID := ""
if item.ParentID != 0 {
parentExternalID = parentMap[item.ParentID]
}
respItems = append(respItems, toCommentResponse(&item, req.DocumentExternalId, parentExternalID, creatorMap[item.CreatorID]))
}
return &pb.ListDocumentCommentsResponse{
Base: baseResponseFromErr(nil),
Comments: respItems,
Total: total,
}, nil
}
func (s *CommentServiceServer) DeleteDocumentComment(ctx context.Context, req *pb.DeleteDocumentCommentRequest) (*pb.DeleteDocumentCommentResponse, error) {
if req == nil || strings.TrimSpace(req.ExternalId) == "" {
return &pb.DeleteDocumentCommentResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || strings.TrimSpace(userExternalID) == "" {
return &pb.DeleteDocumentCommentResponse{Base: baseResponseFromStatus(status.StatusTokenInvalid)}, nil
}
isAdmin, _ := auth_service.AuthSvc.IsAdmin(userExternalID)
if err := comment_service.CommentSvc.DeleteComment(&dto.DeleteCommentRequest{
ExternalID: req.ExternalId,
OperatorExternalID: userExternalID,
}, isAdmin); err != nil {
return &pb.DeleteDocumentCommentResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.DeleteDocumentCommentResponse{Base: baseResponseFromErr(nil)}, nil
}
func (s *CommentServiceServer) UpdateDocumentComment(ctx context.Context, req *pb.UpdateDocumentCommentRequest) (*pb.UpdateDocumentCommentResponse, error) {
if req == nil || strings.TrimSpace(req.ExternalId) == "" || strings.TrimSpace(req.Content) == "" {
return &pb.UpdateDocumentCommentResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || strings.TrimSpace(userExternalID) == "" {
return &pb.UpdateDocumentCommentResponse{Base: baseResponseFromStatus(status.StatusTokenInvalid)}, nil
}
isAdmin, _ := auth_service.AuthSvc.IsAdmin(userExternalID)
comment, err := comment_service.CommentSvc.UpdateComment(&dto.UpdateCommentRequest{
ExternalID: req.ExternalId,
Content: req.Content,
OperatorExternalID: userExternalID,
}, isAdmin)
if err != nil {
return &pb.UpdateDocumentCommentResponse{Base: baseResponseFromErr(err)}, nil
}
docExternalID := ""
if comment != nil && comment.DocumentID != 0 {
if docs, err := s.docRepo.MGetByIDs([]int64{comment.DocumentID}); err == nil && len(docs) > 0 {
docExternalID = docs[0].ExternalID
}
}
creator, _ := s.userRepo.GetByExternalID(userExternalID).Exec()
resp := toCommentResponse(comment, docExternalID, "", creator)
return &pb.UpdateDocumentCommentResponse{Base: baseResponseFromErr(nil), Comment: resp}, nil
}
func toCommentResponse(comment *model.DocumentComment, docExternalID, parentExternalID string, creator *model.User) *pb.DocumentComment {
if comment == nil {
return nil
}
resp := &pb.DocumentComment{
ExternalId: comment.ExternalID,
DocumentExternalId: docExternalID,
ParentExternalId: parentExternalID,
Content: comment.Content,
AnchorId: comment.AnchorID,
Quote: "",
CreatorUserExternalId: "",
CreatorName: "",
CreatorAvatar: "",
CreatedAt: timeToString(comment.CreatedAt),
}
if creator != nil {
resp.CreatorUserExternalId = creator.ExternalID
if creator.Nickname != "" {
resp.CreatorName = creator.Nickname
} else {
resp.CreatorName = creator.Username
}
resp.CreatorAvatar = media.BuildAvatarURL(creator.ExternalID, creator.AvatarObjectKey, creator.AvatarVersion)
}
return resp
}
+204
View File
@@ -0,0 +1,204 @@
package grpcserver
import (
"time"
"github.com/XingfenD/yoresee_doc/internal/dto"
"github.com/XingfenD/yoresee_doc/internal/mapper/doc_type_mapper"
"github.com/XingfenD/yoresee_doc/internal/media"
"github.com/XingfenD/yoresee_doc/internal/status"
"github.com/XingfenD/yoresee_doc/internal/utils"
pb "github.com/XingfenD/yoresee_doc/pkg/gen/yoresee_doc/v1"
)
func baseResponseFromErr(err error) *pb.BaseResponse {
if err == nil {
return baseResponseFromStatus(status.StatusSuccess)
}
return baseResponseFromStatus(err)
}
func baseResponseFromStatus(err error) *pb.BaseResponse {
st, ok := err.(*status.Status)
if !ok {
st = status.StatusServiceInternalError.(*status.Status)
}
return &pb.BaseResponse{
Code: int32(st.Code),
Message: st.Message,
}
}
func timeToString(t time.Time) string {
if t.IsZero() {
return ""
}
return t.Format(time.RFC3339)
}
func toDocumentResponse(doc *dto.DocumentMetaResponse) *pb.DocumentResponse {
if doc == nil {
return nil
}
resp := &pb.DocumentResponse{
ExternalId: doc.ExternalID,
Title: doc.Title,
Type: doc_type_mapper.ToProtoType(doc.Type),
Summary: doc.Summary,
Tags: doc.Tags,
ViewCount: int32(doc.ViewCount),
EditCount: int32(doc.EditCount),
CreatedAt: timeToString(doc.CreatedAt),
UpdatedAt: timeToString(doc.UpdatedAt),
HasChildren: doc.HasChildren,
IsPublic: doc.IsPublic,
}
if len(doc.Children) > 0 {
resp.Children = make([]*pb.DocumentResponse, 0, len(doc.Children))
for _, child := range doc.Children {
resp.Children = append(resp.Children, toDocumentResponse(child))
}
}
return resp
}
func toKnowledgeBaseResponse(kb *dto.KnowledgeBaseResponse) *pb.KnowledgeBaseResponse {
if kb == nil {
return nil
}
return &pb.KnowledgeBaseResponse{
ExternalId: kb.ExternalID,
Name: kb.Name,
Description: kb.Description,
Cover: kb.Cover,
IsPublic: kb.IsPublic,
CreatedAt: timeToString(kb.CreatedAt),
UpdatedAt: timeToString(kb.UpdatedAt),
DeletedAt: timeToString(kb.DeletedAt),
CreatorUserExternalId: kb.CreatorUserExternalID,
CreatorName: kb.CreatorName,
DocumentsCount: kb.DocumentsCount,
}
}
func toUserResponse(user *dto.UserResponse) *pb.UserResponse {
if user == nil {
return nil
}
resp := &pb.UserResponse{
ExternalId: user.ExternalID,
Username: user.Username,
Email: user.Email,
Nickname: user.Nickname,
Avatar: media.BuildAvatarURL(user.ExternalID, user.AvatarObjectKey, user.AvatarVersion),
Status: int32(user.Status),
CreatedAt: timeToString(user.CreatedAt),
UpdatedAt: timeToString(user.UpdatedAt),
}
if user.InvitationCode != nil {
resp.InvitationCode = user.InvitationCode
}
return resp
}
func toTemplateResponse(tpl *dto.TemplateResponse) *pb.TemplateResponse {
if tpl == nil {
return nil
}
return &pb.TemplateResponse{
Id: tpl.ID,
Name: tpl.Name,
Description: tpl.Description,
Content: tpl.Content,
Type: doc_type_mapper.ToProtoType(tpl.Type),
Scope: tpl.Scope,
KnowledgeBaseExternalId: tpl.KnowledgeBaseExternalID,
Tags: tpl.Tags,
CreatedAt: timeToString(tpl.CreatedAt),
UpdatedAt: timeToString(tpl.UpdatedAt),
}
}
func toAttachmentResponse(attachment *dto.AttachmentResponse) *pb.AttachmentResponse {
if attachment == nil {
return nil
}
return &pb.AttachmentResponse{
ExternalId: attachment.ExternalID,
DocumentExternalId: attachment.DocumentExternalID,
Name: attachment.Name,
Size: attachment.Size,
MimeType: attachment.MimeType,
Path: attachment.Path,
Url: attachment.URL,
CreatedAt: timeToString(attachment.CreatedAt),
UpdatedAt: timeToString(attachment.UpdatedAt),
}
}
func toDocumentVersionResponse(item *dto.DocumentVersionResponse) *pb.DocumentVersionResponse {
if item == nil {
return nil
}
return &pb.DocumentVersionResponse{
Version: int32(item.Version),
Title: item.Title,
Content: item.Content,
ChangeSummary: item.ChangeSummary,
CreatedAt: timeToString(item.CreatedAt),
}
}
func toUserGroupResponse(group *dto.UserGroupResponse) *pb.UserGroupResponse {
if group == nil {
return nil
}
resp := &pb.UserGroupResponse{
ExternalId: group.ExternalID,
Name: group.Name,
Description: group.Description,
CreatorUserExternalId: group.CreatorUserExternalID,
MemberCount: int32(group.MemberCount),
}
return resp
}
func toInvitationResponse(inv *dto.InvitationResponse) *pb.InvitationResponse {
if inv == nil {
return nil
}
resp := &pb.InvitationResponse{
Code: inv.Code,
CreatedByExternalId: inv.CreatedByExternalID,
CreatedByName: inv.CreatedByName,
UsedCnt: inv.UsedCnt,
Disabled: inv.Disabled,
CreatedAt: timeToString(inv.CreatedAt),
}
if inv.MaxUsedCnt != nil {
resp.MaxUsedCnt = inv.MaxUsedCnt
}
if inv.ExpiresAt != nil {
resp.ExpiresAt = utils.Of(timeToString(*inv.ExpiresAt))
}
if inv.Note != nil {
resp.Note = inv.Note
}
return resp
}
func toInvitationRecordResponse(rec *dto.InvitationRecordResponse) *pb.InvitationRecordResponse {
if rec == nil {
return nil
}
resp := &pb.InvitationRecordResponse{
Code: rec.Code,
UsedBy: rec.UsedBy,
UsedAt: timeToString(rec.UsedAt),
Status: rec.Status,
}
if rec.UsedByExternalID != "" {
resp.UsedByExternalId = utils.Of(rec.UsedByExternalID)
}
return resp
}
@@ -0,0 +1,712 @@
package grpcserver
import (
"context"
"time"
"github.com/XingfenD/yoresee_doc/internal/dto"
doc_container_mapper "github.com/XingfenD/yoresee_doc/internal/mapper/doc_container_mapper"
"github.com/XingfenD/yoresee_doc/internal/mapper/doc_type_mapper"
"github.com/XingfenD/yoresee_doc/internal/mapper/template_container_mapper"
"github.com/XingfenD/yoresee_doc/internal/service/document_service"
"github.com/XingfenD/yoresee_doc/internal/status"
"github.com/XingfenD/yoresee_doc/internal/utils"
pb "github.com/XingfenD/yoresee_doc/pkg/gen/yoresee_doc/v1"
)
type DocumentServiceServer struct {
pb.UnimplementedDocumentServiceServer
}
func NewDocumentServiceServer() *DocumentServiceServer {
return &DocumentServiceServer{}
}
func (s *DocumentServiceServer) ListDocuments(ctx context.Context, req *pb.ListDocumentsRequest) (*pb.ListDocumentsResponse, error) {
if req == nil {
return &pb.ListDocumentsResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
filterArgs := &dto.DocumentsListFilterArgs{
TitleKeyword: req.TitleKeyword,
DocType: req.Type,
Tags: req.Tags,
}
if req.CreateTimeRange != nil {
filterArgs.CreateTimeRangeStart = req.CreateTimeRange.Start
filterArgs.CreateTimeRangeEnd = req.CreateTimeRange.End
}
if req.UpdateTimeRange != nil {
filterArgs.UpdateTimeRangeStart = req.UpdateTimeRange.Start
filterArgs.UpdateTimeRangeEnd = req.UpdateTimeRange.End
}
sortArgs := dto.SortArgs{Field: "created_at", Desc: true}
if req.OrderBy != nil {
sortArgs.Field = req.GetOrderBy()
}
if req.OrderDesc != nil {
sortArgs.Desc = req.GetOrderDesc()
}
serviceReq := &dto.ListDocumentsByExternalRequest{
ExternalArgs: &dto.DocumentsListExternalArgs{
UserExternalID: req.UserExternalId,
RootDocumentExternalID: req.RootDocumentExternalId,
KnowledgeExternalID: req.KnowledgeBaseExternalId,
},
FilterArgs: filterArgs,
SortArgs: sortArgs,
Pagination: dto.Pagination{
Page: int(req.Page),
PageSize: int(req.PageSize),
},
}
if req.Options != nil {
serviceReq.Options = &dto.RecursiveOptions{
IncludeChildren: req.Options.IncludeChildren,
Recursive: req.Options.Recursive,
Depth: utils.Of(int(req.Options.Depth)),
}
}
docs, total, err := document_service.DocumentSvc.ListDocumentsByExternal(ctx, serviceReq)
if err != nil {
return &pb.ListDocumentsResponse{Base: baseResponseFromErr(err)}, nil
}
respDocs := make([]*pb.DocumentResponse, 0, len(docs))
for _, doc := range docs {
respDocs = append(respDocs, toDocumentResponse(doc))
}
return &pb.ListDocumentsResponse{
Base: baseResponseFromErr(nil),
Documents: respDocs,
TotalCount: total,
}, nil
}
func (s *DocumentServiceServer) GetDocumentContent(ctx context.Context, req *pb.GetDocumentContentRequest) (*pb.GetDocumentContentResponse, error) {
if req == nil || req.DocumentExternalId == "" {
return &pb.GetDocumentContentResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
document, err := document_service.DocumentSvc.GetDocumentByExternalID(ctx, req.DocumentExternalId)
if err != nil {
return &pb.GetDocumentContentResponse{Base: baseResponseFromErr(status.StatusDocumentNotFound)}, nil
}
return &pb.GetDocumentContentResponse{
Base: baseResponseFromErr(nil),
Document: toDocumentResponse(&document.DocumentMetaResponse),
Content: document.Content,
}, nil
}
func (s *DocumentServiceServer) GetDocumentSettings(ctx context.Context, req *pb.GetDocumentSettingsRequest) (*pb.GetDocumentSettingsResponse, error) {
if req == nil || req.DocumentExternalId == "" {
return &pb.GetDocumentSettingsResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
settings, err := document_service.DocumentSvc.GetDocumentSettings(ctx, &dto.GetDocumentSettingsRequest{
ExternalID: req.DocumentExternalId,
})
if err != nil {
return &pb.GetDocumentSettingsResponse{Base: baseResponseFromErr(status.StatusDocumentNotFound)}, nil
}
return &pb.GetDocumentSettingsResponse{
Base: baseResponseFromErr(nil),
IsPublic: utils.Of(settings.IsPublic),
}, nil
}
func (s *DocumentServiceServer) RecordRecentDocument(ctx context.Context, req *pb.RecordRecentDocumentRequest) (*pb.RecordRecentDocumentResponse, error) {
if req == nil || req.DocumentExternalId == "" {
return &pb.RecordRecentDocumentResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return &pb.RecordRecentDocumentResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if err := document_service.DocumentSvc.RecordRecentDocument(&dto.RecordRecentDocumentRequest{
UserExternalID: userExternalID,
DocumentExternalID: req.DocumentExternalId,
}); err != nil {
return &pb.RecordRecentDocumentResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.RecordRecentDocumentResponse{Base: baseResponseFromErr(nil)}, nil
}
func (s *DocumentServiceServer) ListRecentDocuments(ctx context.Context, req *pb.ListRecentDocumentsRequest) (*pb.ListRecentDocumentsResponse, error) {
if req == nil {
return &pb.ListRecentDocumentsResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return &pb.ListRecentDocumentsResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
var startTime *time.Time
if req.StartTime != nil && *req.StartTime != "" {
t, err := time.Parse(time.RFC3339, *req.StartTime)
if err != nil {
return &pb.ListRecentDocumentsResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
startTime = &t
}
var endTime *time.Time
if req.EndTime != nil && *req.EndTime != "" {
t, err := time.Parse(time.RFC3339, *req.EndTime)
if err != nil {
return &pb.ListRecentDocumentsResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
endTime = &t
}
serviceReq := &dto.ListRecentDocumentsRequest{
UserExternalID: userExternalID,
StartTime: startTime,
EndTime: endTime,
Pagination: dto.Pagination{
Page: int(req.Page),
PageSize: int(req.PageSize),
},
}
documents, total, err := document_service.DocumentSvc.ListRecentDocuments(serviceReq)
if err != nil {
return &pb.ListRecentDocumentsResponse{Base: baseResponseFromErr(err)}, nil
}
respDocs := make([]*pb.DocumentResponse, 0, len(documents))
for _, doc := range documents {
respDocs = append(respDocs, toDocumentResponse(&doc.DocumentMetaResponse))
}
return &pb.ListRecentDocumentsResponse{
Base: baseResponseFromErr(nil),
Documents: respDocs,
Total: total,
}, nil
}
func (s *DocumentServiceServer) GetDocumentYjsSnapshot(ctx context.Context, req *pb.GetDocumentYjsSnapshotRequest) (*pb.GetDocumentYjsSnapshotResponse, error) {
if req == nil || req.DocumentExternalId == "" {
return &pb.GetDocumentYjsSnapshotResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
state, err := document_service.DocumentSvc.GetDocumentYjsSnapshot(ctx, req.DocumentExternalId)
if err != nil {
return &pb.GetDocumentYjsSnapshotResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.GetDocumentYjsSnapshotResponse{
Base: baseResponseFromErr(nil),
State: state,
}, nil
}
func (s *DocumentServiceServer) SaveDocumentYjsSnapshot(ctx context.Context, req *pb.SaveDocumentYjsSnapshotRequest) (*pb.SaveDocumentYjsSnapshotResponse, error) {
if req == nil || req.DocumentExternalId == "" || len(req.State) == 0 {
return &pb.SaveDocumentYjsSnapshotResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if err := document_service.DocumentSvc.SaveDocumentYjsSnapshot(ctx, req.DocumentExternalId, req.State); err != nil {
return &pb.SaveDocumentYjsSnapshotResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.SaveDocumentYjsSnapshotResponse{
Base: baseResponseFromErr(nil),
}, nil
}
func (s *DocumentServiceServer) GetOwnDocuments(ctx context.Context, req *pb.GetOwnDocumentsRequest) (*pb.GetOwnDocumentsResponse, error) {
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return &pb.GetOwnDocumentsResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
queryReq := &dto.ListDocumentsByExternalRequest{
ExternalArgs: &dto.DocumentsListExternalArgs{
UserExternalID: &userExternalID,
},
ListDocumentsBaseArgs: dto.ListDocumentsBaseArgs{
ListOwnDoc: true,
DirectoryOnly: req.DirectoryOnly,
},
Pagination: dto.Pagination{
Page: int(req.Page),
PageSize: int(req.PageSize),
},
Options: &dto.RecursiveOptions{
IncludeChildren: true,
Recursive: true,
},
}
docs, count, err := document_service.DocumentSvc.ListDocumentsByExternal(ctx, queryReq)
if err != nil {
return &pb.GetOwnDocumentsResponse{Base: baseResponseFromErr(err)}, nil
}
respDocs := make([]*pb.DocumentResponse, 0, len(docs))
for _, doc := range docs {
respDocs = append(respDocs, toDocumentResponse(doc))
}
return &pb.GetOwnDocumentsResponse{
Base: baseResponseFromErr(nil),
Documents: respDocs,
TotalCount: count,
}, nil
}
func (s *DocumentServiceServer) CreateDocument(ctx context.Context, req *pb.CreateDocumentRequest) (*pb.CreateDocumentResponse, error) {
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return &pb.CreateDocumentResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if req == nil || req.Title == "" {
return &pb.CreateDocumentResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
dtoReq := &dto.CreateDocumentRequest{
Title: req.Title,
Type: doc_type_mapper.FromProtoType(req.Type),
IsPublic: req.GetIsPublic(),
CreatorExternalID: utils.Of(userExternalID),
ParentExternalID: req.ParentExternalId,
}
if req.TemplateId != nil && *req.TemplateId > 0 {
dtoReq.TemplateID = req.TemplateId
}
containerType, ok := doc_container_mapper.FromProtoType(req.ContainerType)
if !ok {
return &pb.CreateDocumentResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
dtoReq.ContainerType = containerType
if containerType == dto.ContainerType_KnowledgeBase {
dtoReq.KnowledgeExternalID = req.KnowledgeBaseExternalId
}
resp, err := document_service.DocumentSvc.Create(ctx, dtoReq)
if err != nil {
return &pb.CreateDocumentResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.CreateDocumentResponse{
Base: baseResponseFromErr(nil),
ExternalId: resp.ExternalID,
}, nil
}
func (s *DocumentServiceServer) UpdateDocument(ctx context.Context, req *pb.UpdateDocumentRequest) (*pb.UpdateDocumentResponse, error) {
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return &pb.UpdateDocumentResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if err := validateUpdateDocumentRequest(req); err != nil {
return &pb.UpdateDocumentResponse{Base: baseResponseFromErr(err)}, nil
}
serviceReq := &dto.UpdateDocumentRequest{
ExternalID: req.ExternalId,
Title: req.Title,
ParentExternalID: req.ParentExternalId,
KnowledgeBaseExternalID: req.KnowledgeBaseExternalId,
Content: req.Content,
}
if req.MoveToContainer != nil {
moveToContainer, ok := doc_container_mapper.FromProtoType(*req.MoveToContainer)
if !ok {
return &pb.UpdateDocumentResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
serviceReq.MoveToContainer = utils.Of(moveToContainer)
}
if _, err := document_service.DocumentSvc.Update(ctx, serviceReq); err != nil {
return &pb.UpdateDocumentResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.UpdateDocumentResponse{
Base: baseResponseFromErr(nil),
}, nil
}
func (s *DocumentServiceServer) UpdateDocumentMeta(ctx context.Context, req *pb.UpdateDocumentMetaRequest) (*pb.UpdateDocumentMetaResponse, error) {
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return &pb.UpdateDocumentMetaResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if req == nil || req.ExternalId == "" {
return &pb.UpdateDocumentMetaResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if req.Title == nil && req.Summary == nil && req.Tags == nil {
return &pb.UpdateDocumentMetaResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
serviceReq := &dto.UpdateDocumentMetaRequest{
ExternalID: req.ExternalId,
Title: req.Title,
Summary: req.Summary,
}
if req.Tags != nil {
tags := req.Tags
serviceReq.Tags = &tags
}
if _, err := document_service.DocumentSvc.UpdateDocumentMeta(ctx, serviceReq); err != nil {
return &pb.UpdateDocumentMetaResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.UpdateDocumentMetaResponse{
Base: baseResponseFromErr(nil),
}, nil
}
func (s *DocumentServiceServer) UpdateDocumentSettings(ctx context.Context, req *pb.UpdateDocumentSettingsRequest) (*pb.UpdateDocumentSettingsResponse, error) {
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return &pb.UpdateDocumentSettingsResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if req == nil || req.ExternalId == "" || req.IsPublic == nil {
return &pb.UpdateDocumentSettingsResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
serviceReq := &dto.UpdateDocumentSettingsRequest{
ExternalID: req.ExternalId,
IsPublic: req.GetIsPublic(),
}
updated, err := document_service.DocumentSvc.UpdateDocumentSettings(ctx, serviceReq)
if err != nil {
return &pb.UpdateDocumentSettingsResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.UpdateDocumentSettingsResponse{
Base: baseResponseFromErr(nil),
IsPublic: utils.Of(updated.IsPublic),
}, nil
}
func (s *DocumentServiceServer) UploadDocumentAttachment(ctx context.Context, req *pb.UploadDocumentAttachmentRequest) (*pb.UploadDocumentAttachmentResponse, error) {
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return &pb.UploadDocumentAttachmentResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if req == nil || req.DocumentExternalId == "" || len(req.FileContent) == 0 {
return &pb.UploadDocumentAttachmentResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
attachment, err := document_service.DocumentSvc.UploadAttachment(
ctx,
userExternalID,
req.DocumentExternalId,
req.FileContent,
req.FileName,
req.GetContentType(),
)
if err != nil {
return &pb.UploadDocumentAttachmentResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.UploadDocumentAttachmentResponse{
Base: baseResponseFromErr(nil),
Attachment: toAttachmentResponse(attachment),
}, nil
}
func (s *DocumentServiceServer) ListDocumentAttachments(ctx context.Context, req *pb.ListDocumentAttachmentsRequest) (*pb.ListDocumentAttachmentsResponse, error) {
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return &pb.ListDocumentAttachmentsResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if req == nil || req.DocumentExternalId == "" {
return &pb.ListDocumentAttachmentsResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
attachments, err := document_service.DocumentSvc.ListAttachments(ctx, req.DocumentExternalId)
if err != nil {
return &pb.ListDocumentAttachmentsResponse{Base: baseResponseFromErr(err)}, nil
}
respAttachments := make([]*pb.AttachmentResponse, 0, len(attachments))
for _, attachment := range attachments {
respAttachments = append(respAttachments, toAttachmentResponse(attachment))
}
return &pb.ListDocumentAttachmentsResponse{
Base: baseResponseFromErr(nil),
Attachments: respAttachments,
}, nil
}
func (s *DocumentServiceServer) DeleteDocumentAttachment(ctx context.Context, req *pb.DeleteDocumentAttachmentRequest) (*pb.DeleteDocumentAttachmentResponse, error) {
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return &pb.DeleteDocumentAttachmentResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if req == nil || req.DocumentExternalId == "" || req.AttachmentExternalId == "" {
return &pb.DeleteDocumentAttachmentResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if err := document_service.DocumentSvc.DeleteAttachment(ctx, req.DocumentExternalId, req.AttachmentExternalId); err != nil {
return &pb.DeleteDocumentAttachmentResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.DeleteDocumentAttachmentResponse{
Base: baseResponseFromErr(nil),
}, nil
}
func (s *DocumentServiceServer) ListDocumentVersions(ctx context.Context, req *pb.ListDocumentVersionsRequest) (*pb.ListDocumentVersionsResponse, error) {
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return &pb.ListDocumentVersionsResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if req == nil || req.DocumentExternalId == "" {
return &pb.ListDocumentVersionsResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
items, total, err := document_service.DocumentSvc.ListDocumentVersions(ctx, req.DocumentExternalId, req.Page, req.PageSize)
if err != nil {
return &pb.ListDocumentVersionsResponse{Base: baseResponseFromErr(err)}, nil
}
respItems := make([]*pb.DocumentVersionResponse, 0, len(items))
for _, item := range items {
respItems = append(respItems, toDocumentVersionResponse(item))
}
return &pb.ListDocumentVersionsResponse{
Base: baseResponseFromErr(nil),
Versions: respItems,
Total: total,
}, nil
}
func (s *DocumentServiceServer) GetDocumentVersionContent(ctx context.Context, req *pb.GetDocumentVersionContentRequest) (*pb.GetDocumentVersionContentResponse, error) {
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return &pb.GetDocumentVersionContentResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if req == nil || req.DocumentExternalId == "" || req.Version <= 0 {
return &pb.GetDocumentVersionContentResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
item, err := document_service.DocumentSvc.GetDocumentVersionContent(ctx, req.DocumentExternalId, req.Version)
if err != nil {
return &pb.GetDocumentVersionContentResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.GetDocumentVersionContentResponse{
Base: baseResponseFromErr(nil),
Version: toDocumentVersionResponse(item),
}, nil
}
func (s *DocumentServiceServer) CreateTemplate(ctx context.Context, req *pb.CreateTemplateRequest) (*pb.CreateTemplateResponse, error) {
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return &pb.CreateTemplateResponse{Base: baseResponseFromStatus(status.StatusTokenInvalid)}, nil
}
if req == nil {
return &pb.CreateTemplateResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
serviceReq := &dto.CreateTemplateRequest{
UserExternalID: userExternalID,
TargetContainer: template_container_mapper.FromProtoType(req.TargetContainer),
TemplateContent: req.TemplateContent,
Type: doc_type_mapper.FromProtoType(req.Type),
}
if req.KnowledgeBaseId != "" {
serviceReq.KnowledgeBaseExternalID = utils.Of(req.KnowledgeBaseId)
}
if err := document_service.DocumentSvc.CreateTemplate(ctx, serviceReq); err != nil {
return &pb.CreateTemplateResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.CreateTemplateResponse{
Base: baseResponseFromErr(nil),
}, nil
}
func (s *DocumentServiceServer) GetTemplate(ctx context.Context, req *pb.GetTemplateRequest) (*pb.GetTemplateResponse, error) {
if req == nil || req.TemplateId <= 0 {
return &pb.GetTemplateResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return &pb.GetTemplateResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
resp, err := document_service.DocumentSvc.GetTemplateByID(req.TemplateId)
if err != nil {
return &pb.GetTemplateResponse{Base: baseResponseFromErr(err)}, nil
}
if req.RecordRecentLog {
_ = document_service.DocumentSvc.CreateRecentTemplate(&dto.CreateRecentTemplateRequest{
UserExternalID: userExternalID,
TemplateID: req.TemplateId,
AccessTime: time.Now(),
})
}
return &pb.GetTemplateResponse{
Base: baseResponseFromErr(nil),
Template: toTemplateResponse(resp),
}, nil
}
func (s *DocumentServiceServer) ListRecentTemplates(ctx context.Context, req *pb.ListRecentTemplatesRequest) (*pb.ListRecentTemplatesResponse, error) {
if req == nil {
return &pb.ListRecentTemplatesResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return &pb.ListRecentTemplatesResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
var startTime *time.Time
if req.StartTime != nil && *req.StartTime != "" {
t, err := time.Parse(time.RFC3339, *req.StartTime)
if err != nil {
return &pb.ListRecentTemplatesResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
startTime = &t
}
var endTime *time.Time
if req.EndTime != nil && *req.EndTime != "" {
t, err := time.Parse(time.RFC3339, *req.EndTime)
if err != nil {
return &pb.ListRecentTemplatesResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
endTime = &t
}
serviceReq := &dto.ListRecentTemplatesRequest{
UserExternalID: userExternalID,
StartTime: startTime,
EndTime: endTime,
Pagination: dto.Pagination{
Page: int(req.Page),
PageSize: int(req.PageSize),
},
}
templates, total, err := document_service.DocumentSvc.ListRecentTemplates(serviceReq)
if err != nil {
return &pb.ListRecentTemplatesResponse{Base: baseResponseFromErr(err)}, nil
}
respTemplates := make([]*pb.TemplateResponse, 0, len(templates))
for _, tpl := range templates {
respTemplates = append(respTemplates, toTemplateResponse(tpl))
}
return &pb.ListRecentTemplatesResponse{
Base: baseResponseFromErr(nil),
Templates: respTemplates,
Total: total,
}, nil
}
func (s *DocumentServiceServer) ListTemplates(ctx context.Context, req *pb.ListTemplatesRequest) (*pb.ListTemplatesResponse, error) {
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return &pb.ListTemplatesResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if req == nil {
return &pb.ListTemplatesResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
filterArgs := &dto.TemplateListFilterArgs{
NameKeyword: req.NameKeyword,
KnowledgeBaseID: req.KnowledgeBaseId,
}
if req.TargetContainer != nil {
container := template_container_mapper.FromProtoType(req.GetTargetContainer())
filterArgs.TargetContainer = utils.Of(container)
}
if req.Type != nil {
filterArgs.Type = utils.Of(doc_type_mapper.FromProtoType(req.GetType()))
}
sortArgs := dto.SortArgs{Field: "created_at", Desc: true}
if req.OrderBy != nil {
sortArgs.Field = req.GetOrderBy()
}
if req.OrderDesc != nil {
sortArgs.Desc = req.GetOrderDesc()
}
serviceReq := &dto.TemplateListByExternalRequest{
FilterArgs: filterArgs,
SortArgs: sortArgs,
Pagination: dto.Pagination{
Page: int(req.Page),
PageSize: int(req.PageSize),
},
}
if req.OnlyMine {
serviceReq.CreatorExternalID = userExternalID
}
if req.TargetContainer != nil && template_container_mapper.FromProtoType(req.GetTargetContainer()) == dto.TemplateContainerOwn {
serviceReq.CreatorExternalID = userExternalID
}
templates, total, err := document_service.DocumentSvc.ListTemplatesByExternal(serviceReq)
if err != nil {
return &pb.ListTemplatesResponse{Base: baseResponseFromErr(err)}, nil
}
respTemplates := make([]*pb.TemplateResponse, 0, len(templates))
for _, tpl := range templates {
respTemplates = append(respTemplates, toTemplateResponse(tpl))
}
return &pb.ListTemplatesResponse{
Base: baseResponseFromErr(nil),
Templates: respTemplates,
Total: total,
}, nil
}
func (s *DocumentServiceServer) UpdateTemplateSettings(ctx context.Context, req *pb.UpdateTemplateSettingsRequest) (*pb.UpdateTemplateSettingsResponse, error) {
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return &pb.UpdateTemplateSettingsResponse{Base: baseResponseFromStatus(status.StatusTokenInvalid)}, nil
}
if req == nil || req.TemplateId <= 0 {
return &pb.UpdateTemplateSettingsResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
serviceReq := &dto.UpdateTemplateSettingsRequest{
UserExternalID: userExternalID,
TemplateID: req.TemplateId,
Name: req.Name,
Description: req.Description,
IsPublic: req.IsPublic,
}
if err := document_service.DocumentSvc.UpdateTemplateSettings(ctx, serviceReq); err != nil {
return &pb.UpdateTemplateSettingsResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.UpdateTemplateSettingsResponse{
Base: baseResponseFromErr(nil),
}, nil
}
func (s *DocumentServiceServer) DeleteDocument(ctx context.Context, req *pb.DeleteDocumentRequest) (*pb.DeleteDocumentResponse, error) {
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return &pb.DeleteDocumentResponse{Base: baseResponseFromStatus(status.StatusTokenInvalid)}, nil
}
if req == nil || req.DocumentExternalId == "" {
return &pb.DeleteDocumentResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if err := document_service.DocumentSvc.DeleteDocumentByExternalID(ctx, req.DocumentExternalId); err != nil {
return &pb.DeleteDocumentResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.DeleteDocumentResponse{
Base: baseResponseFromErr(nil),
}, nil
}
@@ -0,0 +1,47 @@
package grpcserver
import (
"context"
"github.com/XingfenD/yoresee_doc/internal/config"
"github.com/XingfenD/yoresee_doc/internal/middleware"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
func UnaryAuthInterceptor(allowUnauth map[string]struct{}) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
if _, ok := allowUnauth[info.FullMethod]; ok {
return handler(ctx, req)
}
if md, ok := metadata.FromIncomingContext(ctx); ok {
if key := config.GlobalConfig.Backend.InternalRPCKey; key != "" {
if values := md.Get("x-internal-key"); len(values) > 0 && values[0] == key {
return handler(ctx, req)
}
}
}
authHeader := ""
if md, ok := metadata.FromIncomingContext(ctx); ok {
if values := md.Get("authorization"); len(values) > 0 {
authHeader = values[0]
} else if values := md.Get("Authorization"); len(values) > 0 {
authHeader = values[0]
}
}
claims, err := middleware.JWTAuth.ValidateAuthorizationHeader(authHeader)
if err != nil {
return nil, status.Error(codes.Unauthenticated, err.Error())
}
ctx = context.WithValue(ctx, "user_external_id", claims.ExternalID)
ctx = context.WithValue(ctx, "username", claims.Username)
return handler(ctx, req)
}
}
@@ -0,0 +1,323 @@
package grpcserver
import (
"context"
"strings"
"time"
"github.com/XingfenD/yoresee_doc/internal/dto"
"github.com/XingfenD/yoresee_doc/internal/repository/user_repo"
"github.com/XingfenD/yoresee_doc/internal/service/auth_service"
"github.com/XingfenD/yoresee_doc/internal/service/invitation_service"
"github.com/XingfenD/yoresee_doc/internal/status"
"github.com/XingfenD/yoresee_doc/internal/utils"
pb "github.com/XingfenD/yoresee_doc/pkg/gen/yoresee_doc/v1"
)
type InvitationServiceServer struct {
pb.UnimplementedInvitationServiceServer
userRepo *user_repo.UserRepository
}
func NewInvitationServiceServer(userRepo *user_repo.UserRepository) *InvitationServiceServer {
return &InvitationServiceServer{
userRepo: userRepo,
}
}
func (s *InvitationServiceServer) requireAdmin(ctx context.Context) error {
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return status.StatusTokenInvalid
}
isAdmin, err := auth_service.AuthSvc.IsAdmin(userExternalID)
if err != nil {
return err
}
if !isAdmin {
return status.StatusPermissionDenied
}
return nil
}
func parseOptionalTime(input *string) (*time.Time, error) {
if input == nil || strings.TrimSpace(*input) == "" {
return nil, nil
}
tm, err := time.Parse(time.RFC3339, *input)
if err != nil {
return nil, err
}
return &tm, nil
}
func (s *InvitationServiceServer) CreateInvitation(ctx context.Context, req *pb.CreateInvitationRequest) (*pb.CreateInvitationResponse, error) {
if req == nil {
return &pb.CreateInvitationResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if err := s.requireAdmin(ctx); err != nil {
return &pb.CreateInvitationResponse{Base: baseResponseFromErr(err)}, nil
}
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return &pb.CreateInvitationResponse{Base: baseResponseFromStatus(status.StatusTokenInvalid)}, nil
}
expiresAt, err := parseOptionalTime(req.ExpiresAt)
if err != nil {
return &pb.CreateInvitationResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
inv, err := invitation_service.InvitationSvc.CreateInvitation(&dto.CreateInvitationRequest{
CreatorExternalID: userExternalID,
MaxUsedCnt: req.MaxUsedCnt,
ExpiresAt: expiresAt,
Note: req.Note,
})
if err != nil {
return &pb.CreateInvitationResponse{Base: baseResponseFromErr(err)}, nil
}
user, _ := s.userRepo.GetByExternalID(userExternalID).Exec()
resp := &dto.InvitationResponse{
Code: inv.Code,
CreatedByExternalID: userExternalID,
CreatedByName: "",
UsedCnt: inv.UsedCnt,
MaxUsedCnt: inv.MaxUsedCnt,
ExpiresAt: inv.ExpiresAt,
CreatedAt: inv.CreatedAt,
Disabled: inv.Disabled,
Note: inv.Note,
}
if user != nil {
if user.Nickname != "" {
resp.CreatedByName = user.Nickname
} else {
resp.CreatedByName = user.Username
}
}
return &pb.CreateInvitationResponse{
Base: baseResponseFromErr(nil),
Invitation: toInvitationResponse(resp),
}, nil
}
func (s *InvitationServiceServer) ListInvitations(ctx context.Context, req *pb.ListInvitationsRequest) (*pb.ListInvitationsResponse, error) {
if req == nil {
return &pb.ListInvitationsResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
onlyMine := req.OnlyMine != nil && req.GetOnlyMine()
if !onlyMine {
if err := s.requireAdmin(ctx); err != nil {
return &pb.ListInvitationsResponse{Base: baseResponseFromErr(err)}, nil
}
}
var creatorID *int64
if onlyMine {
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || strings.TrimSpace(userExternalID) == "" {
return &pb.ListInvitationsResponse{Base: baseResponseFromStatus(status.StatusTokenInvalid)}, nil
}
id, err := s.userRepo.GetIDByExternalID(userExternalID).Exec()
if err != nil {
return &pb.ListInvitationsResponse{Base: baseResponseFromErr(status.StatusUserNotFound)}, nil
}
creatorID = utils.Of(id)
}
sortArgs := dto.SortArgs{Field: "created_at", Desc: true}
if req.OrderBy != nil {
sortArgs.Field = req.GetOrderBy()
}
if req.OrderDesc != nil {
sortArgs.Desc = req.GetOrderDesc()
}
listReq := &dto.ListInvitationsReq{
CreatorID: creatorID,
MaxUsedCnt: req.MaxUsedCnt,
ExpiresAtStart: req.ExpiresAtStart,
ExpiresAtEnd: req.ExpiresAtEnd,
CreatedAtStart: req.CreatedAtStart,
CreatedAtEnd: req.CreatedAtEnd,
Disabled: req.Disabled,
OnlyMine: req.OnlyMine,
Keyword: req.Keyword,
SortArgs: sortArgs,
Pagination: dto.Pagination{
Page: int(req.Page),
PageSize: int(req.PageSize),
},
}
invitations, total, err := invitation_service.InvitationSvc.ListInvitations(listReq)
if err != nil {
return &pb.ListInvitationsResponse{Base: baseResponseFromErr(err)}, nil
}
creatorIDs := make([]int64, 0, len(invitations))
for _, inv := range invitations {
creatorIDs = append(creatorIDs, inv.CreatedBy)
}
creatorMap, _ := s.userRepo.MGetUserByID(creatorIDs).Exec()
respInvites := make([]*pb.InvitationResponse, 0, len(invitations))
for _, inv := range invitations {
createdByExternalID := ""
createdByName := ""
if creator := creatorMap[inv.CreatedBy]; creator != nil {
createdByExternalID = creator.ExternalID
if creator.Nickname != "" {
createdByName = creator.Nickname
} else {
createdByName = creator.Username
}
}
resp := &dto.InvitationResponse{
Code: inv.Code,
CreatedByExternalID: createdByExternalID,
CreatedByName: createdByName,
UsedCnt: inv.UsedCnt,
MaxUsedCnt: inv.MaxUsedCnt,
ExpiresAt: inv.ExpiresAt,
CreatedAt: inv.CreatedAt,
Disabled: inv.Disabled,
Note: inv.Note,
}
respInvites = append(respInvites, toInvitationResponse(resp))
}
return &pb.ListInvitationsResponse{
Base: baseResponseFromErr(nil),
Invitations: respInvites,
Total: total,
}, nil
}
func (s *InvitationServiceServer) UpdateInvitation(ctx context.Context, req *pb.UpdateInvitationRequest) (*pb.UpdateInvitationResponse, error) {
if req == nil || strings.TrimSpace(req.Code) == "" {
return &pb.UpdateInvitationResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if err := s.requireAdmin(ctx); err != nil {
return &pb.UpdateInvitationResponse{Base: baseResponseFromErr(err)}, nil
}
expiresAt, err := parseOptionalTime(req.ExpiresAt)
if err != nil {
return &pb.UpdateInvitationResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if err := invitation_service.InvitationSvc.UpdateInvitation(&dto.UpdateInvitationRequest{
Code: req.Code,
MaxUsedCnt: req.MaxUsedCnt,
ExpiresAt: expiresAt,
Disabled: req.Disabled,
Note: req.Note,
}); err != nil {
return &pb.UpdateInvitationResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.UpdateInvitationResponse{Base: baseResponseFromErr(nil)}, nil
}
func (s *InvitationServiceServer) DeleteInvitation(ctx context.Context, req *pb.DeleteInvitationRequest) (*pb.DeleteInvitationResponse, error) {
if req == nil || strings.TrimSpace(req.Code) == "" {
return &pb.DeleteInvitationResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if err := s.requireAdmin(ctx); err != nil {
return &pb.DeleteInvitationResponse{Base: baseResponseFromErr(err)}, nil
}
if err := invitation_service.InvitationSvc.DeleteInvitation(&dto.DeleteInvitationRequest{Code: req.Code}); err != nil {
return &pb.DeleteInvitationResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.DeleteInvitationResponse{Base: baseResponseFromErr(nil)}, nil
}
func (s *InvitationServiceServer) ListInvitationRecords(ctx context.Context, req *pb.ListInvitationRecordsRequest) (*pb.ListInvitationRecordsResponse, error) {
if req == nil {
return &pb.ListInvitationRecordsResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
onlyMine := req.OnlyMine != nil && req.GetOnlyMine()
if !onlyMine {
if err := s.requireAdmin(ctx); err != nil {
return &pb.ListInvitationRecordsResponse{Base: baseResponseFromErr(err)}, nil
}
}
var creatorID *int64
if onlyMine {
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || strings.TrimSpace(userExternalID) == "" {
return &pb.ListInvitationRecordsResponse{Base: baseResponseFromStatus(status.StatusTokenInvalid)}, nil
}
id, err := s.userRepo.GetIDByExternalID(userExternalID).Exec()
if err != nil {
return &pb.ListInvitationRecordsResponse{Base: baseResponseFromErr(status.StatusUserNotFound)}, nil
}
creatorID = utils.Of(id)
}
listReq := &dto.ListInvitationRecordsRequest{
Code: req.Code,
Status: req.Status,
UsedAtStart: req.UsedAtStart,
UsedAtEnd: req.UsedAtEnd,
CreatorID: creatorID,
OnlyMine: req.OnlyMine,
Keyword: req.Keyword,
Pagination: dto.Pagination{
Page: int(req.Page),
PageSize: int(req.PageSize),
},
}
records, total, err := invitation_service.InvitationSvc.ListInvitationRecords(listReq)
if err != nil {
return &pb.ListInvitationRecordsResponse{Base: baseResponseFromErr(err)}, nil
}
userIDs := make([]int64, 0, len(records))
for _, r := range records {
if r.UsedByUserID != nil {
userIDs = append(userIDs, *r.UsedByUserID)
}
}
userMap, _ := s.userRepo.MGetUserByID(userIDs).Exec()
respRecords := make([]*pb.InvitationRecordResponse, 0, len(records))
for _, r := range records {
usedBy := r.UsedBy
usedByExternalID := ""
if r.UsedByUserID != nil {
if user := userMap[*r.UsedByUserID]; user != nil {
usedByExternalID = user.ExternalID
if user.Nickname != "" {
usedBy = user.Nickname
} else if user.Username != "" {
usedBy = user.Username
}
}
}
resp := &dto.InvitationRecordResponse{
Code: r.Code,
UsedBy: usedBy,
UsedByExternalID: usedByExternalID,
UsedAt: r.UsedAt,
Status: r.Status,
}
respRecords = append(respRecords, toInvitationRecordResponse(resp))
}
return &pb.ListInvitationRecordsResponse{
Base: baseResponseFromErr(nil),
Records: respRecords,
Total: total,
}, nil
}
@@ -0,0 +1,225 @@
package grpcserver
import (
"context"
"time"
"github.com/XingfenD/yoresee_doc/internal/dto"
"github.com/XingfenD/yoresee_doc/internal/service/document_service"
"github.com/XingfenD/yoresee_doc/internal/service/knowledge_base_service"
"github.com/XingfenD/yoresee_doc/internal/status"
"github.com/XingfenD/yoresee_doc/internal/utils"
pb "github.com/XingfenD/yoresee_doc/pkg/gen/yoresee_doc/v1"
)
type KnowledgeBaseServiceServer struct {
pb.UnimplementedKnowledgeBaseServiceServer
}
func NewKnowledgeBaseServiceServer() *KnowledgeBaseServiceServer {
return &KnowledgeBaseServiceServer{}
}
func (s *KnowledgeBaseServiceServer) ListKnowledgeBases(ctx context.Context, req *pb.ListKnowledgeBasesRequest) (*pb.ListKnowledgeBasesResponse, error) {
if req == nil {
return &pb.ListKnowledgeBasesResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return &pb.ListKnowledgeBasesResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
filterArgs := &dto.KnowledgeBaseListFilterArgs{
IsPublic: req.IsPublic,
NameKeyword: req.NameKeyword,
}
if req.CreateTimeRange != nil {
filterArgs.CreateTimeRangeStart = req.CreateTimeRange.Start
filterArgs.CreateTimeRangeEnd = req.CreateTimeRange.End
}
if req.UpdateTimeRange != nil {
filterArgs.UpdateTimeRangeStart = req.UpdateTimeRange.Start
filterArgs.UpdateTimeRangeEnd = req.UpdateTimeRange.End
}
sortArgs := dto.SortArgs{Field: "created_at", Desc: true}
if req.OrderBy != nil {
sortArgs.Field = req.GetOrderBy()
}
if req.OrderDesc != nil {
sortArgs.Desc = req.GetOrderDesc()
}
serviceReq := &dto.KnowledgeBaseListByExternalRequest{
CreatorExternalID: "",
FilterArgs: filterArgs,
SortArgs: sortArgs,
Pagination: dto.Pagination{
Page: int(req.Page),
PageSize: int(req.PageSize),
},
}
if req.OnlyMine {
serviceReq.CreatorExternalID = userExternalID
}
kbs, total, err := knowledge_base_service.KnowledgeBaseSvc.ListByExternal(serviceReq)
if err != nil {
return &pb.ListKnowledgeBasesResponse{Base: baseResponseFromErr(err)}, nil
}
respKBs := make([]*pb.KnowledgeBaseResponse, 0, len(kbs))
for _, kb := range kbs {
respKBs = append(respKBs, toKnowledgeBaseResponse(kb))
}
return &pb.ListKnowledgeBasesResponse{
Base: baseResponseFromErr(nil),
KnowledgeBases: respKBs,
Total: total,
}, nil
}
func (s *KnowledgeBaseServiceServer) GetKnowledgeBase(ctx context.Context, req *pb.GetKnowledgeBaseRequest) (*pb.GetKnowledgeBaseResponse, error) {
if req == nil || req.KnowledgeBaseExternalId == "" {
return &pb.GetKnowledgeBaseResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return &pb.GetKnowledgeBaseResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
kbDTO, err := knowledge_base_service.KnowledgeBaseSvc.GetByExternalID(&dto.KnowledgeBaseGetByExternalIDRequest{
KnowledgeBaseExternalID: req.KnowledgeBaseExternalId,
}).WithExtend().Exec()
if err != nil {
return &pb.GetKnowledgeBaseResponse{Base: baseResponseFromErr(status.StatusKnowledgeBaseNotFound)}, nil
}
if req.RecordRecentLog {
knowledge_base_service.KnowledgeBaseSvc.CreateRecentKnowledgeBase(&dto.CreateRecentKnowledgeBaseRequest{
UserExternalID: userExternalID,
KnowledgeBaseExternalID: req.KnowledgeBaseExternalId,
AssessTime: time.Now(),
})
}
svcReq := &dto.ListDocumentsByExternalRequest{
ExternalArgs: &dto.DocumentsListExternalArgs{
KnowledgeExternalID: utils.Of(kbDTO.ExternalID),
},
ListDocumentsBaseArgs: dto.ListDocumentsBaseArgs{
DirectoryOnly: req.DirectoryOnly,
},
Pagination: dto.Pagination{
Page: int(req.Page),
PageSize: int(req.PageSize),
},
Options: &dto.RecursiveOptions{
IncludeChildren: true,
Recursive: true,
},
}
documents, totalCount, err := document_service.DocumentSvc.ListDocumentsByExternal(
ctx,
svcReq,
)
if err != nil {
return &pb.GetKnowledgeBaseResponse{Base: baseResponseFromErr(status.StatusDocumentNotFound)}, nil
}
respDocs := make([]*pb.DocumentResponse, 0, len(documents))
for _, doc := range documents {
respDocs = append(respDocs, toDocumentResponse(doc))
}
return &pb.GetKnowledgeBaseResponse{
Base: baseResponseFromErr(nil),
KnowledgeBase: toKnowledgeBaseResponse(kbDTO),
Documents: respDocs,
TotalCount: totalCount,
}, nil
}
func (s *KnowledgeBaseServiceServer) CreateKnowledgeBase(ctx context.Context, req *pb.CreateKnowledgeBaseRequest) (*pb.CreateKnowledgeBaseResponse, error) {
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return &pb.CreateKnowledgeBaseResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if req == nil || req.Name == "" {
return &pb.CreateKnowledgeBaseResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
resp, err := knowledge_base_service.KnowledgeBaseSvc.Create(&dto.CreateKnowledgeBaseRequest{
CreatorExternalID: userExternalID,
Name: req.Name,
Description: req.Description,
Cover: req.Cover,
IsPublic: req.IsPublic,
})
if err != nil {
return &pb.CreateKnowledgeBaseResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.CreateKnowledgeBaseResponse{
Base: baseResponseFromErr(nil),
ExternalId: resp.ExternalID,
}, nil
}
func (s *KnowledgeBaseServiceServer) ListRecentKnowledgeBases(ctx context.Context, req *pb.ListRecentKnowledgeBasesRequest) (*pb.ListRecentKnowledgeBasesResponse, error) {
if req == nil {
return &pb.ListRecentKnowledgeBasesResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return &pb.ListRecentKnowledgeBasesResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
var startTime *time.Time
if req.StartTime != nil && *req.StartTime != "" {
t, err := time.Parse(time.RFC3339, *req.StartTime)
if err != nil {
return &pb.ListRecentKnowledgeBasesResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
startTime = &t
}
var endTime *time.Time
if req.EndTime != nil && *req.EndTime != "" {
t, err := time.Parse(time.RFC3339, *req.EndTime)
if err != nil {
return &pb.ListRecentKnowledgeBasesResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
endTime = &t
}
serviceReq := &dto.ListRecentKnowledgeBasesRequest{
UserExternalID: userExternalID,
StartTime: startTime,
EndTime: endTime,
Pagination: dto.Pagination{
Page: int(req.Page),
PageSize: int(req.PageSize),
},
}
kbs, total, err := knowledge_base_service.KnowledgeBaseSvc.ListRecentKnowledgeBases(serviceReq)
if err != nil {
return &pb.ListRecentKnowledgeBasesResponse{Base: baseResponseFromErr(err)}, nil
}
respKBs := make([]*pb.KnowledgeBaseResponse, 0, len(kbs))
for _, kb := range kbs {
respKBs = append(respKBs, toKnowledgeBaseResponse(kb))
}
return &pb.ListRecentKnowledgeBasesResponse{
Base: baseResponseFromErr(nil),
KnowledgeBases: respKBs,
Total: total,
}, nil
}
@@ -0,0 +1,446 @@
package grpcserver
import (
"context"
"strings"
"github.com/XingfenD/yoresee_doc/internal/dto"
"github.com/XingfenD/yoresee_doc/internal/service/auth_service"
"github.com/XingfenD/yoresee_doc/internal/service/membership_service"
"github.com/XingfenD/yoresee_doc/internal/status"
pb "github.com/XingfenD/yoresee_doc/pkg/gen/yoresee_doc/v1"
)
type MembershipServiceServer struct {
pb.UnimplementedMembershipServiceServer
}
func NewMembershipServiceServer() *MembershipServiceServer {
return &MembershipServiceServer{}
}
func (s *MembershipServiceServer) requireAdmin(ctx context.Context) error {
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return status.StatusTokenInvalid
}
isAdmin, err := auth_service.AuthSvc.IsAdmin(userExternalID)
if err != nil {
return err
}
if !isAdmin {
return status.StatusPermissionDenied
}
return nil
}
func (s *MembershipServiceServer) ListUserGroups(ctx context.Context, req *pb.ListUserGroupsRequest) (*pb.ListUserGroupsResponse, error) {
if req == nil {
return &pb.ListUserGroupsResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if err := s.requireAdmin(ctx); err != nil {
return &pb.ListUserGroupsResponse{Base: baseResponseFromErr(err)}, nil
}
serviceReq := &dto.ListUserGroupsRequest{
Keyword: req.Keyword,
Pagination: dto.Pagination{
Page: int(req.Page),
PageSize: int(req.PageSize),
},
}
groups, total, err := membership_service.MembershipSvc.ListUserGroups(serviceReq)
if err != nil {
return &pb.ListUserGroupsResponse{Base: baseResponseFromErr(err)}, nil
}
respGroups := make([]*pb.UserGroupResponse, 0, len(groups))
for _, group := range groups {
respGroups = append(respGroups, toUserGroupResponse(group))
}
return &pb.ListUserGroupsResponse{
Base: baseResponseFromErr(nil),
UserGroups: respGroups,
Total: total,
}, nil
}
func (s *MembershipServiceServer) GetUserGroup(ctx context.Context, req *pb.GetUserGroupRequest) (*pb.GetUserGroupResponse, error) {
if req == nil || strings.TrimSpace(req.ExternalId) == "" {
return &pb.GetUserGroupResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if err := s.requireAdmin(ctx); err != nil {
return &pb.GetUserGroupResponse{Base: baseResponseFromErr(err)}, nil
}
group, err := membership_service.MembershipSvc.GetUserGroup(&dto.GetUserGroupRequest{
ExternalID: req.ExternalId,
})
if err != nil {
return &pb.GetUserGroupResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.GetUserGroupResponse{
Base: baseResponseFromErr(nil),
UserGroup: toUserGroupResponse(group),
}, nil
}
func (s *MembershipServiceServer) CreateUserGroup(ctx context.Context, req *pb.CreateUserGroupRequest) (*pb.CreateUserGroupResponse, error) {
if req == nil || strings.TrimSpace(req.Name) == "" {
return &pb.CreateUserGroupResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return &pb.CreateUserGroupResponse{Base: baseResponseFromStatus(status.StatusTokenInvalid)}, nil
}
if err := s.requireAdmin(ctx); err != nil {
return &pb.CreateUserGroupResponse{Base: baseResponseFromErr(err)}, nil
}
externalID, err := membership_service.MembershipSvc.CreateUserGroup(&dto.CreateUserGroupRequest{
CreatorUserExternalID: userExternalID,
Name: req.Name,
Description: req.Description,
MemberUserExternalIDs: req.MemberUserExternalIds,
})
if err != nil {
return &pb.CreateUserGroupResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.CreateUserGroupResponse{
Base: baseResponseFromErr(nil),
ExternalId: externalID,
}, nil
}
func (s *MembershipServiceServer) UpdateUserGroup(ctx context.Context, req *pb.UpdateUserGroupRequest) (*pb.UpdateUserGroupResponse, error) {
if req == nil || strings.TrimSpace(req.ExternalId) == "" {
return &pb.UpdateUserGroupResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if err := s.requireAdmin(ctx); err != nil {
return &pb.UpdateUserGroupResponse{Base: baseResponseFromErr(err)}, nil
}
err := membership_service.MembershipSvc.UpdateUserGroup(&dto.UpdateUserGroupRequest{
ExternalID: req.ExternalId,
Name: req.Name,
Description: req.Description,
SyncMembers: req.SyncMembers,
MemberUserExternalIDs: req.MemberUserExternalIds,
})
if err != nil {
return &pb.UpdateUserGroupResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.UpdateUserGroupResponse{
Base: baseResponseFromErr(nil),
}, nil
}
func (s *MembershipServiceServer) DeleteUserGroup(ctx context.Context, req *pb.DeleteUserGroupRequest) (*pb.DeleteUserGroupResponse, error) {
if req == nil || strings.TrimSpace(req.ExternalId) == "" {
return &pb.DeleteUserGroupResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if err := s.requireAdmin(ctx); err != nil {
return &pb.DeleteUserGroupResponse{Base: baseResponseFromErr(err)}, nil
}
if err := membership_service.MembershipSvc.DeleteUserGroup(&dto.DeleteUserGroupRequest{
ExternalID: req.ExternalId,
}); err != nil {
return &pb.DeleteUserGroupResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.DeleteUserGroupResponse{
Base: baseResponseFromErr(nil),
}, nil
}
func (s *MembershipServiceServer) ListUsers(ctx context.Context, req *pb.ListUsersRequest) (*pb.ListUsersResponse, error) {
if req == nil {
return &pb.ListUsersResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if err := s.requireAdmin(ctx); err != nil {
return &pb.ListUsersResponse{Base: baseResponseFromErr(err)}, nil
}
serviceReq := &dto.ListUsersRequest{
Keyword: req.Keyword,
Pagination: dto.Pagination{
Page: int(req.Page),
PageSize: int(req.PageSize),
},
}
users, total, err := membership_service.MembershipSvc.ListUsers(serviceReq)
if err != nil {
return &pb.ListUsersResponse{Base: baseResponseFromErr(err)}, nil
}
respUsers := make([]*pb.UserResponse, 0, len(users))
for _, user := range users {
respUsers = append(respUsers, toUserResponse(user))
}
return &pb.ListUsersResponse{
Base: baseResponseFromErr(nil),
Users: respUsers,
Total: total,
}, nil
}
func (s *MembershipServiceServer) UpdateUser(ctx context.Context, req *pb.UpdateUserRequest) (*pb.UpdateUserResponse, error) {
if req == nil || strings.TrimSpace(req.ExternalId) == "" {
return &pb.UpdateUserResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if err := s.requireAdmin(ctx); err != nil {
return &pb.UpdateUserResponse{Base: baseResponseFromErr(err)}, nil
}
serviceReq := &dto.UpdateUserRequest{
ExternalID: req.ExternalId,
Username: req.Username,
Email: req.Email,
Nickname: req.Nickname,
Status: req.Status,
}
if err := membership_service.MembershipSvc.UpdateUser(serviceReq); err != nil {
return &pb.UpdateUserResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.UpdateUserResponse{
Base: baseResponseFromErr(nil),
}, nil
}
func (s *MembershipServiceServer) ListUserGroupMembers(ctx context.Context, req *pb.ListUserGroupMembersRequest) (*pb.ListUserGroupMembersResponse, error) {
if req == nil || strings.TrimSpace(req.ExternalId) == "" {
return &pb.ListUserGroupMembersResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if err := s.requireAdmin(ctx); err != nil {
return &pb.ListUserGroupMembersResponse{Base: baseResponseFromErr(err)}, nil
}
serviceReq := &dto.ListUserGroupMembersRequest{
ExternalID: req.ExternalId,
Keyword: req.Keyword,
Pagination: dto.Pagination{
Page: int(req.Page),
PageSize: int(req.PageSize),
},
}
users, total, err := membership_service.MembershipSvc.ListUserGroupMembers(serviceReq)
if err != nil {
return &pb.ListUserGroupMembersResponse{Base: baseResponseFromErr(err)}, nil
}
respUsers := make([]*pb.UserResponse, 0, len(users))
for _, user := range users {
respUsers = append(respUsers, toUserResponse(user))
}
return &pb.ListUserGroupMembersResponse{
Base: baseResponseFromErr(nil),
Users: respUsers,
Total: total,
}, nil
}
func (s *MembershipServiceServer) ListOrgNodes(ctx context.Context, req *pb.ListOrgNodesRequest) (*pb.ListOrgNodesResponse, error) {
if req == nil {
return &pb.ListOrgNodesResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if err := s.requireAdmin(ctx); err != nil {
return &pb.ListOrgNodesResponse{Base: baseResponseFromErr(err)}, nil
}
serviceReq := &dto.ListOrgNodesRequest{
ParentExternalID: req.ParentExternalId,
Keyword: req.Keyword,
IncludeChildren: req.IncludeChildren,
Pagination: dto.Pagination{
Page: int(req.Page),
PageSize: int(req.PageSize),
},
}
nodes, total, err := membership_service.MembershipSvc.ListOrgNodes(serviceReq)
if err != nil {
return &pb.ListOrgNodesResponse{Base: baseResponseFromErr(err)}, nil
}
respNodes := make([]*pb.OrgNodeResponse, 0, len(nodes))
for _, node := range nodes {
respNodes = append(respNodes, toOrgNodeResponse(node))
}
return &pb.ListOrgNodesResponse{
Base: baseResponseFromErr(nil),
OrgNodes: respNodes,
Total: total,
}, nil
}
func (s *MembershipServiceServer) GetOrgNode(ctx context.Context, req *pb.GetOrgNodeRequest) (*pb.GetOrgNodeResponse, error) {
if req == nil || strings.TrimSpace(req.ExternalId) == "" {
return &pb.GetOrgNodeResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if err := s.requireAdmin(ctx); err != nil {
return &pb.GetOrgNodeResponse{Base: baseResponseFromErr(err)}, nil
}
node, err := membership_service.MembershipSvc.GetOrgNode(&dto.GetOrgNodeRequest{
ExternalID: req.ExternalId,
IncludeChildren: req.IncludeChildren,
})
if err != nil {
return &pb.GetOrgNodeResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.GetOrgNodeResponse{
Base: baseResponseFromErr(nil),
OrgNode: toOrgNodeResponse(node),
}, nil
}
func (s *MembershipServiceServer) CreateOrgNode(ctx context.Context, req *pb.CreateOrgNodeRequest) (*pb.CreateOrgNodeResponse, error) {
if req == nil || strings.TrimSpace(req.Name) == "" {
return &pb.CreateOrgNodeResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return &pb.CreateOrgNodeResponse{Base: baseResponseFromStatus(status.StatusTokenInvalid)}, nil
}
if err := s.requireAdmin(ctx); err != nil {
return &pb.CreateOrgNodeResponse{Base: baseResponseFromErr(err)}, nil
}
externalID, err := membership_service.MembershipSvc.CreateOrgNode(&dto.CreateOrgNodeRequest{
CreatorUserExternalID: userExternalID,
ParentExternalID: req.ParentExternalId,
Name: req.Name,
Description: req.Description,
MemberUserExternalIDs: req.MemberUserExternalIds,
})
if err != nil {
return &pb.CreateOrgNodeResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.CreateOrgNodeResponse{
Base: baseResponseFromErr(nil),
ExternalId: externalID,
}, nil
}
func (s *MembershipServiceServer) UpdateOrgNode(ctx context.Context, req *pb.UpdateOrgNodeRequest) (*pb.UpdateOrgNodeResponse, error) {
if req == nil || strings.TrimSpace(req.ExternalId) == "" {
return &pb.UpdateOrgNodeResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if err := s.requireAdmin(ctx); err != nil {
return &pb.UpdateOrgNodeResponse{Base: baseResponseFromErr(err)}, nil
}
err := membership_service.MembershipSvc.UpdateOrgNode(&dto.UpdateOrgNodeRequest{
ExternalID: req.ExternalId,
Name: req.Name,
Description: req.Description,
SyncMembers: req.SyncMembers,
MemberUserExternalIDs: req.MemberUserExternalIds,
})
if err != nil {
return &pb.UpdateOrgNodeResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.UpdateOrgNodeResponse{
Base: baseResponseFromErr(nil),
}, nil
}
func (s *MembershipServiceServer) DeleteOrgNode(ctx context.Context, req *pb.DeleteOrgNodeRequest) (*pb.DeleteOrgNodeResponse, error) {
if req == nil || strings.TrimSpace(req.ExternalId) == "" {
return &pb.DeleteOrgNodeResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if err := s.requireAdmin(ctx); err != nil {
return &pb.DeleteOrgNodeResponse{Base: baseResponseFromErr(err)}, nil
}
if err := membership_service.MembershipSvc.DeleteOrgNode(&dto.DeleteOrgNodeRequest{
ExternalID: req.ExternalId,
}); err != nil {
return &pb.DeleteOrgNodeResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.DeleteOrgNodeResponse{
Base: baseResponseFromErr(nil),
}, nil
}
func (s *MembershipServiceServer) MoveOrgNode(ctx context.Context, req *pb.MoveOrgNodeRequest) (*pb.MoveOrgNodeResponse, error) {
if req == nil || strings.TrimSpace(req.ExternalId) == "" {
return &pb.MoveOrgNodeResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if err := s.requireAdmin(ctx); err != nil {
return &pb.MoveOrgNodeResponse{Base: baseResponseFromErr(err)}, nil
}
if err := membership_service.MembershipSvc.MoveOrgNode(&dto.MoveOrgNodeRequest{
ExternalID: req.ExternalId,
NewParentExternalID: req.NewParentExternalId,
}); err != nil {
return &pb.MoveOrgNodeResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.MoveOrgNodeResponse{
Base: baseResponseFromErr(nil),
}, nil
}
func (s *MembershipServiceServer) ListOrgNodeMembers(ctx context.Context, req *pb.ListOrgNodeMembersRequest) (*pb.ListOrgNodeMembersResponse, error) {
if req == nil || strings.TrimSpace(req.ExternalId) == "" {
return &pb.ListOrgNodeMembersResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if err := s.requireAdmin(ctx); err != nil {
return &pb.ListOrgNodeMembersResponse{Base: baseResponseFromErr(err)}, nil
}
serviceReq := &dto.ListOrgNodeMembersRequest{
ExternalID: req.ExternalId,
Keyword: req.Keyword,
Pagination: dto.Pagination{
Page: int(req.Page),
PageSize: int(req.PageSize),
},
}
users, total, err := membership_service.MembershipSvc.ListOrgNodeMembers(serviceReq)
if err != nil {
return &pb.ListOrgNodeMembersResponse{Base: baseResponseFromErr(err)}, nil
}
respUsers := make([]*pb.UserResponse, 0, len(users))
for _, user := range users {
respUsers = append(respUsers, toUserResponse(user))
}
return &pb.ListOrgNodeMembersResponse{
Base: baseResponseFromErr(nil),
Users: respUsers,
Total: total,
}, nil
}
func toOrgNodeResponse(node *dto.OrgNodeResponse) *pb.OrgNodeResponse {
resp := &pb.OrgNodeResponse{
ExternalId: node.ExternalID,
ParentExternalId: node.ParentExternalID,
Name: node.Name,
Path: node.Path,
Description: node.Description,
CreatorUserExternalId: node.CreatorUserExternalID,
MemberCount: int32(node.MemberCount),
}
if node.Children != nil {
resp.Children = make([]*pb.OrgNodeResponse, 0, len(node.Children))
for _, child := range node.Children {
resp.Children = append(resp.Children, toOrgNodeResponse(child))
}
}
return resp
}
@@ -0,0 +1,159 @@
package grpcserver
import (
"context"
"strings"
"github.com/XingfenD/yoresee_doc/internal/domain_event"
"github.com/XingfenD/yoresee_doc/internal/dto"
"github.com/XingfenD/yoresee_doc/internal/model"
"github.com/XingfenD/yoresee_doc/internal/service/auth_service"
"github.com/XingfenD/yoresee_doc/internal/service/notification_service"
"github.com/XingfenD/yoresee_doc/internal/status"
pb "github.com/XingfenD/yoresee_doc/pkg/gen/yoresee_doc/v1"
)
type NotificationServiceServer struct {
pb.UnimplementedNotificationServiceServer
}
func NewNotificationServiceServer() *NotificationServiceServer {
return &NotificationServiceServer{}
}
func (s *NotificationServiceServer) CreateNotification(ctx context.Context, req *pb.CreateNotificationRequest) (*pb.CreateNotificationResponse, error) {
if req == nil {
return &pb.CreateNotificationResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || strings.TrimSpace(userExternalID) == "" {
return &pb.CreateNotificationResponse{Base: baseResponseFromStatus(status.StatusTokenInvalid)}, nil
}
receiverExternalIDs := req.ReceiverExternalIds
if len(receiverExternalIDs) == 0 {
receiverExternalIDs = []string{userExternalID}
}
if !onlySelf(receiverExternalIDs, userExternalID) {
isAdmin, err := auth_service.AuthSvc.IsAdmin(userExternalID)
if err != nil {
return &pb.CreateNotificationResponse{Base: baseResponseFromErr(err)}, nil
}
if !isAdmin {
return &pb.CreateNotificationResponse{Base: baseResponseFromStatus(status.StatusPermissionDenied)}, nil
}
}
evt := domain_event.NotificationCreateEvent{
ReceiverExternalIDs: receiverExternalIDs,
Type: req.Type,
Title: req.Title,
Content: req.Content,
PayloadJSON: req.PayloadJson,
}
if err := domain_event.PublishNotificationCreateEvent(ctx, evt); err != nil {
return &pb.CreateNotificationResponse{Base: baseResponseFromErr(status.StatusMQNotInitialized)}, nil
}
return &pb.CreateNotificationResponse{Base: baseResponseFromErr(nil)}, nil
}
func (s *NotificationServiceServer) ListNotifications(ctx context.Context, req *pb.ListNotificationsRequest) (*pb.ListNotificationsResponse, error) {
if req == nil {
return &pb.ListNotificationsResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || strings.TrimSpace(userExternalID) == "" {
return &pb.ListNotificationsResponse{Base: baseResponseFromStatus(status.StatusTokenInvalid)}, nil
}
var statusFilter *string
if req.Status != nil && strings.TrimSpace(req.GetStatus()) != "" {
statusFilter = req.Status
}
list, total, err := notification_service.NotificationSvc.ListNotifications(&dto.ListNotificationsRequest{
UserExternalID: userExternalID,
Status: statusFilter,
Pagination: dto.Pagination{
Page: int(req.Page),
PageSize: int(req.PageSize),
},
})
if err != nil {
return &pb.ListNotificationsResponse{Base: baseResponseFromErr(err)}, nil
}
respItems := make([]*pb.Notification, 0, len(list))
for _, item := range list {
respItems = append(respItems, toNotificationResponse(&item))
}
return &pb.ListNotificationsResponse{
Base: baseResponseFromErr(nil),
Notifications: respItems,
Total: total,
}, nil
}
func (s *NotificationServiceServer) MarkNotificationsRead(ctx context.Context, req *pb.MarkNotificationsReadRequest) (*pb.MarkNotificationsReadResponse, error) {
if req == nil {
return &pb.MarkNotificationsReadResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || strings.TrimSpace(userExternalID) == "" {
return &pb.MarkNotificationsReadResponse{Base: baseResponseFromStatus(status.StatusTokenInvalid)}, nil
}
err := notification_service.NotificationSvc.MarkRead(&dto.MarkNotificationsReadRequest{
UserExternalID: userExternalID,
ExternalIDs: req.ExternalIds,
})
if err != nil {
return &pb.MarkNotificationsReadResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.MarkNotificationsReadResponse{Base: baseResponseFromErr(nil)}, nil
}
func (s *NotificationServiceServer) MarkAllNotificationsRead(ctx context.Context, req *pb.MarkAllNotificationsReadRequest) (*pb.MarkAllNotificationsReadResponse, error) {
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || strings.TrimSpace(userExternalID) == "" {
return &pb.MarkAllNotificationsReadResponse{Base: baseResponseFromStatus(status.StatusTokenInvalid)}, nil
}
if err := notification_service.NotificationSvc.MarkAllRead(userExternalID); err != nil {
return &pb.MarkAllNotificationsReadResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.MarkAllNotificationsReadResponse{Base: baseResponseFromErr(nil)}, nil
}
func toNotificationResponse(item *model.Notification) *pb.Notification {
if item == nil {
return nil
}
return &pb.Notification{
ExternalId: item.ExternalID,
Type: item.Type,
Status: item.Status,
Title: item.Title,
Content: item.Content,
Payload: item.Payload,
CreatedAt: timeToString(item.CreatedAt),
}
}
func onlySelf(list []string, self string) bool {
if len(list) == 0 {
return true
}
for _, id := range list {
if strings.TrimSpace(id) == "" {
continue
}
if id != self {
return false
}
}
return true
}
+158
View File
@@ -0,0 +1,158 @@
package grpcserver
import (
"errors"
"fmt"
"net"
"net/http"
"strconv"
"strings"
"github.com/XingfenD/yoresee_doc/internal/config"
"github.com/XingfenD/yoresee_doc/internal/repository"
"github.com/XingfenD/yoresee_doc/internal/status"
pb "github.com/XingfenD/yoresee_doc/pkg/gen/yoresee_doc/v1"
"github.com/improbable-eng/grpc-web/go/grpcweb"
"github.com/sirupsen/logrus"
"google.golang.org/grpc"
)
func Start(grpcPort, grpcWebPort int, repos *repository.Repositories) (*grpc.Server, error) {
allowUnauth := map[string]struct{}{
"/yoresee_doc.v1.AuthService/Login": {},
"/yoresee_doc.v1.AuthService/Register": {},
"/yoresee_doc.v1.SystemService/Health": {},
"/yoresee_doc.v1.SystemService/SystemInfo": {},
}
grpcServer := grpc.NewServer(
grpc.UnaryInterceptor(UnaryAuthInterceptor(allowUnauth)),
)
pb.RegisterAuthServiceServer(grpcServer, NewAuthServiceServer())
pb.RegisterDocumentServiceServer(grpcServer, NewDocumentServiceServer())
pb.RegisterKnowledgeBaseServiceServer(grpcServer, NewKnowledgeBaseServiceServer())
pb.RegisterSystemServiceServer(grpcServer, NewSystemServiceServer())
pb.RegisterMembershipServiceServer(grpcServer, NewMembershipServiceServer())
pb.RegisterInvitationServiceServer(grpcServer, NewInvitationServiceServer(repos.User))
grpcListener, err := net.Listen("tcp", fmt.Sprintf(":%d", grpcPort))
if err != nil {
return nil, status.GenErrWithCustomMsg(status.StatusServiceInternalError, "listen grpc port failed")
}
go func() {
if serveErr := grpcServer.Serve(grpcListener); serveErr != nil {
logrus.Errorf("grpc server exited with error: %v", serveErr)
}
}()
grpcWebServer := grpcweb.WrapServer(grpcServer,
grpcweb.WithOriginFunc(isOriginAllowed),
)
grpcWebHandler := grpcWebHTTPHandler(grpcWebServer)
grpcWebAddr := fmt.Sprintf(":%d", grpcWebPort)
go func() {
if serveErr := http.ListenAndServe(grpcWebAddr, grpcWebHandler); serveErr != nil && !errors.Is(serveErr, http.ErrServerClosed) {
logrus.Errorf("grpc-web server exited with error: %v", serveErr)
}
}()
return grpcServer, nil
}
func grpcWebHTTPHandler(wrapped *grpcweb.WrappedGrpcServer) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
setGrpcWebCorsHeaders(w, r)
if wrapped.IsGrpcWebRequest(r) || wrapped.IsGrpcWebSocketRequest(r) {
wrapped.ServeHTTP(w, r)
return
}
if r.Method == http.MethodOptions && wrapped.IsAcceptableGrpcCorsRequest(r) {
w.WriteHeader(http.StatusOK)
return
}
http.NotFound(w, r)
})
}
func setGrpcWebCorsHeaders(w http.ResponseWriter, r *http.Request) {
origin := strings.TrimSpace(r.Header.Get("Origin"))
if allowedOrigin := resolveAllowedOrigin(origin); allowedOrigin != "" {
w.Header().Set("Access-Control-Allow-Origin", allowedOrigin)
}
w.Header().Set("Access-Control-Allow-Credentials", strconv.FormatBool(corsAllowCredentials()))
w.Header().Set("Access-Control-Allow-Headers", strings.Join(corsAllowedHeaders(), ","))
w.Header().Set("Access-Control-Allow-Methods", strings.Join(corsAllowedMethods(), ","))
w.Header().Set("Access-Control-Expose-Headers", "Grpc-Status,Grpc-Message,Grpc-Status-Details-Bin")
if maxAge := corsMaxAge(); maxAge > 0 {
w.Header().Set("Access-Control-Max-Age", strconv.Itoa(maxAge))
}
}
func isOriginAllowed(origin string) bool {
origin = strings.TrimSpace(origin)
if origin == "" {
return false
}
allowedOrigins := corsAllowedOrigins()
if len(allowedOrigins) == 0 {
return true
}
for _, allowed := range allowedOrigins {
allowed = strings.TrimSpace(allowed)
if allowed == "*" || strings.EqualFold(allowed, origin) {
return true
}
}
return false
}
func resolveAllowedOrigin(origin string) string {
if origin == "" {
if len(corsAllowedOrigins()) == 0 {
return "*"
}
return ""
}
if isOriginAllowed(origin) {
return origin
}
return ""
}
func corsAllowedOrigins() []string {
if config.GlobalConfig == nil {
return nil
}
return config.GlobalConfig.Backend.Security.CORS.AllowedOrigins
}
func corsAllowedMethods() []string {
if config.GlobalConfig != nil && len(config.GlobalConfig.Backend.Security.CORS.AllowedMethods) > 0 {
return config.GlobalConfig.Backend.Security.CORS.AllowedMethods
}
return []string{"POST", "GET", "OPTIONS"}
}
func corsAllowedHeaders() []string {
if config.GlobalConfig != nil && len(config.GlobalConfig.Backend.Security.CORS.AllowedHeaders) > 0 {
return config.GlobalConfig.Backend.Security.CORS.AllowedHeaders
}
return []string{"Accept", "Accept-Language", "Content-Type", "Content-Length", "X-Grpc-Web", "X-User-Agent", "Grpc-Timeout", "Authorization"}
}
func corsAllowCredentials() bool {
if config.GlobalConfig != nil {
return config.GlobalConfig.Backend.Security.CORS.AllowCredentials
}
return true
}
func corsMaxAge() int {
if config.GlobalConfig != nil {
return config.GlobalConfig.Backend.Security.CORS.MaxAge
}
return 0
}
@@ -0,0 +1,117 @@
package grpcserver
import (
"context"
"strings"
"github.com/XingfenD/yoresee_doc/internal/service/auth_service"
"github.com/XingfenD/yoresee_doc/internal/service/setting_service"
"github.com/XingfenD/yoresee_doc/internal/status"
pb "github.com/XingfenD/yoresee_doc/pkg/gen/yoresee_doc/v1"
)
type SettingServiceServer struct {
pb.UnimplementedSettingServiceServer
}
func NewSettingServiceServer() *SettingServiceServer {
return &SettingServiceServer{}
}
func (s *SettingServiceServer) requireAdmin(ctx context.Context) error {
userExternalID, ok := ctx.Value("user_external_id").(string)
if !ok || userExternalID == "" {
return status.StatusTokenInvalid
}
isAdmin, err := auth_service.AuthSvc.IsAdmin(userExternalID)
if err != nil {
return err
}
if !isAdmin {
return status.StatusPermissionDenied
}
return nil
}
func (s *SettingServiceServer) GetSettings(ctx context.Context, req *pb.GetSettingsRequest) (*pb.GetSettingsResponse, error) {
if req == nil {
return &pb.GetSettingsResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if err := s.requireAdmin(ctx); err != nil {
return &pb.GetSettingsResponse{Base: baseResponseFromErr(err)}, nil
}
scene := strings.TrimSpace(req.Scene)
groups, err := setting_service.SettingSvc.GetSettings(ctx, scene)
if err != nil {
return &pb.GetSettingsResponse{Base: baseResponseFromErr(err)}, nil
}
respGroups := make([]*pb.SettingGroup, 0, len(groups))
for _, group := range groups {
items := make([]*pb.SettingItem, 0, len(group.Items))
for _, item := range group.Items {
options := make([]*pb.SettingOption, 0, len(item.UI.Options))
for _, opt := range item.UI.Options {
options = append(options, &pb.SettingOption{
Label: opt.Label,
LabelKey: opt.LabelKey,
Value: opt.Value,
})
}
items = append(items, &pb.SettingItem{
Key: item.Key,
Label: item.Label,
LabelKey: item.LabelKey,
Description: item.Description,
DescriptionKey: item.DescriptionKey,
Type: item.Type,
Ui: &pb.SettingUI{
Component: item.UI.Component,
Options: options,
Placeholder: item.UI.Placeholder,
PlaceholderKey: item.UI.PlaceholderKey,
},
Value: item.Value,
DefaultValue: item.DefaultValue,
Required: item.Required,
Readonly: item.Readonly,
})
}
respGroups = append(respGroups, &pb.SettingGroup{
Key: group.Key,
Title: group.Title,
TitleKey: group.TitleKey,
Items: items,
})
}
return &pb.GetSettingsResponse{
Base: baseResponseFromErr(nil),
Groups: respGroups,
}, nil
}
func (s *SettingServiceServer) UpdateSettings(ctx context.Context, req *pb.UpdateSettingsRequest) (*pb.UpdateSettingsResponse, error) {
if req == nil {
return &pb.UpdateSettingsResponse{Base: baseResponseFromStatus(status.StatusParamError)}, nil
}
if err := s.requireAdmin(ctx); err != nil {
return &pb.UpdateSettingsResponse{Base: baseResponseFromErr(err)}, nil
}
updates := make([]setting_service.SettingUpdate, 0, len(req.Updates))
for _, update := range req.Updates {
if update == nil {
continue
}
updates = append(updates, setting_service.SettingUpdate{
Key: update.Key,
Value: update.Value,
})
}
if err := setting_service.SettingSvc.UpdateSettings(ctx, updates); err != nil {
return &pb.UpdateSettingsResponse{Base: baseResponseFromErr(err)}, nil
}
return &pb.UpdateSettingsResponse{Base: baseResponseFromErr(nil)}, nil
}
@@ -0,0 +1,35 @@
package grpcserver
import (
"context"
"time"
"github.com/XingfenD/yoresee_doc/internal/config"
"github.com/XingfenD/yoresee_doc/internal/service/config_service"
pb "github.com/XingfenD/yoresee_doc/pkg/gen/yoresee_doc/v1"
)
type SystemServiceServer struct {
pb.UnimplementedSystemServiceServer
}
func NewSystemServiceServer() *SystemServiceServer {
return &SystemServiceServer{}
}
func (s *SystemServiceServer) Health(ctx context.Context, req *pb.HealthRequest) (*pb.HealthResponse, error) {
return &pb.HealthResponse{
Base: baseResponseFromErr(nil),
Timestamp: time.Now().Format(time.RFC3339),
Status: "healthy",
Version: "1.0.0",
}, nil
}
func (s *SystemServiceServer) SystemInfo(ctx context.Context, req *pb.SystemInfoRequest) (*pb.SystemInfoResponse, error) {
return &pb.SystemInfoResponse{
Base: baseResponseFromErr(nil),
SystemName: config.GlobalConfig.Backend.SystemName,
SystemRegisterMode: config_service.ConfigSvc.GetSystemRegisterMode(ctx),
}, nil
}
+33
View File
@@ -0,0 +1,33 @@
package grpcserver
import (
doc_container_mapper "github.com/XingfenD/yoresee_doc/internal/mapper/doc_container_mapper"
"github.com/XingfenD/yoresee_doc/internal/status"
pb "github.com/XingfenD/yoresee_doc/pkg/gen/yoresee_doc/v1"
)
func validateUpdateDocumentRequest(req *pb.UpdateDocumentRequest) error {
if req == nil {
return status.StatusParamError
}
if req.Content == nil && req.KnowledgeBaseExternalId == nil && req.ParentExternalId == nil && req.Title == nil && req.MoveToContainer == nil {
return status.GenErrWithCustomMsg(status.StatusParamError, "no update fields")
}
if req.MoveToContainer != nil {
containerType, ok := doc_container_mapper.FromProtoType(*req.MoveToContainer)
if !ok {
return status.GenErrWithCustomMsg(status.StatusParamError, "invalid move_to_container")
}
requiresKnowledgeBaseID := doc_container_mapper.RequiresKnowledgeBaseID(containerType)
if !requiresKnowledgeBaseID && req.KnowledgeBaseExternalId != nil {
return status.GenErrWithCustomMsg(status.StatusParamError, "KnowledgeBaseExternalId not nil")
}
if requiresKnowledgeBaseID && req.KnowledgeBaseExternalId == nil {
return status.GenErrWithCustomMsg(status.StatusParamError, "KnowledgeBaseExternalId is nil")
}
}
if req.MoveToContainer == nil && req.KnowledgeBaseExternalId != nil {
return status.GenErrWithCustomMsg(status.StatusParamError, "MoveToContainer is nil")
}
return nil
}