Commit d71fd2f8 authored by Steven's avatar Steven

refactor: auth service

parent 9972a77d
......@@ -4,26 +4,40 @@ package memos.api.v1;
import "api/v1/user_service.proto";
import "google/api/annotations.proto";
import "google/api/field_behavior.proto";
import "google/protobuf/empty.proto";
option go_package = "gen/api/v1";
service AuthService {
// GetAuthStatus returns the current auth status of the user.
// GetAuthStatus returns the current authentication status of the user.
// This method is idempotent and safe, suitable for checking authentication state.
rpc GetAuthStatus(GetAuthStatusRequest) returns (User) {
option (google.api.http) = {post: "/api/v1/auth/status"};
option (google.api.http) = {get: "/api/v1/auth/status"};
}
// SignIn signs in the user.
rpc SignIn(SignInRequest) returns (User) {
option (google.api.http) = {post: "/api/v1/auth/signin"};
// CreateSession authenticates a user and creates a new session.
// Returns the authenticated user information upon successful authentication.
rpc CreateSession(CreateSessionRequest) returns (User) {
option (google.api.http) = {
post: "/api/v1/auth/sessions"
body: "*"
};
}
// SignUp signs up the user with the given username and password.
rpc SignUp(SignUpRequest) returns (User) {
option (google.api.http) = {post: "/api/v1/auth/signup"};
// RegisterUser creates a new user account with username and password.
// Returns the newly created user information upon successful registration.
rpc RegisterUser(RegisterUserRequest) returns (User) {
option (google.api.http) = {
post: "/api/v1/auth/users"
body: "*"
};
}
// SignOut signs out the user.
rpc SignOut(SignOutRequest) returns (google.protobuf.Empty) {
option (google.api.http) = {post: "/api/v1/auth/signout"};
// DeleteSession terminates the current user session.
// This is an idempotent operation that invalidates the user's authentication.
rpc DeleteSession(DeleteSessionRequest) returns (google.protobuf.Empty) {
option (google.api.http) = {delete: "/api/v1/auth/sessions/current"};
}
}
......@@ -33,8 +47,9 @@ message GetAuthStatusResponse {
User user = 1;
}
message SignInRequest {
message CreateSessionRequest {
// Provide one authentication method (username/password or SSO).
// Required field to specify the authentication method.
oneof method {
// Username and password authentication method.
PasswordCredentials password_credentials = 1;
......@@ -42,31 +57,44 @@ message SignInRequest {
// SSO provider authentication method.
SSOCredentials sso_credentials = 2;
}
// Whether the session should never expire.
bool never_expire = 3;
// Optional field that defaults to false for security.
bool never_expire = 3 [(google.api.field_behavior) = OPTIONAL];
}
message PasswordCredentials {
// The username to sign in with.
string username = 1;
// Required field for password-based authentication.
string username = 1 [(google.api.field_behavior) = REQUIRED];
// The password to sign in with.
string password = 2;
// Required field for password-based authentication.
string password = 2 [(google.api.field_behavior) = REQUIRED];
}
message SSOCredentials {
// The ID of the SSO provider.
int32 idp_id = 1;
// The code to sign in with.
string code = 2;
// The redirect URI.
string redirect_uri = 3;
// Required field to identify the SSO provider.
int32 idp_id = 1 [(google.api.field_behavior) = REQUIRED];
// The authorization code from the SSO provider.
// Required field for completing the SSO flow.
string code = 2 [(google.api.field_behavior) = REQUIRED];
// The redirect URI used in the SSO flow.
// Required field for security validation.
string redirect_uri = 3 [(google.api.field_behavior) = REQUIRED];
}
message SignUpRequest {
message RegisterUserRequest {
// The username to sign up with.
string username = 1;
// Required field that must be unique across the system.
string username = 1 [(google.api.field_behavior) = REQUIRED];
// The password to sign up with.
string password = 2;
// Required field that should meet security requirements.
string password = 2 [(google.api.field_behavior) = REQUIRED];
}
message SignOutRequest {}
message DeleteSessionRequest {}
......@@ -3,68 +3,88 @@ syntax = "proto3";
package memos.api.v1;
import "google/api/annotations.proto";
import "google/api/field_behavior.proto";
option go_package = "gen/api/v1";
service MarkdownService {
// ParseMarkdown parses the given markdown content and returns a list of nodes.
// This is a utility method that transforms markdown text into structured nodes.
rpc ParseMarkdown(ParseMarkdownRequest) returns (ParseMarkdownResponse) {
option (google.api.http) = {
post: "/api/v1/markdown:parse"
body: "*"
};
}
// RestoreMarkdownNodes restores the given nodes to markdown content.
// This is the inverse operation of ParseMarkdown.
rpc RestoreMarkdownNodes(RestoreMarkdownNodesRequest) returns (RestoreMarkdownNodesResponse) {
option (google.api.http) = {
post: "/api/v1/markdown/node:restore"
post: "/api/v1/markdown:restore"
body: "*"
};
}
// StringifyMarkdownNodes stringify the given nodes to plain text content.
// This removes all markdown formatting and returns plain text.
rpc StringifyMarkdownNodes(StringifyMarkdownNodesRequest) returns (StringifyMarkdownNodesResponse) {
option (google.api.http) = {
post: "/api/v1/markdown/node:stringify"
post: "/api/v1/markdown:stringify"
body: "*"
};
}
// GetLinkMetadata returns metadata for a given link.
// This is useful for generating link previews.
rpc GetLinkMetadata(GetLinkMetadataRequest) returns (LinkMetadata) {
option (google.api.http) = {get: "/api/v1/markdown/link:metadata"};
option (google.api.http) = {get: "/api/v1/markdown/links:getMetadata"};
}
}
message ParseMarkdownRequest {
string markdown = 1;
// The markdown content to parse.
string markdown = 1 [(google.api.field_behavior) = REQUIRED];
}
message ParseMarkdownResponse {
// The parsed markdown nodes.
repeated Node nodes = 1;
}
message RestoreMarkdownNodesRequest {
repeated Node nodes = 1;
// The nodes to restore to markdown content.
repeated Node nodes = 1 [(google.api.field_behavior) = REQUIRED];
}
message RestoreMarkdownNodesResponse {
// The restored markdown content.
string markdown = 1;
}
message StringifyMarkdownNodesRequest {
repeated Node nodes = 1;
// The nodes to stringify to plain text.
repeated Node nodes = 1 [(google.api.field_behavior) = REQUIRED];
}
message StringifyMarkdownNodesResponse {
// The plain text content.
string plain_text = 1;
}
message GetLinkMetadataRequest {
string link = 1;
// The link URL to get metadata for.
string link = 1 [(google.api.field_behavior) = REQUIRED];
}
message LinkMetadata {
// The title of the linked page.
string title = 1;
// The description of the linked page.
string description = 2;
// The URL of the preview image for the linked page.
string image = 3;
}
......@@ -218,7 +238,10 @@ message TableNode {
}
message EmbeddedContentNode {
// The resource name of the embedded content.
string resource_name = 1;
// Additional parameters for the embedded content.
string params = 2;
}
......@@ -289,7 +312,10 @@ message SuperscriptNode {
}
message ReferencedContentNode {
// The resource name of the referenced content.
string resource_name = 1;
// Additional parameters for the referenced content.
string params = 2;
}
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -172,7 +172,7 @@ func RegisterMarkdownServiceHandlerServer(ctx context.Context, mux *runtime.Serv
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.MarkdownService/RestoreMarkdownNodes", runtime.WithHTTPPathPattern("/api/v1/markdown/node:restore"))
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.MarkdownService/RestoreMarkdownNodes", runtime.WithHTTPPathPattern("/api/v1/markdown:restore"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
......@@ -192,7 +192,7 @@ func RegisterMarkdownServiceHandlerServer(ctx context.Context, mux *runtime.Serv
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.MarkdownService/StringifyMarkdownNodes", runtime.WithHTTPPathPattern("/api/v1/markdown/node:stringify"))
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.MarkdownService/StringifyMarkdownNodes", runtime.WithHTTPPathPattern("/api/v1/markdown:stringify"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
......@@ -212,7 +212,7 @@ func RegisterMarkdownServiceHandlerServer(ctx context.Context, mux *runtime.Serv
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.MarkdownService/GetLinkMetadata", runtime.WithHTTPPathPattern("/api/v1/markdown/link:metadata"))
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.MarkdownService/GetLinkMetadata", runtime.WithHTTPPathPattern("/api/v1/markdown/links:getMetadata"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
......@@ -287,7 +287,7 @@ func RegisterMarkdownServiceHandlerClient(ctx context.Context, mux *runtime.Serv
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.MarkdownService/RestoreMarkdownNodes", runtime.WithHTTPPathPattern("/api/v1/markdown/node:restore"))
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.MarkdownService/RestoreMarkdownNodes", runtime.WithHTTPPathPattern("/api/v1/markdown:restore"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
......@@ -304,7 +304,7 @@ func RegisterMarkdownServiceHandlerClient(ctx context.Context, mux *runtime.Serv
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.MarkdownService/StringifyMarkdownNodes", runtime.WithHTTPPathPattern("/api/v1/markdown/node:stringify"))
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.MarkdownService/StringifyMarkdownNodes", runtime.WithHTTPPathPattern("/api/v1/markdown:stringify"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
......@@ -321,7 +321,7 @@ func RegisterMarkdownServiceHandlerClient(ctx context.Context, mux *runtime.Serv
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.MarkdownService/GetLinkMetadata", runtime.WithHTTPPathPattern("/api/v1/markdown/link:metadata"))
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.MarkdownService/GetLinkMetadata", runtime.WithHTTPPathPattern("/api/v1/markdown/links:getMetadata"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
......@@ -339,9 +339,9 @@ func RegisterMarkdownServiceHandlerClient(ctx context.Context, mux *runtime.Serv
var (
pattern_MarkdownService_ParseMarkdown_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "markdown"}, "parse"))
pattern_MarkdownService_RestoreMarkdownNodes_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "markdown", "node"}, "restore"))
pattern_MarkdownService_StringifyMarkdownNodes_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "markdown", "node"}, "stringify"))
pattern_MarkdownService_GetLinkMetadata_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "markdown", "link"}, "metadata"))
pattern_MarkdownService_RestoreMarkdownNodes_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "markdown"}, "restore"))
pattern_MarkdownService_StringifyMarkdownNodes_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "markdown"}, "stringify"))
pattern_MarkdownService_GetLinkMetadata_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "markdown", "links"}, "getMetadata"))
)
var (
......
......@@ -30,12 +30,16 @@ const (
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type MarkdownServiceClient interface {
// ParseMarkdown parses the given markdown content and returns a list of nodes.
// This is a utility method that transforms markdown text into structured nodes.
ParseMarkdown(ctx context.Context, in *ParseMarkdownRequest, opts ...grpc.CallOption) (*ParseMarkdownResponse, error)
// RestoreMarkdownNodes restores the given nodes to markdown content.
// This is the inverse operation of ParseMarkdown.
RestoreMarkdownNodes(ctx context.Context, in *RestoreMarkdownNodesRequest, opts ...grpc.CallOption) (*RestoreMarkdownNodesResponse, error)
// StringifyMarkdownNodes stringify the given nodes to plain text content.
// This removes all markdown formatting and returns plain text.
StringifyMarkdownNodes(ctx context.Context, in *StringifyMarkdownNodesRequest, opts ...grpc.CallOption) (*StringifyMarkdownNodesResponse, error)
// GetLinkMetadata returns metadata for a given link.
// This is useful for generating link previews.
GetLinkMetadata(ctx context.Context, in *GetLinkMetadataRequest, opts ...grpc.CallOption) (*LinkMetadata, error)
}
......@@ -92,12 +96,16 @@ func (c *markdownServiceClient) GetLinkMetadata(ctx context.Context, in *GetLink
// for forward compatibility.
type MarkdownServiceServer interface {
// ParseMarkdown parses the given markdown content and returns a list of nodes.
// This is a utility method that transforms markdown text into structured nodes.
ParseMarkdown(context.Context, *ParseMarkdownRequest) (*ParseMarkdownResponse, error)
// RestoreMarkdownNodes restores the given nodes to markdown content.
// This is the inverse operation of ParseMarkdown.
RestoreMarkdownNodes(context.Context, *RestoreMarkdownNodesRequest) (*RestoreMarkdownNodesResponse, error)
// StringifyMarkdownNodes stringify the given nodes to plain text content.
// This removes all markdown formatting and returns plain text.
StringifyMarkdownNodes(context.Context, *StringifyMarkdownNodesRequest) (*StringifyMarkdownNodesResponse, error)
// GetLinkMetadata returns metadata for a given link.
// This is useful for generating link previews.
GetLinkMetadata(context.Context, *GetLinkMetadataRequest) (*LinkMetadata, error)
mustEmbedUnimplementedMarkdownServiceServer()
}
......
This diff is collapsed.
......@@ -6,10 +6,9 @@ var authenticationAllowlistMethods = map[string]bool{
"/memos.api.v1.IdentityProviderService/GetIdentityProvider": true,
"/memos.api.v1.IdentityProviderService/ListIdentityProviders": true,
"/memos.api.v1.AuthService/GetAuthStatus": true,
"/memos.api.v1.AuthService/SignIn": true,
"/memos.api.v1.AuthService/SignInWithSSO": true,
"/memos.api.v1.AuthService/SignOut": true,
"/memos.api.v1.AuthService/SignUp": true,
"/memos.api.v1.AuthService/CreateSession": true,
"/memos.api.v1.AuthService/RegisterUser": true,
"/memos.api.v1.AuthService/DeleteSession": true,
"/memos.api.v1.UserService/GetUser": true,
"/memos.api.v1.UserService/GetUserAvatar": true,
"/memos.api.v1.UserService/GetUserStats": true,
......
......@@ -44,7 +44,7 @@ func (s *APIV1Service) GetAuthStatus(ctx context.Context, _ *v1pb.GetAuthStatusR
return convertUserFromStore(user), nil
}
func (s *APIV1Service) SignIn(ctx context.Context, request *v1pb.SignInRequest) (*v1pb.User, error) {
func (s *APIV1Service) CreateSession(ctx context.Context, request *v1pb.CreateSessionRequest) (*v1pb.User, error) {
var existingUser *store.User
if passwordCredentials := request.GetPasswordCredentials(); passwordCredentials != nil {
user, err := s.Store.GetUser(ctx, &store.FindUser{
......@@ -189,7 +189,7 @@ func (s *APIV1Service) doSignIn(ctx context.Context, user *store.User, expireTim
return nil
}
func (s *APIV1Service) SignUp(ctx context.Context, request *v1pb.SignUpRequest) (*v1pb.User, error) {
func (s *APIV1Service) RegisterUser(ctx context.Context, request *v1pb.RegisterUserRequest) (*v1pb.User, error) {
workspaceGeneralSetting, err := s.Store.GetWorkspaceGeneralSetting(ctx)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get workspace general setting, error: %v", err)
......@@ -237,7 +237,7 @@ func (s *APIV1Service) SignUp(ctx context.Context, request *v1pb.SignUpRequest)
return convertUserFromStore(user), nil
}
func (s *APIV1Service) SignOut(ctx context.Context, _ *v1pb.SignOutRequest) (*emptypb.Empty, error) {
func (s *APIV1Service) DeleteSession(ctx context.Context, _ *v1pb.DeleteSessionRequest) (*emptypb.Empty, error) {
accessToken, ok := ctx.Value(accessTokenContextKey).(string)
// Try to delete the access token from the store.
if ok {
......
......@@ -45,7 +45,7 @@ const PasswordSignInForm = observer(() => {
try {
actionBtnLoadingState.setLoading();
await authServiceClient.signIn({ passwordCredentials: { username, password }, neverExpire: remember });
await authServiceClient.createSession({ passwordCredentials: { username, password }, neverExpire: remember });
await initialUserStore();
navigateTo("/");
} catch (error: any) {
......
......@@ -19,7 +19,7 @@ const UserBanner = (props: Props) => {
const currentUser = useCurrentUser();
const handleSignOut = async () => {
await authServiceClient.signOut({});
await authServiceClient.deleteSession({});
window.location.href = Routes.AUTH;
};
......
......@@ -46,7 +46,7 @@ const AuthCallback = observer(() => {
const redirectUri = absolutifyLink("/auth/callback");
(async () => {
try {
await authServiceClient.signIn({
await authServiceClient.createSession({
ssoCredentials: {
idpId: identityProviderId,
code,
......
......@@ -47,7 +47,7 @@ const SignUp = observer(() => {
try {
actionBtnLoadingState.setLoading();
await authServiceClient.signUp({ username, password });
await authServiceClient.registerUser({ username, password });
await initialUserStore();
navigateTo("/");
} catch (error: any) {
......
This diff is collapsed.
......@@ -225,36 +225,46 @@ export function nodeTypeToNumber(object: NodeType): number {
}
export interface ParseMarkdownRequest {
/** The markdown content to parse. */
markdown: string;
}
export interface ParseMarkdownResponse {
/** The parsed markdown nodes. */
nodes: Node[];
}
export interface RestoreMarkdownNodesRequest {
/** The nodes to restore to markdown content. */
nodes: Node[];
}
export interface RestoreMarkdownNodesResponse {
/** The restored markdown content. */
markdown: string;
}
export interface StringifyMarkdownNodesRequest {
/** The nodes to stringify to plain text. */
nodes: Node[];
}
export interface StringifyMarkdownNodesResponse {
/** The plain text content. */
plainText: string;
}
export interface GetLinkMetadataRequest {
/** The link URL to get metadata for. */
link: string;
}
export interface LinkMetadata {
/** The title of the linked page. */
title: string;
/** The description of the linked page. */
description: string;
/** The URL of the preview image for the linked page. */
image: string;
}
......@@ -407,7 +417,9 @@ export interface TableNode_Row {
}
export interface EmbeddedContentNode {
/** The resource name of the embedded content. */
resourceName: string;
/** Additional parameters for the embedded content. */
params: string;
}
......@@ -478,7 +490,9 @@ export interface SuperscriptNode {
}
export interface ReferencedContentNode {
/** The resource name of the referenced content. */
resourceName: string;
/** Additional parameters for the referenced content. */
params: string;
}
......@@ -3202,7 +3216,10 @@ export const MarkdownServiceDefinition = {
name: "MarkdownService",
fullName: "memos.api.v1.MarkdownService",
methods: {
/** ParseMarkdown parses the given markdown content and returns a list of nodes. */
/**
* ParseMarkdown parses the given markdown content and returns a list of nodes.
* This is a utility method that transforms markdown text into structured nodes.
*/
parseMarkdown: {
name: "ParseMarkdown",
requestType: ParseMarkdownRequest,
......@@ -3246,7 +3263,10 @@ export const MarkdownServiceDefinition = {
},
},
},
/** RestoreMarkdownNodes restores the given nodes to markdown content. */
/**
* RestoreMarkdownNodes restores the given nodes to markdown content.
* This is the inverse operation of ParseMarkdown.
*/
restoreMarkdownNodes: {
name: "RestoreMarkdownNodes",
requestType: RestoreMarkdownNodesRequest,
......@@ -3257,12 +3277,12 @@ export const MarkdownServiceDefinition = {
_unknownFields: {
578365826: [
new Uint8Array([
34,
29,
58,
1,
42,
34,
29,
24,
47,
97,
112,
......@@ -3279,11 +3299,6 @@ export const MarkdownServiceDefinition = {
111,
119,
110,
47,
110,
111,
100,
101,
58,
114,
101,
......@@ -3297,7 +3312,10 @@ export const MarkdownServiceDefinition = {
},
},
},
/** StringifyMarkdownNodes stringify the given nodes to plain text content. */
/**
* StringifyMarkdownNodes stringify the given nodes to plain text content.
* This removes all markdown formatting and returns plain text.
*/
stringifyMarkdownNodes: {
name: "StringifyMarkdownNodes",
requestType: StringifyMarkdownNodesRequest,
......@@ -3308,12 +3326,12 @@ export const MarkdownServiceDefinition = {
_unknownFields: {
578365826: [
new Uint8Array([
36,
31,
58,
1,
42,
34,
31,
26,
47,
97,
112,
......@@ -3330,11 +3348,6 @@ export const MarkdownServiceDefinition = {
111,
119,
110,
47,
110,
111,
100,
101,
58,
115,
116,
......@@ -3350,7 +3363,10 @@ export const MarkdownServiceDefinition = {
},
},
},
/** GetLinkMetadata returns metadata for a given link. */
/**
* GetLinkMetadata returns metadata for a given link.
* This is useful for generating link previews.
*/
getLinkMetadata: {
name: "GetLinkMetadata",
requestType: GetLinkMetadataRequest,
......@@ -3361,9 +3377,9 @@ export const MarkdownServiceDefinition = {
_unknownFields: {
578365826: [
new Uint8Array([
32,
36,
18,
30,
34,
47,
97,
112,
......@@ -3385,8 +3401,12 @@ export const MarkdownServiceDefinition = {
105,
110,
107,
115,
58,
109,
103,
101,
116,
77,
101,
116,
97,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment