Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
canifa_note
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Vũ Hoàng Anh
canifa_note
Commits
62959795
Commit
62959795
authored
Apr 30, 2024
by
Steven
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
chore: add user avatar route
parent
cac6f427
Changes
10
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
867 additions
and
380 deletions
+867
-380
main.go
bin/memos/main.go
+8
-7
apidocs.swagger.yaml
docs/apidocs.swagger.yaml
+212
-0
user_service.proto
proto/api/v1/user_service.proto
+15
-0
user_service.pb.go
proto/gen/api/v1/user_service.pb.go
+411
-320
user_service.pb.gw.go
proto/gen/api/v1/user_service.pb.gw.go
+121
-0
user_service_grpc.pb.go
proto/gen/api/v1/user_service_grpc.pb.go
+40
-0
acl_config.go
server/route/api/v1/acl_config.go
+1
-0
user_service.go
server/route/api/v1/user_service.go
+47
-0
frontend.go
server/route/frontend/frontend.go
+5
-7
server.go
server/server.go
+7
-46
No files found.
bin/memos/main.go
View file @
62959795
...
@@ -76,13 +76,6 @@ var (
...
@@ -76,13 +76,6 @@ var (
// The default signal sent by the `kill` command is SIGTERM,
// The default signal sent by the `kill` command is SIGTERM,
// which is taken as the graceful shutdown signal for many systems, eg., Kubernetes, Gunicorn.
// which is taken as the graceful shutdown signal for many systems, eg., Kubernetes, Gunicorn.
signal
.
Notify
(
c
,
os
.
Interrupt
,
syscall
.
SIGTERM
)
signal
.
Notify
(
c
,
os
.
Interrupt
,
syscall
.
SIGTERM
)
go
func
()
{
<-
c
s
.
Shutdown
(
ctx
)
cancel
()
}()
printGreetings
()
if
err
:=
s
.
Start
(
ctx
);
err
!=
nil
{
if
err
:=
s
.
Start
(
ctx
);
err
!=
nil
{
if
err
!=
http
.
ErrServerClosed
{
if
err
!=
http
.
ErrServerClosed
{
...
@@ -91,6 +84,14 @@ var (
...
@@ -91,6 +84,14 @@ var (
}
}
}
}
printGreetings
()
go
func
()
{
<-
c
s
.
Shutdown
(
ctx
)
cancel
()
}()
// Wait for CTRL-C.
// Wait for CTRL-C.
<-
ctx
.
Done
()
<-
ctx
.
Done
()
},
},
...
...
docs/apidocs.swagger.yaml
View file @
62959795
...
@@ -1371,6 +1371,41 @@ paths:
...
@@ -1371,6 +1371,41 @@ paths:
type
:
string
type
:
string
tags
:
tags
:
-
UserService
-
UserService
/api/v1/{name}/avatar
:
get
:
summary
:
GetUserAvatar gets the avatar of a user.
operationId
:
UserService_GetUserAvatar
responses
:
"
200"
:
description
:
A successful response.
schema
:
$ref
:
'
#/definitions/apiHttpBody'
default
:
description
:
An unexpected error response.
schema
:
$ref
:
'
#/definitions/googlerpcStatus'
parameters
:
-
name
:
name
description
:
|-
The name of the user.
Format: users/{id}
in: path
required: true
type: string
pattern: users/[^/]+
-
name
:
httpBody.contentType
description
:
The HTTP Content-Type header value specifying the content type of the body.
in
:
query
required
:
false
type
:
string
-
name
:
httpBody.data
description
:
The HTTP request/response body as raw binary.
in
:
query
required
:
false
type
:
string
format
:
byte
tags
:
-
UserService
/api/v1/{name}/comments
:
/api/v1/{name}/comments
:
get
:
get
:
summary
:
ListMemoComments lists comments for a memo.
summary
:
ListMemoComments lists comments for a memo.
...
@@ -1800,6 +1835,68 @@ definitions:
...
@@ -1800,6 +1835,68 @@ definitions:
expiresAt
:
expiresAt
:
type
:
string
type
:
string
format
:
date-time
format
:
date-time
apiHttpBody
:
type
:
object
properties
:
contentType
:
type
:
string
description
:
The HTTP Content-Type header value specifying the content type of the body.
data
:
type
:
string
format
:
byte
description
:
The HTTP request/response body as raw binary.
extensions
:
type
:
array
items
:
type
:
object
$ref
:
'
#/definitions/protobufAny'
description
:
|-
Application specific response metadata. Must be set in the first response
for streaming APIs.
description
:
|-
Message that represents an arbitrary HTTP body. It should only be used for
payload formats that can't be represented as JSON, such as raw binary or
an HTML page.
This message can be used both in streaming and non-streaming API methods in
the request as well as the response.
It can be used as a top-level request field, which is convenient if one
wants to extract parameters from either the URL or HTTP template into the
request fields and also want access to the raw HTTP body.
Example:
message GetResourceRequest {
// A unique request id.
string request_id = 1;
// The raw HTTP body is bound to this field.
google.api.HttpBody http_body = 2;
}
service ResourceService {
rpc GetResource(GetResourceRequest)
returns (google.api.HttpBody);
rpc UpdateResource(google.api.HttpBody)
returns (google.protobuf.Empty);
}
Example with streaming methods:
service CaldavService {
rpc GetCalendar(stream google.api.HttpBody)
returns (stream google.api.HttpBody);
rpc UpdateCalendar(stream google.api.HttpBody)
returns (stream google.api.HttpBody);
}
Use of this type only changes how the request and response bodies are
handled, all other features will continue to work unchanged.
apiv1ActivityMemoCommentPayload
:
apiv1ActivityMemoCommentPayload
:
type
:
object
type
:
object
properties
:
properties
:
...
@@ -2016,7 +2113,122 @@ definitions:
...
@@ -2016,7 +2113,122 @@ definitions:
properties
:
properties
:
'
@type'
:
'
@type'
:
type
:
string
type
:
string
description
:
|-
A URL/resource name that uniquely identifies the type of the serialized
protocol buffer message. This string must contain at least
one "/" character. The last segment of the URL's path must represent
the fully qualified name of the type (as in
`path/google.protobuf.Duration`). The name should be in a canonical form
(e.g., leading "." is not accepted).
In practice, teams usually precompile into the binary all types that they
expect it to use in the context of Any. However, for URLs which use the
scheme `http`, `https`, or no scheme, one can optionally set up a type
server that maps type URLs to message definitions as follows:
* If no scheme is provided, `https` is assumed.
* An HTTP GET on the URL must yield a [google.protobuf.Type][]
value in binary format, or produce an error.
* Applications are allowed to cache lookup results based on the
URL, or have them precompiled into a binary to avoid any
lookup. Therefore, binary compatibility needs to be preserved
on changes to types. (Use versioned type names to manage
breaking changes.)
Note: this functionality is not currently available in the official
protobuf release, and it is not used for type URLs beginning with
type.googleapis.com. As of May 2023, there are no widely used type server
implementations and no plans to implement one.
Schemes other than `http`, `https` (or the empty scheme) might be
used with implementation specific semantics.
additionalProperties
:
{}
additionalProperties
:
{}
description
:
|-
`Any` contains an arbitrary serialized protocol buffer message along with a
URL that describes the type of the serialized message.
Protobuf library provides support to pack/unpack Any values in the form
of utility functions or additional generated methods of the Any type.
Example 1: Pack and unpack a message in C++.
Foo foo = ...;
Any any;
any.PackFrom(foo);
...
if (any.UnpackTo(&foo)) {
...
}
Example 2: Pack and unpack a message in Java.
Foo foo = ...;
Any any = Any.pack(foo);
...
if (any.is(Foo.class)) {
foo = any.unpack(Foo.class);
}
// or ...
if (any.isSameTypeAs(Foo.getDefaultInstance())) {
foo = any.unpack(Foo.getDefaultInstance());
}
Example 3: Pack and unpack a message in Python.
foo = Foo(...)
any = Any()
any.Pack(foo)
...
if any.Is(Foo.DESCRIPTOR):
any.Unpack(foo)
...
Example 4: Pack and unpack a message in Go
foo := &pb.Foo{...}
any, err := anypb.New(foo)
if err != nil {
...
}
...
foo := &pb.Foo{}
if err := any.UnmarshalTo(foo); err != nil {
...
}
The pack methods provided by protobuf library will by default use
'type.googleapis.com/full.type.name' as the type URL and the unpack
methods only use the fully qualified type name after the last '/'
in the type URL, for example "foo.bar.com/x/y.z" will yield type
name "y.z".
JSON
====
The JSON representation of an `Any` value uses the regular
representation of the deserialized, embedded message, with an
additional field `@type` which contains the type URL. Example:
package google.profile;
message Person {
string first_name = 1;
string last_name = 2;
}
{
"@type": "type.googleapis.com/google.profile.Person",
"firstName": <string>,
"lastName": <string>
}
If the embedded message type is well-known and has a custom JSON
representation, that representation will be embedded adding a field
`value` which holds the custom JSON in addition to the `@type`
field. Example (for message [google.protobuf.Duration][]):
{
"@type": "type.googleapis.com/google.protobuf.Duration",
"value": "1.212s"
}
v1Activity
:
v1Activity
:
type
:
object
type
:
object
properties
:
properties
:
...
...
proto/api/v1/user_service.proto
View file @
62959795
...
@@ -6,6 +6,7 @@ import "api/v1/common.proto";
...
@@ -6,6 +6,7 @@ import "api/v1/common.proto";
import
"google/api/annotations.proto"
;
import
"google/api/annotations.proto"
;
import
"google/api/client.proto"
;
import
"google/api/client.proto"
;
import
"google/api/field_behavior.proto"
;
import
"google/api/field_behavior.proto"
;
import
"google/api/httpbody.proto"
;
import
"google/protobuf/empty.proto"
;
import
"google/protobuf/empty.proto"
;
import
"google/protobuf/field_mask.proto"
;
import
"google/protobuf/field_mask.proto"
;
import
"google/protobuf/timestamp.proto"
;
import
"google/protobuf/timestamp.proto"
;
...
@@ -26,6 +27,11 @@ service UserService {
...
@@ -26,6 +27,11 @@ service UserService {
option
(
google.api.http
)
=
{
get
:
"/api/v1/{name=users/*}"
};
option
(
google.api.http
)
=
{
get
:
"/api/v1/{name=users/*}"
};
option
(
google.api.method_signature
)
=
"name"
;
option
(
google.api.method_signature
)
=
"name"
;
}
}
// GetUserAvatar gets the avatar of a user.
rpc
GetUserAvatar
(
GetUserAvatarRequest
)
returns
(
google.api.HttpBody
)
{
option
(
google.api.http
)
=
{
get
:
"/api/v1/{name=users/*}/avatar"
};
option
(
google.api.method_signature
)
=
"name"
;
}
// CreateUser creates a new user.
// CreateUser creates a new user.
rpc
CreateUser
(
CreateUserRequest
)
returns
(
User
)
{
rpc
CreateUser
(
CreateUserRequest
)
returns
(
User
)
{
option
(
google.api.http
)
=
{
option
(
google.api.http
)
=
{
...
@@ -137,6 +143,15 @@ message GetUserRequest {
...
@@ -137,6 +143,15 @@ message GetUserRequest {
string
name
=
1
;
string
name
=
1
;
}
}
message
GetUserAvatarRequest
{
// The name of the user.
// Format: users/{id}
string
name
=
1
;
// The raw HTTP body is bound to this field.
google.api.HttpBody
http_body
=
2
;
}
message
CreateUserRequest
{
message
CreateUserRequest
{
User
user
=
1
;
User
user
=
1
;
}
}
...
...
proto/gen/api/v1/user_service.pb.go
View file @
62959795
This diff is collapsed.
Click to expand it.
proto/gen/api/v1/user_service.pb.gw.go
View file @
62959795
...
@@ -137,6 +137,76 @@ func local_request_UserService_GetUser_0(ctx context.Context, marshaler runtime.
...
@@ -137,6 +137,76 @@ func local_request_UserService_GetUser_0(ctx context.Context, marshaler runtime.
}
}
var
(
filter_UserService_GetUserAvatar_0
=
&
utilities
.
DoubleArray
{
Encoding
:
map
[
string
]
int
{
"name"
:
0
},
Base
:
[]
int
{
1
,
1
,
0
},
Check
:
[]
int
{
0
,
1
,
2
}}
)
func
request_UserService_GetUserAvatar_0
(
ctx
context
.
Context
,
marshaler
runtime
.
Marshaler
,
client
UserServiceClient
,
req
*
http
.
Request
,
pathParams
map
[
string
]
string
)
(
proto
.
Message
,
runtime
.
ServerMetadata
,
error
)
{
var
protoReq
GetUserAvatarRequest
var
metadata
runtime
.
ServerMetadata
var
(
val
string
ok
bool
err
error
_
=
err
)
val
,
ok
=
pathParams
[
"name"
]
if
!
ok
{
return
nil
,
metadata
,
status
.
Errorf
(
codes
.
InvalidArgument
,
"missing parameter %s"
,
"name"
)
}
protoReq
.
Name
,
err
=
runtime
.
String
(
val
)
if
err
!=
nil
{
return
nil
,
metadata
,
status
.
Errorf
(
codes
.
InvalidArgument
,
"type mismatch, parameter: %s, error: %v"
,
"name"
,
err
)
}
if
err
:=
req
.
ParseForm
();
err
!=
nil
{
return
nil
,
metadata
,
status
.
Errorf
(
codes
.
InvalidArgument
,
"%v"
,
err
)
}
if
err
:=
runtime
.
PopulateQueryParameters
(
&
protoReq
,
req
.
Form
,
filter_UserService_GetUserAvatar_0
);
err
!=
nil
{
return
nil
,
metadata
,
status
.
Errorf
(
codes
.
InvalidArgument
,
"%v"
,
err
)
}
msg
,
err
:=
client
.
GetUserAvatar
(
ctx
,
&
protoReq
,
grpc
.
Header
(
&
metadata
.
HeaderMD
),
grpc
.
Trailer
(
&
metadata
.
TrailerMD
))
return
msg
,
metadata
,
err
}
func
local_request_UserService_GetUserAvatar_0
(
ctx
context
.
Context
,
marshaler
runtime
.
Marshaler
,
server
UserServiceServer
,
req
*
http
.
Request
,
pathParams
map
[
string
]
string
)
(
proto
.
Message
,
runtime
.
ServerMetadata
,
error
)
{
var
protoReq
GetUserAvatarRequest
var
metadata
runtime
.
ServerMetadata
var
(
val
string
ok
bool
err
error
_
=
err
)
val
,
ok
=
pathParams
[
"name"
]
if
!
ok
{
return
nil
,
metadata
,
status
.
Errorf
(
codes
.
InvalidArgument
,
"missing parameter %s"
,
"name"
)
}
protoReq
.
Name
,
err
=
runtime
.
String
(
val
)
if
err
!=
nil
{
return
nil
,
metadata
,
status
.
Errorf
(
codes
.
InvalidArgument
,
"type mismatch, parameter: %s, error: %v"
,
"name"
,
err
)
}
if
err
:=
req
.
ParseForm
();
err
!=
nil
{
return
nil
,
metadata
,
status
.
Errorf
(
codes
.
InvalidArgument
,
"%v"
,
err
)
}
if
err
:=
runtime
.
PopulateQueryParameters
(
&
protoReq
,
req
.
Form
,
filter_UserService_GetUserAvatar_0
);
err
!=
nil
{
return
nil
,
metadata
,
status
.
Errorf
(
codes
.
InvalidArgument
,
"%v"
,
err
)
}
msg
,
err
:=
server
.
GetUserAvatar
(
ctx
,
&
protoReq
)
return
msg
,
metadata
,
err
}
func
request_UserService_CreateUser_0
(
ctx
context
.
Context
,
marshaler
runtime
.
Marshaler
,
client
UserServiceClient
,
req
*
http
.
Request
,
pathParams
map
[
string
]
string
)
(
proto
.
Message
,
runtime
.
ServerMetadata
,
error
)
{
func
request_UserService_CreateUser_0
(
ctx
context
.
Context
,
marshaler
runtime
.
Marshaler
,
client
UserServiceClient
,
req
*
http
.
Request
,
pathParams
map
[
string
]
string
)
(
proto
.
Message
,
runtime
.
ServerMetadata
,
error
)
{
var
protoReq
CreateUserRequest
var
protoReq
CreateUserRequest
var
metadata
runtime
.
ServerMetadata
var
metadata
runtime
.
ServerMetadata
...
@@ -732,6 +802,31 @@ func RegisterUserServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
...
@@ -732,6 +802,31 @@ func RegisterUserServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
})
})
mux
.
Handle
(
"GET"
,
pattern_UserService_GetUserAvatar_0
,
func
(
w
http
.
ResponseWriter
,
req
*
http
.
Request
,
pathParams
map
[
string
]
string
)
{
ctx
,
cancel
:=
context
.
WithCancel
(
req
.
Context
())
defer
cancel
()
var
stream
runtime
.
ServerTransportStream
ctx
=
grpc
.
NewContextWithServerTransportStream
(
ctx
,
&
stream
)
inboundMarshaler
,
outboundMarshaler
:=
runtime
.
MarshalerForRequest
(
mux
,
req
)
var
err
error
var
annotatedContext
context
.
Context
annotatedContext
,
err
=
runtime
.
AnnotateIncomingContext
(
ctx
,
mux
,
req
,
"/memos.api.v1.UserService/GetUserAvatar"
,
runtime
.
WithHTTPPathPattern
(
"/api/v1/{name=users/*}/avatar"
))
if
err
!=
nil
{
runtime
.
HTTPError
(
ctx
,
mux
,
outboundMarshaler
,
w
,
req
,
err
)
return
}
resp
,
md
,
err
:=
local_request_UserService_GetUserAvatar_0
(
annotatedContext
,
inboundMarshaler
,
server
,
req
,
pathParams
)
md
.
HeaderMD
,
md
.
TrailerMD
=
metadata
.
Join
(
md
.
HeaderMD
,
stream
.
Header
()),
metadata
.
Join
(
md
.
TrailerMD
,
stream
.
Trailer
())
annotatedContext
=
runtime
.
NewServerMetadataContext
(
annotatedContext
,
md
)
if
err
!=
nil
{
runtime
.
HTTPError
(
annotatedContext
,
mux
,
outboundMarshaler
,
w
,
req
,
err
)
return
}
forward_UserService_GetUserAvatar_0
(
annotatedContext
,
mux
,
outboundMarshaler
,
w
,
req
,
resp
,
mux
.
GetForwardResponseOptions
()
...
)
})
mux
.
Handle
(
"POST"
,
pattern_UserService_CreateUser_0
,
func
(
w
http
.
ResponseWriter
,
req
*
http
.
Request
,
pathParams
map
[
string
]
string
)
{
mux
.
Handle
(
"POST"
,
pattern_UserService_CreateUser_0
,
func
(
w
http
.
ResponseWriter
,
req
*
http
.
Request
,
pathParams
map
[
string
]
string
)
{
ctx
,
cancel
:=
context
.
WithCancel
(
req
.
Context
())
ctx
,
cancel
:=
context
.
WithCancel
(
req
.
Context
())
defer
cancel
()
defer
cancel
()
...
@@ -1039,6 +1134,28 @@ func RegisterUserServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux
...
@@ -1039,6 +1134,28 @@ func RegisterUserServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux
})
})
mux
.
Handle
(
"GET"
,
pattern_UserService_GetUserAvatar_0
,
func
(
w
http
.
ResponseWriter
,
req
*
http
.
Request
,
pathParams
map
[
string
]
string
)
{
ctx
,
cancel
:=
context
.
WithCancel
(
req
.
Context
())
defer
cancel
()
inboundMarshaler
,
outboundMarshaler
:=
runtime
.
MarshalerForRequest
(
mux
,
req
)
var
err
error
var
annotatedContext
context
.
Context
annotatedContext
,
err
=
runtime
.
AnnotateContext
(
ctx
,
mux
,
req
,
"/memos.api.v1.UserService/GetUserAvatar"
,
runtime
.
WithHTTPPathPattern
(
"/api/v1/{name=users/*}/avatar"
))
if
err
!=
nil
{
runtime
.
HTTPError
(
ctx
,
mux
,
outboundMarshaler
,
w
,
req
,
err
)
return
}
resp
,
md
,
err
:=
request_UserService_GetUserAvatar_0
(
annotatedContext
,
inboundMarshaler
,
client
,
req
,
pathParams
)
annotatedContext
=
runtime
.
NewServerMetadataContext
(
annotatedContext
,
md
)
if
err
!=
nil
{
runtime
.
HTTPError
(
annotatedContext
,
mux
,
outboundMarshaler
,
w
,
req
,
err
)
return
}
forward_UserService_GetUserAvatar_0
(
annotatedContext
,
mux
,
outboundMarshaler
,
w
,
req
,
resp
,
mux
.
GetForwardResponseOptions
()
...
)
})
mux
.
Handle
(
"POST"
,
pattern_UserService_CreateUser_0
,
func
(
w
http
.
ResponseWriter
,
req
*
http
.
Request
,
pathParams
map
[
string
]
string
)
{
mux
.
Handle
(
"POST"
,
pattern_UserService_CreateUser_0
,
func
(
w
http
.
ResponseWriter
,
req
*
http
.
Request
,
pathParams
map
[
string
]
string
)
{
ctx
,
cancel
:=
context
.
WithCancel
(
req
.
Context
())
ctx
,
cancel
:=
context
.
WithCancel
(
req
.
Context
())
defer
cancel
()
defer
cancel
()
...
@@ -1225,6 +1342,8 @@ var (
...
@@ -1225,6 +1342,8 @@ var (
pattern_UserService_GetUser_0
=
runtime
.
MustPattern
(
runtime
.
NewPattern
(
1
,
[]
int
{
2
,
0
,
2
,
1
,
2
,
2
,
1
,
0
,
4
,
2
,
5
,
3
},
[]
string
{
"api"
,
"v1"
,
"users"
,
"name"
},
""
))
pattern_UserService_GetUser_0
=
runtime
.
MustPattern
(
runtime
.
NewPattern
(
1
,
[]
int
{
2
,
0
,
2
,
1
,
2
,
2
,
1
,
0
,
4
,
2
,
5
,
3
},
[]
string
{
"api"
,
"v1"
,
"users"
,
"name"
},
""
))
pattern_UserService_GetUserAvatar_0
=
runtime
.
MustPattern
(
runtime
.
NewPattern
(
1
,
[]
int
{
2
,
0
,
2
,
1
,
2
,
2
,
1
,
0
,
4
,
2
,
5
,
3
,
2
,
4
},
[]
string
{
"api"
,
"v1"
,
"users"
,
"name"
,
"avatar"
},
""
))
pattern_UserService_CreateUser_0
=
runtime
.
MustPattern
(
runtime
.
NewPattern
(
1
,
[]
int
{
2
,
0
,
2
,
1
,
2
,
2
},
[]
string
{
"api"
,
"v1"
,
"users"
},
""
))
pattern_UserService_CreateUser_0
=
runtime
.
MustPattern
(
runtime
.
NewPattern
(
1
,
[]
int
{
2
,
0
,
2
,
1
,
2
,
2
},
[]
string
{
"api"
,
"v1"
,
"users"
},
""
))
pattern_UserService_UpdateUser_0
=
runtime
.
MustPattern
(
runtime
.
NewPattern
(
1
,
[]
int
{
2
,
0
,
2
,
1
,
2
,
2
,
1
,
0
,
4
,
2
,
5
,
3
},
[]
string
{
"api"
,
"v1"
,
"users"
,
"user.name"
},
""
))
pattern_UserService_UpdateUser_0
=
runtime
.
MustPattern
(
runtime
.
NewPattern
(
1
,
[]
int
{
2
,
0
,
2
,
1
,
2
,
2
,
1
,
0
,
4
,
2
,
5
,
3
},
[]
string
{
"api"
,
"v1"
,
"users"
,
"user.name"
},
""
))
...
@@ -1249,6 +1368,8 @@ var (
...
@@ -1249,6 +1368,8 @@ var (
forward_UserService_GetUser_0
=
runtime
.
ForwardResponseMessage
forward_UserService_GetUser_0
=
runtime
.
ForwardResponseMessage
forward_UserService_GetUserAvatar_0
=
runtime
.
ForwardResponseMessage
forward_UserService_CreateUser_0
=
runtime
.
ForwardResponseMessage
forward_UserService_CreateUser_0
=
runtime
.
ForwardResponseMessage
forward_UserService_UpdateUser_0
=
runtime
.
ForwardResponseMessage
forward_UserService_UpdateUser_0
=
runtime
.
ForwardResponseMessage
...
...
proto/gen/api/v1/user_service_grpc.pb.go
View file @
62959795
...
@@ -8,6 +8,7 @@ package apiv1
...
@@ -8,6 +8,7 @@ package apiv1
import
(
import
(
context
"context"
context
"context"
httpbody
"google.golang.org/genproto/googleapis/api/httpbody"
grpc
"google.golang.org/grpc"
grpc
"google.golang.org/grpc"
codes
"google.golang.org/grpc/codes"
codes
"google.golang.org/grpc/codes"
status
"google.golang.org/grpc/status"
status
"google.golang.org/grpc/status"
...
@@ -23,6 +24,7 @@ const (
...
@@ -23,6 +24,7 @@ const (
UserService_ListUsers_FullMethodName
=
"/memos.api.v1.UserService/ListUsers"
UserService_ListUsers_FullMethodName
=
"/memos.api.v1.UserService/ListUsers"
UserService_SearchUsers_FullMethodName
=
"/memos.api.v1.UserService/SearchUsers"
UserService_SearchUsers_FullMethodName
=
"/memos.api.v1.UserService/SearchUsers"
UserService_GetUser_FullMethodName
=
"/memos.api.v1.UserService/GetUser"
UserService_GetUser_FullMethodName
=
"/memos.api.v1.UserService/GetUser"
UserService_GetUserAvatar_FullMethodName
=
"/memos.api.v1.UserService/GetUserAvatar"
UserService_CreateUser_FullMethodName
=
"/memos.api.v1.UserService/CreateUser"
UserService_CreateUser_FullMethodName
=
"/memos.api.v1.UserService/CreateUser"
UserService_UpdateUser_FullMethodName
=
"/memos.api.v1.UserService/UpdateUser"
UserService_UpdateUser_FullMethodName
=
"/memos.api.v1.UserService/UpdateUser"
UserService_DeleteUser_FullMethodName
=
"/memos.api.v1.UserService/DeleteUser"
UserService_DeleteUser_FullMethodName
=
"/memos.api.v1.UserService/DeleteUser"
...
@@ -43,6 +45,8 @@ type UserServiceClient interface {
...
@@ -43,6 +45,8 @@ type UserServiceClient interface {
SearchUsers
(
ctx
context
.
Context
,
in
*
SearchUsersRequest
,
opts
...
grpc
.
CallOption
)
(
*
SearchUsersResponse
,
error
)
SearchUsers
(
ctx
context
.
Context
,
in
*
SearchUsersRequest
,
opts
...
grpc
.
CallOption
)
(
*
SearchUsersResponse
,
error
)
// GetUser gets a user by name.
// GetUser gets a user by name.
GetUser
(
ctx
context
.
Context
,
in
*
GetUserRequest
,
opts
...
grpc
.
CallOption
)
(
*
User
,
error
)
GetUser
(
ctx
context
.
Context
,
in
*
GetUserRequest
,
opts
...
grpc
.
CallOption
)
(
*
User
,
error
)
// GetUserAvatar gets the avatar of a user.
GetUserAvatar
(
ctx
context
.
Context
,
in
*
GetUserAvatarRequest
,
opts
...
grpc
.
CallOption
)
(
*
httpbody
.
HttpBody
,
error
)
// CreateUser creates a new user.
// CreateUser creates a new user.
CreateUser
(
ctx
context
.
Context
,
in
*
CreateUserRequest
,
opts
...
grpc
.
CallOption
)
(
*
User
,
error
)
CreateUser
(
ctx
context
.
Context
,
in
*
CreateUserRequest
,
opts
...
grpc
.
CallOption
)
(
*
User
,
error
)
// UpdateUser updates a user.
// UpdateUser updates a user.
...
@@ -96,6 +100,15 @@ func (c *userServiceClient) GetUser(ctx context.Context, in *GetUserRequest, opt
...
@@ -96,6 +100,15 @@ func (c *userServiceClient) GetUser(ctx context.Context, in *GetUserRequest, opt
return
out
,
nil
return
out
,
nil
}
}
func
(
c
*
userServiceClient
)
GetUserAvatar
(
ctx
context
.
Context
,
in
*
GetUserAvatarRequest
,
opts
...
grpc
.
CallOption
)
(
*
httpbody
.
HttpBody
,
error
)
{
out
:=
new
(
httpbody
.
HttpBody
)
err
:=
c
.
cc
.
Invoke
(
ctx
,
UserService_GetUserAvatar_FullMethodName
,
in
,
out
,
opts
...
)
if
err
!=
nil
{
return
nil
,
err
}
return
out
,
nil
}
func
(
c
*
userServiceClient
)
CreateUser
(
ctx
context
.
Context
,
in
*
CreateUserRequest
,
opts
...
grpc
.
CallOption
)
(
*
User
,
error
)
{
func
(
c
*
userServiceClient
)
CreateUser
(
ctx
context
.
Context
,
in
*
CreateUserRequest
,
opts
...
grpc
.
CallOption
)
(
*
User
,
error
)
{
out
:=
new
(
User
)
out
:=
new
(
User
)
err
:=
c
.
cc
.
Invoke
(
ctx
,
UserService_CreateUser_FullMethodName
,
in
,
out
,
opts
...
)
err
:=
c
.
cc
.
Invoke
(
ctx
,
UserService_CreateUser_FullMethodName
,
in
,
out
,
opts
...
)
...
@@ -178,6 +191,8 @@ type UserServiceServer interface {
...
@@ -178,6 +191,8 @@ type UserServiceServer interface {
SearchUsers
(
context
.
Context
,
*
SearchUsersRequest
)
(
*
SearchUsersResponse
,
error
)
SearchUsers
(
context
.
Context
,
*
SearchUsersRequest
)
(
*
SearchUsersResponse
,
error
)
// GetUser gets a user by name.
// GetUser gets a user by name.
GetUser
(
context
.
Context
,
*
GetUserRequest
)
(
*
User
,
error
)
GetUser
(
context
.
Context
,
*
GetUserRequest
)
(
*
User
,
error
)
// GetUserAvatar gets the avatar of a user.
GetUserAvatar
(
context
.
Context
,
*
GetUserAvatarRequest
)
(
*
httpbody
.
HttpBody
,
error
)
// CreateUser creates a new user.
// CreateUser creates a new user.
CreateUser
(
context
.
Context
,
*
CreateUserRequest
)
(
*
User
,
error
)
CreateUser
(
context
.
Context
,
*
CreateUserRequest
)
(
*
User
,
error
)
// UpdateUser updates a user.
// UpdateUser updates a user.
...
@@ -210,6 +225,9 @@ func (UnimplementedUserServiceServer) SearchUsers(context.Context, *SearchUsersR
...
@@ -210,6 +225,9 @@ func (UnimplementedUserServiceServer) SearchUsers(context.Context, *SearchUsersR
func
(
UnimplementedUserServiceServer
)
GetUser
(
context
.
Context
,
*
GetUserRequest
)
(
*
User
,
error
)
{
func
(
UnimplementedUserServiceServer
)
GetUser
(
context
.
Context
,
*
GetUserRequest
)
(
*
User
,
error
)
{
return
nil
,
status
.
Errorf
(
codes
.
Unimplemented
,
"method GetUser not implemented"
)
return
nil
,
status
.
Errorf
(
codes
.
Unimplemented
,
"method GetUser not implemented"
)
}
}
func
(
UnimplementedUserServiceServer
)
GetUserAvatar
(
context
.
Context
,
*
GetUserAvatarRequest
)
(
*
httpbody
.
HttpBody
,
error
)
{
return
nil
,
status
.
Errorf
(
codes
.
Unimplemented
,
"method GetUserAvatar not implemented"
)
}
func
(
UnimplementedUserServiceServer
)
CreateUser
(
context
.
Context
,
*
CreateUserRequest
)
(
*
User
,
error
)
{
func
(
UnimplementedUserServiceServer
)
CreateUser
(
context
.
Context
,
*
CreateUserRequest
)
(
*
User
,
error
)
{
return
nil
,
status
.
Errorf
(
codes
.
Unimplemented
,
"method CreateUser not implemented"
)
return
nil
,
status
.
Errorf
(
codes
.
Unimplemented
,
"method CreateUser not implemented"
)
}
}
...
@@ -301,6 +319,24 @@ func _UserService_GetUser_Handler(srv interface{}, ctx context.Context, dec func
...
@@ -301,6 +319,24 @@ func _UserService_GetUser_Handler(srv interface{}, ctx context.Context, dec func
return
interceptor
(
ctx
,
in
,
info
,
handler
)
return
interceptor
(
ctx
,
in
,
info
,
handler
)
}
}
func
_UserService_GetUserAvatar_Handler
(
srv
interface
{},
ctx
context
.
Context
,
dec
func
(
interface
{})
error
,
interceptor
grpc
.
UnaryServerInterceptor
)
(
interface
{},
error
)
{
in
:=
new
(
GetUserAvatarRequest
)
if
err
:=
dec
(
in
);
err
!=
nil
{
return
nil
,
err
}
if
interceptor
==
nil
{
return
srv
.
(
UserServiceServer
)
.
GetUserAvatar
(
ctx
,
in
)
}
info
:=
&
grpc
.
UnaryServerInfo
{
Server
:
srv
,
FullMethod
:
UserService_GetUserAvatar_FullMethodName
,
}
handler
:=
func
(
ctx
context
.
Context
,
req
interface
{})
(
interface
{},
error
)
{
return
srv
.
(
UserServiceServer
)
.
GetUserAvatar
(
ctx
,
req
.
(
*
GetUserAvatarRequest
))
}
return
interceptor
(
ctx
,
in
,
info
,
handler
)
}
func
_UserService_CreateUser_Handler
(
srv
interface
{},
ctx
context
.
Context
,
dec
func
(
interface
{})
error
,
interceptor
grpc
.
UnaryServerInterceptor
)
(
interface
{},
error
)
{
func
_UserService_CreateUser_Handler
(
srv
interface
{},
ctx
context
.
Context
,
dec
func
(
interface
{})
error
,
interceptor
grpc
.
UnaryServerInterceptor
)
(
interface
{},
error
)
{
in
:=
new
(
CreateUserRequest
)
in
:=
new
(
CreateUserRequest
)
if
err
:=
dec
(
in
);
err
!=
nil
{
if
err
:=
dec
(
in
);
err
!=
nil
{
...
@@ -464,6 +500,10 @@ var UserService_ServiceDesc = grpc.ServiceDesc{
...
@@ -464,6 +500,10 @@ var UserService_ServiceDesc = grpc.ServiceDesc{
MethodName
:
"GetUser"
,
MethodName
:
"GetUser"
,
Handler
:
_UserService_GetUser_Handler
,
Handler
:
_UserService_GetUser_Handler
,
},
},
{
MethodName
:
"GetUserAvatar"
,
Handler
:
_UserService_GetUserAvatar_Handler
,
},
{
{
MethodName
:
"CreateUser"
,
MethodName
:
"CreateUser"
,
Handler
:
_UserService_CreateUser_Handler
,
Handler
:
_UserService_CreateUser_Handler
,
...
...
server/route/api/v1/acl_config.go
View file @
62959795
...
@@ -12,6 +12,7 @@ var authenticationAllowlistMethods = map[string]bool{
...
@@ -12,6 +12,7 @@ var authenticationAllowlistMethods = map[string]bool{
"/memos.api.v1.AuthService/SignOut"
:
true
,
"/memos.api.v1.AuthService/SignOut"
:
true
,
"/memos.api.v1.AuthService/SignUp"
:
true
,
"/memos.api.v1.AuthService/SignUp"
:
true
,
"/memos.api.v1.UserService/GetUser"
:
true
,
"/memos.api.v1.UserService/GetUser"
:
true
,
"/memos.api.v1.UserService/GetUserAvatar"
:
true
,
"/memos.api.v1.UserService/SearchUsers"
:
true
,
"/memos.api.v1.UserService/SearchUsers"
:
true
,
"/memos.api.v1.MemoService/ListMemos"
:
true
,
"/memos.api.v1.MemoService/ListMemos"
:
true
,
"/memos.api.v1.MemoService/GetMemo"
:
true
,
"/memos.api.v1.MemoService/GetMemo"
:
true
,
...
...
server/route/api/v1/user_service.go
View file @
62959795
...
@@ -2,8 +2,10 @@ package v1
...
@@ -2,8 +2,10 @@ package v1
import
(
import
(
"context"
"context"
"encoding/base64"
"fmt"
"fmt"
"net/http"
"net/http"
"regexp"
"slices"
"slices"
"strings"
"strings"
"time"
"time"
...
@@ -14,6 +16,7 @@ import (
...
@@ -14,6 +16,7 @@ import (
"github.com/pkg/errors"
"github.com/pkg/errors"
"golang.org/x/crypto/bcrypt"
"golang.org/x/crypto/bcrypt"
expr
"google.golang.org/genproto/googleapis/api/expr/v1alpha1"
expr
"google.golang.org/genproto/googleapis/api/expr/v1alpha1"
"google.golang.org/genproto/googleapis/api/httpbody"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/emptypb"
"google.golang.org/protobuf/types/known/emptypb"
...
@@ -100,6 +103,39 @@ func (s *APIV1Service) GetUser(ctx context.Context, request *v1pb.GetUserRequest
...
@@ -100,6 +103,39 @@ func (s *APIV1Service) GetUser(ctx context.Context, request *v1pb.GetUserRequest
return
convertUserFromStore
(
user
),
nil
return
convertUserFromStore
(
user
),
nil
}
}
func
(
s
*
APIV1Service
)
GetUserAvatar
(
ctx
context
.
Context
,
request
*
v1pb
.
GetUserAvatarRequest
)
(
*
httpbody
.
HttpBody
,
error
)
{
userID
,
err
:=
ExtractUserIDFromName
(
request
.
Name
)
if
err
!=
nil
{
return
nil
,
status
.
Errorf
(
codes
.
InvalidArgument
,
"invalid user name: %v"
,
err
)
}
user
,
err
:=
s
.
Store
.
GetUser
(
ctx
,
&
store
.
FindUser
{
ID
:
&
userID
,
})
if
err
!=
nil
{
return
nil
,
status
.
Errorf
(
codes
.
Internal
,
"failed to get user: %v"
,
err
)
}
if
user
==
nil
{
return
nil
,
status
.
Errorf
(
codes
.
NotFound
,
"user not found"
)
}
if
user
.
AvatarURL
==
""
{
return
nil
,
status
.
Errorf
(
codes
.
NotFound
,
"avatar not found"
)
}
imageType
,
base64Data
,
err
:=
extractImageInfo
(
user
.
AvatarURL
)
if
err
!=
nil
{
return
nil
,
status
.
Errorf
(
codes
.
Internal
,
"failed to extract image info: %v"
,
err
)
}
imageData
,
err
:=
base64
.
StdEncoding
.
DecodeString
(
base64Data
)
if
err
!=
nil
{
return
nil
,
status
.
Errorf
(
codes
.
Internal
,
"failed to decode string: %v"
,
err
)
}
httpBody
:=
&
httpbody
.
HttpBody
{
ContentType
:
imageType
,
Data
:
imageData
,
}
return
httpBody
,
nil
}
func
(
s
*
APIV1Service
)
CreateUser
(
ctx
context
.
Context
,
request
*
v1pb
.
CreateUserRequest
)
(
*
v1pb
.
User
,
error
)
{
func
(
s
*
APIV1Service
)
CreateUser
(
ctx
context
.
Context
,
request
*
v1pb
.
CreateUserRequest
)
(
*
v1pb
.
User
,
error
)
{
currentUser
,
err
:=
getCurrentUser
(
ctx
,
s
.
Store
)
currentUser
,
err
:=
getCurrentUser
(
ctx
,
s
.
Store
)
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -567,3 +603,14 @@ func findSearchUsersField(callExpr *expr.Expr_Call, filter *SearchUsersFilter) {
...
@@ -567,3 +603,14 @@ func findSearchUsersField(callExpr *expr.Expr_Call, filter *SearchUsersFilter) {
}
}
}
}
}
}
func
extractImageInfo
(
dataURI
string
)
(
string
,
string
,
error
)
{
dataURIRegex
:=
regexp
.
MustCompile
(
`^data:(?P<type>.+);base64,(?P<base64>.+)`
)
matches
:=
dataURIRegex
.
FindStringSubmatch
(
dataURI
)
if
len
(
matches
)
!=
3
{
return
""
,
""
,
errors
.
New
(
"Invalid data URI format"
)
}
imageType
:=
matches
[
1
]
base64Data
:=
matches
[
2
]
return
imageType
,
base64Data
,
nil
}
server/route/frontend/frontend.go
View file @
62959795
...
@@ -40,16 +40,17 @@ func NewFrontendService(profile *profile.Profile, store *store.Store) *FrontendS
...
@@ -40,16 +40,17 @@ func NewFrontendService(profile *profile.Profile, store *store.Store) *FrontendS
}
}
func
(
s
*
FrontendService
)
Serve
(
ctx
context
.
Context
,
e
*
echo
.
Echo
)
{
func
(
s
*
FrontendService
)
Serve
(
ctx
context
.
Context
,
e
*
echo
.
Echo
)
{
skipper
:=
func
(
c
echo
.
Context
)
bool
{
return
util
.
HasPrefixes
(
c
.
Path
(),
"/api"
,
"/memos.api.v1"
,
"/robots.txt"
,
"/sitemap.xml"
,
"/m/:name"
)
}
// Use echo static middleware to serve the built dist folder.
// Use echo static middleware to serve the built dist folder.
// Reference: https://github.com/labstack/echo/blob/master/middleware/static.go
// Reference: https://github.com/labstack/echo/blob/master/middleware/static.go
e
.
Use
(
middleware
.
StaticWithConfig
(
middleware
.
StaticConfig
{
e
.
Use
(
middleware
.
StaticWithConfig
(
middleware
.
StaticConfig
{
HTML5
:
true
,
HTML5
:
true
,
Filesystem
:
getFileSystem
(
"dist"
),
Filesystem
:
getFileSystem
(
"dist"
),
Skipper
:
func
(
c
echo
.
Context
)
bool
{
Skipper
:
skipper
,
return
util
.
HasPrefixes
(
c
.
Path
(),
"/api"
,
"/memos.api.v1"
,
"/robots.txt"
,
"/sitemap.xml"
,
"/m/:name"
)
},
}))
}))
g
:=
e
.
Group
(
"assets"
)
g
:=
e
.
Group
(
"assets"
)
// Use echo gzip middleware to compress the response.
// Use echo gzip middleware to compress the response.
// Reference: https://echo.labstack.com/docs/middleware/gzip
// Reference: https://echo.labstack.com/docs/middleware/gzip
...
@@ -68,9 +69,6 @@ func (s *FrontendService) Serve(ctx context.Context, e *echo.Echo) {
...
@@ -68,9 +69,6 @@ func (s *FrontendService) Serve(ctx context.Context, e *echo.Echo) {
g
.
Use
(
middleware
.
StaticWithConfig
(
middleware
.
StaticConfig
{
g
.
Use
(
middleware
.
StaticWithConfig
(
middleware
.
StaticConfig
{
HTML5
:
true
,
HTML5
:
true
,
Filesystem
:
getFileSystem
(
"dist/assets"
),
Filesystem
:
getFileSystem
(
"dist/assets"
),
Skipper
:
func
(
c
echo
.
Context
)
bool
{
return
util
.
HasPrefixes
(
c
.
Path
(),
"/api"
,
"/memos.api.v1"
,
"/robots.txt"
,
"/sitemap.xml"
,
"/m/:name"
)
},
}))
}))
s
.
registerRoutes
(
e
)
s
.
registerRoutes
(
e
)
...
...
server/server.go
View file @
62959795
...
@@ -6,7 +6,6 @@ import (
...
@@ -6,7 +6,6 @@ import (
"log/slog"
"log/slog"
"net"
"net"
"net/http"
"net/http"
"strings"
"time"
"time"
"github.com/google/uuid"
"github.com/google/uuid"
...
@@ -47,9 +46,6 @@ func NewServer(ctx context.Context, profile *profile.Profile, store *store.Store
...
@@ -47,9 +46,6 @@ func NewServer(ctx context.Context, profile *profile.Profile, store *store.Store
echoServer
.
HidePort
=
true
echoServer
.
HidePort
=
true
s
.
echoServer
=
echoServer
s
.
echoServer
=
echoServer
// Register CORS middleware.
echoServer
.
Use
(
CORSMiddleware
(
s
.
Profile
.
Origins
))
workspaceBasicSetting
,
err
:=
s
.
getOrUpsertWorkspaceBasicSetting
(
ctx
)
workspaceBasicSetting
,
err
:=
s
.
getOrUpsertWorkspaceBasicSetting
(
ctx
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
errors
.
Wrap
(
err
,
"failed to get workspace basic setting"
)
return
nil
,
errors
.
Wrap
(
err
,
"failed to get workspace basic setting"
)
...
@@ -116,15 +112,20 @@ func (s *Server) Start(ctx context.Context) error {
...
@@ -116,15 +112,20 @@ func (s *Server) Start(ctx context.Context) error {
}
}
}()
}()
go
func
()
{
go
func
()
{
httpListener
:=
muxServer
.
Match
(
cmux
.
HTTP1Fast
()
,
cmux
.
Any
()
)
httpListener
:=
muxServer
.
Match
(
cmux
.
HTTP1Fast
())
s
.
echoServer
.
Listener
=
httpListener
s
.
echoServer
.
Listener
=
httpListener
if
err
:=
s
.
echoServer
.
Start
(
address
);
err
!=
nil
{
if
err
:=
s
.
echoServer
.
Start
(
address
);
err
!=
nil
{
slog
.
Error
(
"failed to start echo server"
,
err
)
slog
.
Error
(
"failed to start echo server"
,
err
)
}
}
}()
}()
go
func
()
{
if
err
:=
muxServer
.
Serve
();
err
!=
nil
{
slog
.
Error
(
"mux server listen error"
,
err
)
}
}()
s
.
StartBackgroundRunners
(
ctx
)
s
.
StartBackgroundRunners
(
ctx
)
return
muxServer
.
Serve
()
return
nil
}
}
func
(
s
*
Server
)
Shutdown
(
ctx
context
.
Context
)
{
func
(
s
*
Server
)
Shutdown
(
ctx
context
.
Context
)
{
...
@@ -170,43 +171,3 @@ func (s *Server) getOrUpsertWorkspaceBasicSetting(ctx context.Context) (*storepb
...
@@ -170,43 +171,3 @@ func (s *Server) getOrUpsertWorkspaceBasicSetting(ctx context.Context) (*storepb
}
}
return
workspaceBasicSetting
,
nil
return
workspaceBasicSetting
,
nil
}
}
func
grpcRequestSkipper
(
c
echo
.
Context
)
bool
{
return
strings
.
HasPrefix
(
c
.
Request
()
.
URL
.
Path
,
"/memos.api.v1."
)
}
func
CORSMiddleware
(
origins
[]
string
)
echo
.
MiddlewareFunc
{
return
func
(
next
echo
.
HandlerFunc
)
echo
.
HandlerFunc
{
return
func
(
c
echo
.
Context
)
error
{
if
grpcRequestSkipper
(
c
)
{
return
next
(
c
)
}
r
:=
c
.
Request
()
w
:=
c
.
Response
()
.
Writer
requestOrigin
:=
r
.
Header
.
Get
(
"Origin"
)
if
len
(
origins
)
==
0
{
w
.
Header
()
.
Set
(
"Access-Control-Allow-Origin"
,
requestOrigin
)
}
else
{
for
_
,
origin
:=
range
origins
{
if
origin
==
requestOrigin
{
w
.
Header
()
.
Set
(
"Access-Control-Allow-Origin"
,
origin
)
break
}
}
}
w
.
Header
()
.
Set
(
"Access-Control-Allow-Methods"
,
"GET, POST, PUT, DELETE, PATCH, OPTIONS"
)
w
.
Header
()
.
Set
(
"Access-Control-Allow-Headers"
,
"Content-Type, Authorization"
)
w
.
Header
()
.
Set
(
"Access-Control-Allow-Credentials"
,
"true"
)
// If it's preflight request, return immediately.
if
r
.
Method
==
"OPTIONS"
{
w
.
WriteHeader
(
http
.
StatusOK
)
return
nil
}
return
next
(
c
)
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment