refactor: migrate to connect-rpc (#5338)

This commit is contained in:
Johnny 2025-12-11 19:49:07 +08:00 committed by GitHub
parent 8af8b9d238
commit edd3ced9bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
151 changed files with 14556 additions and 27933 deletions

6
go.mod
View File

@ -3,6 +3,7 @@ module github.com/usememos/memos
go 1.25
require (
connectrpc.com/connect v1.19.1
github.com/aws/aws-sdk-go-v2 v1.39.2
github.com/aws/aws-sdk-go-v2/config v1.31.12
github.com/aws/aws-sdk-go-v2/credentials v1.18.16
@ -14,7 +15,6 @@ require (
github.com/gorilla/feeds v1.2.0
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2
github.com/improbable-eng/grpc-web v0.15.0
github.com/joho/godotenv v1.5.1
github.com/labstack/echo/v4 v4.13.4
github.com/lib/pq v1.10.9
@ -38,8 +38,6 @@ require (
cel.dev/expr v0.24.0 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/desertbit/timer v1.0.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
@ -48,7 +46,6 @@ require (
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/rs/cors v1.11.1 // indirect
github.com/sagikazarmark/locafero v0.7.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.12.0 // indirect
@ -64,7 +61,6 @@ require (
modernc.org/libc v1.66.8 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
nhooyr.io/websocket v1.8.17 // indirect
)
require (

383
go.sum
View File

@ -1,33 +1,13 @@
cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=
cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14=
connectrpc.com/connect v1.19.1/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/aws/aws-sdk-go-v2 v1.39.2 h1:EJLg8IdbzgeD7xgvZ+I8M1e0fL0ptn/M47lianzth0I=
github.com/aws/aws-sdk-go-v2 v1.39.2/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1 h1:i8p8P4diljCr60PpJp6qZXNlgX4m2yQFpYk+9ZT+J4E=
@ -67,196 +47,69 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.38.6/go.mod h1:WtKK+ppze5yKPkZ0XwqIV
github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE=
github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE=
github.com/desertbit/timer v1.0.1 h1:yRpYNn5Vaaj6QXecdLMPMJsW81JLiI1eokUft5nBmeo=
github.com/desertbit/timer v1.0.1/go.mod h1:htRrYeY5V/t4iu1xCJ5XsQvp4xve8QulXXctAzxqcwE=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/cel-go v0.26.1 h1:iPbVVEdkhTX++hpe3lzSk7D3G3QSYqLGoHOcEio+UXQ=
github.com/google/cel-go v0.26.1/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/feeds v1.2.0 h1:O6pBiXJ5JHhPvqy53NsjKOThq+dNFm8+DFrxBEdzSCc=
github.com/gorilla/feeds v1.2.0/go.mod h1:WMib8uJP3BbY+X8Szd1rA5Pzhdfh+HCCAYT2z7Fza6Y=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ=
github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
@ -268,151 +121,50 @@ github.com/labstack/echo/v4 v4.13.4 h1:oTZZW+T3s9gAu5L8vmzihV7/lkXGZuITzTQkTEhcX
github.com/labstack/echo/v4 v4.13.4/go.mod h1:g63b33BZ5vZzcIUF8AtRH40DrTlXnx4UMC8rBdndmjQ=
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/lithammer/shortuuid/v4 v4.2.0 h1:LMFOzVB3996a7b8aBuEXxqOBflbfPQAiVzkIcHO0h8c=
github.com/lithammer/shortuuid/v4 v4.2.0/go.mod h1:D5noHZ2oFw/YaKCfGy0YxyE7M0wMbezmMjPdhyEFe6Y=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/grpc-proxy v0.0.0-20181017164139-0f1106ef9c76/go.mod h1:x5OoJHDHqxHS801UIuhqGl6QdSAEJvtausosHSdazIo=
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs=
github.com/stoewer/go-strcase v1.3.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
@ -428,25 +180,14 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA=
github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
@ -459,150 +200,76 @@ go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFh
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b h1:DXr+pvt3nC887026GRP39Ej11UATqWDmWuS99x26cD0=
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.30.0 h1:jD5RhkmVAnjqaCUXfbGBrn3lpxbknfN9w2UhHHU+5B4=
golang.org/x/image v0.30.0/go.mod h1:SAEUTxCCMWSrJcCy/4HwavEsfZZJlYxeHLc6tTiAe/c=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
@ -613,77 +280,36 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1 h1:APHvLLYBhtZvsbnpkfknDZ7NyH4z5+ub/I0u8L3Oz6g=
google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1/go.mod h1:xUjFWUnWDpZ/C0Gu0qloASKFb6f8/QXiiXhSPFsD668=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 h1:pmJpJEvT846VzausCQ5d7KreSROcDqmO388w5YbnltA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI=
google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
modernc.org/cc/v4 v4.26.4 h1:jPhG8oNjtTYuP2FA4YefTJ/wioNUGALmGuEWt7SUR6s=
modernc.org/cc/v4 v4.26.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.28.1 h1:wPKYn5EC/mYTqBO373jKjvX2n+3+aK7+sICCv4Fjy1A=
@ -710,8 +336,3 @@ modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
nhooyr.io/websocket v1.8.17 h1:KEVeLJkUywCKVsnLIDlD/5gtayKp8VoCkksHCGGfT9Y=
nhooyr.io/websocket v1.8.17/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=

View File

@ -7,6 +7,15 @@ managed:
override:
- file_option: go_package_prefix
value: github.com/usememos/memos/proto/gen
inputs:
- directory: .
- module: buf.build/googleapis/googleapis
paths:
- google/api/annotations.proto
- google/api/client.proto
- google/api/field_behavior.proto
- google/api/http.proto
- google/api/resource.proto
plugins:
- remote: buf.build/protocolbuffers/go
out: gen
@ -14,19 +23,17 @@ plugins:
- remote: buf.build/grpc/go
out: gen
opt: paths=source_relative
- remote: buf.build/connectrpc/go
out: gen
opt: paths=source_relative
- remote: buf.build/grpc-ecosystem/gateway
out: gen
opt: paths=source_relative
- remote: buf.build/community/google-gnostic-openapi
out: gen
opt: paths=source_relative,enum_type=string
- remote: buf.build/community/stephenh-ts-proto
- remote: buf.build/bufbuild/es
out: ../web/src/types/proto
opt:
- env=browser
- useOptionals=messages
- outputServices=generic-definitions
- outputJsonMethods=false
- useExactTypes=false
- esModuleInterop=true
- stringEnums=true
- target=ts
include_imports: true

View File

@ -0,0 +1,142 @@
// Code generated by protoc-gen-connect-go. DO NOT EDIT.
//
// Source: api/v1/activity_service.proto
package apiv1connect
import (
connect "connectrpc.com/connect"
context "context"
errors "errors"
v1 "github.com/usememos/memos/proto/gen/api/v1"
http "net/http"
strings "strings"
)
// This is a compile-time assertion to ensure that this generated file and the connect package are
// compatible. If you get a compiler error that this constant is not defined, this code was
// generated with a version of connect newer than the one compiled into your binary. You can fix the
// problem by either regenerating this code with an older version of connect or updating the connect
// version compiled into your binary.
const _ = connect.IsAtLeastVersion1_13_0
const (
// ActivityServiceName is the fully-qualified name of the ActivityService service.
ActivityServiceName = "memos.api.v1.ActivityService"
)
// These constants are the fully-qualified names of the RPCs defined in this package. They're
// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
//
// Note that these are different from the fully-qualified method names used by
// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
// period.
const (
// ActivityServiceListActivitiesProcedure is the fully-qualified name of the ActivityService's
// ListActivities RPC.
ActivityServiceListActivitiesProcedure = "/memos.api.v1.ActivityService/ListActivities"
// ActivityServiceGetActivityProcedure is the fully-qualified name of the ActivityService's
// GetActivity RPC.
ActivityServiceGetActivityProcedure = "/memos.api.v1.ActivityService/GetActivity"
)
// ActivityServiceClient is a client for the memos.api.v1.ActivityService service.
type ActivityServiceClient interface {
// ListActivities returns a list of activities.
ListActivities(context.Context, *connect.Request[v1.ListActivitiesRequest]) (*connect.Response[v1.ListActivitiesResponse], error)
// GetActivity returns the activity with the given id.
GetActivity(context.Context, *connect.Request[v1.GetActivityRequest]) (*connect.Response[v1.Activity], error)
}
// NewActivityServiceClient constructs a client for the memos.api.v1.ActivityService service. By
// default, it uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses,
// and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the
// connect.WithGRPC() or connect.WithGRPCWeb() options.
//
// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
// http://api.acme.com or https://acme.com/grpc).
func NewActivityServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) ActivityServiceClient {
baseURL = strings.TrimRight(baseURL, "/")
activityServiceMethods := v1.File_api_v1_activity_service_proto.Services().ByName("ActivityService").Methods()
return &activityServiceClient{
listActivities: connect.NewClient[v1.ListActivitiesRequest, v1.ListActivitiesResponse](
httpClient,
baseURL+ActivityServiceListActivitiesProcedure,
connect.WithSchema(activityServiceMethods.ByName("ListActivities")),
connect.WithClientOptions(opts...),
),
getActivity: connect.NewClient[v1.GetActivityRequest, v1.Activity](
httpClient,
baseURL+ActivityServiceGetActivityProcedure,
connect.WithSchema(activityServiceMethods.ByName("GetActivity")),
connect.WithClientOptions(opts...),
),
}
}
// activityServiceClient implements ActivityServiceClient.
type activityServiceClient struct {
listActivities *connect.Client[v1.ListActivitiesRequest, v1.ListActivitiesResponse]
getActivity *connect.Client[v1.GetActivityRequest, v1.Activity]
}
// ListActivities calls memos.api.v1.ActivityService.ListActivities.
func (c *activityServiceClient) ListActivities(ctx context.Context, req *connect.Request[v1.ListActivitiesRequest]) (*connect.Response[v1.ListActivitiesResponse], error) {
return c.listActivities.CallUnary(ctx, req)
}
// GetActivity calls memos.api.v1.ActivityService.GetActivity.
func (c *activityServiceClient) GetActivity(ctx context.Context, req *connect.Request[v1.GetActivityRequest]) (*connect.Response[v1.Activity], error) {
return c.getActivity.CallUnary(ctx, req)
}
// ActivityServiceHandler is an implementation of the memos.api.v1.ActivityService service.
type ActivityServiceHandler interface {
// ListActivities returns a list of activities.
ListActivities(context.Context, *connect.Request[v1.ListActivitiesRequest]) (*connect.Response[v1.ListActivitiesResponse], error)
// GetActivity returns the activity with the given id.
GetActivity(context.Context, *connect.Request[v1.GetActivityRequest]) (*connect.Response[v1.Activity], error)
}
// NewActivityServiceHandler builds an HTTP handler from the service implementation. It returns the
// path on which to mount the handler and the handler itself.
//
// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
// and JSON codecs. They also support gzip compression.
func NewActivityServiceHandler(svc ActivityServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
activityServiceMethods := v1.File_api_v1_activity_service_proto.Services().ByName("ActivityService").Methods()
activityServiceListActivitiesHandler := connect.NewUnaryHandler(
ActivityServiceListActivitiesProcedure,
svc.ListActivities,
connect.WithSchema(activityServiceMethods.ByName("ListActivities")),
connect.WithHandlerOptions(opts...),
)
activityServiceGetActivityHandler := connect.NewUnaryHandler(
ActivityServiceGetActivityProcedure,
svc.GetActivity,
connect.WithSchema(activityServiceMethods.ByName("GetActivity")),
connect.WithHandlerOptions(opts...),
)
return "/memos.api.v1.ActivityService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case ActivityServiceListActivitiesProcedure:
activityServiceListActivitiesHandler.ServeHTTP(w, r)
case ActivityServiceGetActivityProcedure:
activityServiceGetActivityHandler.ServeHTTP(w, r)
default:
http.NotFound(w, r)
}
})
}
// UnimplementedActivityServiceHandler returns CodeUnimplemented from all methods.
type UnimplementedActivityServiceHandler struct{}
func (UnimplementedActivityServiceHandler) ListActivities(context.Context, *connect.Request[v1.ListActivitiesRequest]) (*connect.Response[v1.ListActivitiesResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.ActivityService.ListActivities is not implemented"))
}
func (UnimplementedActivityServiceHandler) GetActivity(context.Context, *connect.Request[v1.GetActivityRequest]) (*connect.Response[v1.Activity], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.ActivityService.GetActivity is not implemented"))
}

View File

@ -0,0 +1,236 @@
// Code generated by protoc-gen-connect-go. DO NOT EDIT.
//
// Source: api/v1/attachment_service.proto
package apiv1connect
import (
connect "connectrpc.com/connect"
context "context"
errors "errors"
v1 "github.com/usememos/memos/proto/gen/api/v1"
emptypb "google.golang.org/protobuf/types/known/emptypb"
http "net/http"
strings "strings"
)
// This is a compile-time assertion to ensure that this generated file and the connect package are
// compatible. If you get a compiler error that this constant is not defined, this code was
// generated with a version of connect newer than the one compiled into your binary. You can fix the
// problem by either regenerating this code with an older version of connect or updating the connect
// version compiled into your binary.
const _ = connect.IsAtLeastVersion1_13_0
const (
// AttachmentServiceName is the fully-qualified name of the AttachmentService service.
AttachmentServiceName = "memos.api.v1.AttachmentService"
)
// These constants are the fully-qualified names of the RPCs defined in this package. They're
// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
//
// Note that these are different from the fully-qualified method names used by
// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
// period.
const (
// AttachmentServiceCreateAttachmentProcedure is the fully-qualified name of the AttachmentService's
// CreateAttachment RPC.
AttachmentServiceCreateAttachmentProcedure = "/memos.api.v1.AttachmentService/CreateAttachment"
// AttachmentServiceListAttachmentsProcedure is the fully-qualified name of the AttachmentService's
// ListAttachments RPC.
AttachmentServiceListAttachmentsProcedure = "/memos.api.v1.AttachmentService/ListAttachments"
// AttachmentServiceGetAttachmentProcedure is the fully-qualified name of the AttachmentService's
// GetAttachment RPC.
AttachmentServiceGetAttachmentProcedure = "/memos.api.v1.AttachmentService/GetAttachment"
// AttachmentServiceUpdateAttachmentProcedure is the fully-qualified name of the AttachmentService's
// UpdateAttachment RPC.
AttachmentServiceUpdateAttachmentProcedure = "/memos.api.v1.AttachmentService/UpdateAttachment"
// AttachmentServiceDeleteAttachmentProcedure is the fully-qualified name of the AttachmentService's
// DeleteAttachment RPC.
AttachmentServiceDeleteAttachmentProcedure = "/memos.api.v1.AttachmentService/DeleteAttachment"
)
// AttachmentServiceClient is a client for the memos.api.v1.AttachmentService service.
type AttachmentServiceClient interface {
// CreateAttachment creates a new attachment.
CreateAttachment(context.Context, *connect.Request[v1.CreateAttachmentRequest]) (*connect.Response[v1.Attachment], error)
// ListAttachments lists all attachments.
ListAttachments(context.Context, *connect.Request[v1.ListAttachmentsRequest]) (*connect.Response[v1.ListAttachmentsResponse], error)
// GetAttachment returns a attachment by name.
GetAttachment(context.Context, *connect.Request[v1.GetAttachmentRequest]) (*connect.Response[v1.Attachment], error)
// UpdateAttachment updates a attachment.
UpdateAttachment(context.Context, *connect.Request[v1.UpdateAttachmentRequest]) (*connect.Response[v1.Attachment], error)
// DeleteAttachment deletes a attachment by name.
DeleteAttachment(context.Context, *connect.Request[v1.DeleteAttachmentRequest]) (*connect.Response[emptypb.Empty], error)
}
// NewAttachmentServiceClient constructs a client for the memos.api.v1.AttachmentService service. By
// default, it uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses,
// and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the
// connect.WithGRPC() or connect.WithGRPCWeb() options.
//
// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
// http://api.acme.com or https://acme.com/grpc).
func NewAttachmentServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) AttachmentServiceClient {
baseURL = strings.TrimRight(baseURL, "/")
attachmentServiceMethods := v1.File_api_v1_attachment_service_proto.Services().ByName("AttachmentService").Methods()
return &attachmentServiceClient{
createAttachment: connect.NewClient[v1.CreateAttachmentRequest, v1.Attachment](
httpClient,
baseURL+AttachmentServiceCreateAttachmentProcedure,
connect.WithSchema(attachmentServiceMethods.ByName("CreateAttachment")),
connect.WithClientOptions(opts...),
),
listAttachments: connect.NewClient[v1.ListAttachmentsRequest, v1.ListAttachmentsResponse](
httpClient,
baseURL+AttachmentServiceListAttachmentsProcedure,
connect.WithSchema(attachmentServiceMethods.ByName("ListAttachments")),
connect.WithClientOptions(opts...),
),
getAttachment: connect.NewClient[v1.GetAttachmentRequest, v1.Attachment](
httpClient,
baseURL+AttachmentServiceGetAttachmentProcedure,
connect.WithSchema(attachmentServiceMethods.ByName("GetAttachment")),
connect.WithClientOptions(opts...),
),
updateAttachment: connect.NewClient[v1.UpdateAttachmentRequest, v1.Attachment](
httpClient,
baseURL+AttachmentServiceUpdateAttachmentProcedure,
connect.WithSchema(attachmentServiceMethods.ByName("UpdateAttachment")),
connect.WithClientOptions(opts...),
),
deleteAttachment: connect.NewClient[v1.DeleteAttachmentRequest, emptypb.Empty](
httpClient,
baseURL+AttachmentServiceDeleteAttachmentProcedure,
connect.WithSchema(attachmentServiceMethods.ByName("DeleteAttachment")),
connect.WithClientOptions(opts...),
),
}
}
// attachmentServiceClient implements AttachmentServiceClient.
type attachmentServiceClient struct {
createAttachment *connect.Client[v1.CreateAttachmentRequest, v1.Attachment]
listAttachments *connect.Client[v1.ListAttachmentsRequest, v1.ListAttachmentsResponse]
getAttachment *connect.Client[v1.GetAttachmentRequest, v1.Attachment]
updateAttachment *connect.Client[v1.UpdateAttachmentRequest, v1.Attachment]
deleteAttachment *connect.Client[v1.DeleteAttachmentRequest, emptypb.Empty]
}
// CreateAttachment calls memos.api.v1.AttachmentService.CreateAttachment.
func (c *attachmentServiceClient) CreateAttachment(ctx context.Context, req *connect.Request[v1.CreateAttachmentRequest]) (*connect.Response[v1.Attachment], error) {
return c.createAttachment.CallUnary(ctx, req)
}
// ListAttachments calls memos.api.v1.AttachmentService.ListAttachments.
func (c *attachmentServiceClient) ListAttachments(ctx context.Context, req *connect.Request[v1.ListAttachmentsRequest]) (*connect.Response[v1.ListAttachmentsResponse], error) {
return c.listAttachments.CallUnary(ctx, req)
}
// GetAttachment calls memos.api.v1.AttachmentService.GetAttachment.
func (c *attachmentServiceClient) GetAttachment(ctx context.Context, req *connect.Request[v1.GetAttachmentRequest]) (*connect.Response[v1.Attachment], error) {
return c.getAttachment.CallUnary(ctx, req)
}
// UpdateAttachment calls memos.api.v1.AttachmentService.UpdateAttachment.
func (c *attachmentServiceClient) UpdateAttachment(ctx context.Context, req *connect.Request[v1.UpdateAttachmentRequest]) (*connect.Response[v1.Attachment], error) {
return c.updateAttachment.CallUnary(ctx, req)
}
// DeleteAttachment calls memos.api.v1.AttachmentService.DeleteAttachment.
func (c *attachmentServiceClient) DeleteAttachment(ctx context.Context, req *connect.Request[v1.DeleteAttachmentRequest]) (*connect.Response[emptypb.Empty], error) {
return c.deleteAttachment.CallUnary(ctx, req)
}
// AttachmentServiceHandler is an implementation of the memos.api.v1.AttachmentService service.
type AttachmentServiceHandler interface {
// CreateAttachment creates a new attachment.
CreateAttachment(context.Context, *connect.Request[v1.CreateAttachmentRequest]) (*connect.Response[v1.Attachment], error)
// ListAttachments lists all attachments.
ListAttachments(context.Context, *connect.Request[v1.ListAttachmentsRequest]) (*connect.Response[v1.ListAttachmentsResponse], error)
// GetAttachment returns a attachment by name.
GetAttachment(context.Context, *connect.Request[v1.GetAttachmentRequest]) (*connect.Response[v1.Attachment], error)
// UpdateAttachment updates a attachment.
UpdateAttachment(context.Context, *connect.Request[v1.UpdateAttachmentRequest]) (*connect.Response[v1.Attachment], error)
// DeleteAttachment deletes a attachment by name.
DeleteAttachment(context.Context, *connect.Request[v1.DeleteAttachmentRequest]) (*connect.Response[emptypb.Empty], error)
}
// NewAttachmentServiceHandler builds an HTTP handler from the service implementation. It returns
// the path on which to mount the handler and the handler itself.
//
// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
// and JSON codecs. They also support gzip compression.
func NewAttachmentServiceHandler(svc AttachmentServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
attachmentServiceMethods := v1.File_api_v1_attachment_service_proto.Services().ByName("AttachmentService").Methods()
attachmentServiceCreateAttachmentHandler := connect.NewUnaryHandler(
AttachmentServiceCreateAttachmentProcedure,
svc.CreateAttachment,
connect.WithSchema(attachmentServiceMethods.ByName("CreateAttachment")),
connect.WithHandlerOptions(opts...),
)
attachmentServiceListAttachmentsHandler := connect.NewUnaryHandler(
AttachmentServiceListAttachmentsProcedure,
svc.ListAttachments,
connect.WithSchema(attachmentServiceMethods.ByName("ListAttachments")),
connect.WithHandlerOptions(opts...),
)
attachmentServiceGetAttachmentHandler := connect.NewUnaryHandler(
AttachmentServiceGetAttachmentProcedure,
svc.GetAttachment,
connect.WithSchema(attachmentServiceMethods.ByName("GetAttachment")),
connect.WithHandlerOptions(opts...),
)
attachmentServiceUpdateAttachmentHandler := connect.NewUnaryHandler(
AttachmentServiceUpdateAttachmentProcedure,
svc.UpdateAttachment,
connect.WithSchema(attachmentServiceMethods.ByName("UpdateAttachment")),
connect.WithHandlerOptions(opts...),
)
attachmentServiceDeleteAttachmentHandler := connect.NewUnaryHandler(
AttachmentServiceDeleteAttachmentProcedure,
svc.DeleteAttachment,
connect.WithSchema(attachmentServiceMethods.ByName("DeleteAttachment")),
connect.WithHandlerOptions(opts...),
)
return "/memos.api.v1.AttachmentService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case AttachmentServiceCreateAttachmentProcedure:
attachmentServiceCreateAttachmentHandler.ServeHTTP(w, r)
case AttachmentServiceListAttachmentsProcedure:
attachmentServiceListAttachmentsHandler.ServeHTTP(w, r)
case AttachmentServiceGetAttachmentProcedure:
attachmentServiceGetAttachmentHandler.ServeHTTP(w, r)
case AttachmentServiceUpdateAttachmentProcedure:
attachmentServiceUpdateAttachmentHandler.ServeHTTP(w, r)
case AttachmentServiceDeleteAttachmentProcedure:
attachmentServiceDeleteAttachmentHandler.ServeHTTP(w, r)
default:
http.NotFound(w, r)
}
})
}
// UnimplementedAttachmentServiceHandler returns CodeUnimplemented from all methods.
type UnimplementedAttachmentServiceHandler struct{}
func (UnimplementedAttachmentServiceHandler) CreateAttachment(context.Context, *connect.Request[v1.CreateAttachmentRequest]) (*connect.Response[v1.Attachment], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.AttachmentService.CreateAttachment is not implemented"))
}
func (UnimplementedAttachmentServiceHandler) ListAttachments(context.Context, *connect.Request[v1.ListAttachmentsRequest]) (*connect.Response[v1.ListAttachmentsResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.AttachmentService.ListAttachments is not implemented"))
}
func (UnimplementedAttachmentServiceHandler) GetAttachment(context.Context, *connect.Request[v1.GetAttachmentRequest]) (*connect.Response[v1.Attachment], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.AttachmentService.GetAttachment is not implemented"))
}
func (UnimplementedAttachmentServiceHandler) UpdateAttachment(context.Context, *connect.Request[v1.UpdateAttachmentRequest]) (*connect.Response[v1.Attachment], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.AttachmentService.UpdateAttachment is not implemented"))
}
func (UnimplementedAttachmentServiceHandler) DeleteAttachment(context.Context, *connect.Request[v1.DeleteAttachmentRequest]) (*connect.Response[emptypb.Empty], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.AttachmentService.DeleteAttachment is not implemented"))
}

View File

@ -0,0 +1,180 @@
// Code generated by protoc-gen-connect-go. DO NOT EDIT.
//
// Source: api/v1/auth_service.proto
package apiv1connect
import (
connect "connectrpc.com/connect"
context "context"
errors "errors"
v1 "github.com/usememos/memos/proto/gen/api/v1"
emptypb "google.golang.org/protobuf/types/known/emptypb"
http "net/http"
strings "strings"
)
// This is a compile-time assertion to ensure that this generated file and the connect package are
// compatible. If you get a compiler error that this constant is not defined, this code was
// generated with a version of connect newer than the one compiled into your binary. You can fix the
// problem by either regenerating this code with an older version of connect or updating the connect
// version compiled into your binary.
const _ = connect.IsAtLeastVersion1_13_0
const (
// AuthServiceName is the fully-qualified name of the AuthService service.
AuthServiceName = "memos.api.v1.AuthService"
)
// These constants are the fully-qualified names of the RPCs defined in this package. They're
// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
//
// Note that these are different from the fully-qualified method names used by
// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
// period.
const (
// AuthServiceGetCurrentSessionProcedure is the fully-qualified name of the AuthService's
// GetCurrentSession RPC.
AuthServiceGetCurrentSessionProcedure = "/memos.api.v1.AuthService/GetCurrentSession"
// AuthServiceCreateSessionProcedure is the fully-qualified name of the AuthService's CreateSession
// RPC.
AuthServiceCreateSessionProcedure = "/memos.api.v1.AuthService/CreateSession"
// AuthServiceDeleteSessionProcedure is the fully-qualified name of the AuthService's DeleteSession
// RPC.
AuthServiceDeleteSessionProcedure = "/memos.api.v1.AuthService/DeleteSession"
)
// AuthServiceClient is a client for the memos.api.v1.AuthService service.
type AuthServiceClient interface {
// GetCurrentSession returns the current active session information.
// This method is idempotent and safe, suitable for checking current session state.
GetCurrentSession(context.Context, *connect.Request[v1.GetCurrentSessionRequest]) (*connect.Response[v1.GetCurrentSessionResponse], error)
// CreateSession authenticates a user and creates a new session.
// Returns the authenticated user information upon successful authentication.
CreateSession(context.Context, *connect.Request[v1.CreateSessionRequest]) (*connect.Response[v1.CreateSessionResponse], error)
// DeleteSession terminates the current user session.
// This is an idempotent operation that invalidates the user's authentication.
DeleteSession(context.Context, *connect.Request[v1.DeleteSessionRequest]) (*connect.Response[emptypb.Empty], error)
}
// NewAuthServiceClient constructs a client for the memos.api.v1.AuthService service. By default, it
// uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends
// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or
// connect.WithGRPCWeb() options.
//
// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
// http://api.acme.com or https://acme.com/grpc).
func NewAuthServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) AuthServiceClient {
baseURL = strings.TrimRight(baseURL, "/")
authServiceMethods := v1.File_api_v1_auth_service_proto.Services().ByName("AuthService").Methods()
return &authServiceClient{
getCurrentSession: connect.NewClient[v1.GetCurrentSessionRequest, v1.GetCurrentSessionResponse](
httpClient,
baseURL+AuthServiceGetCurrentSessionProcedure,
connect.WithSchema(authServiceMethods.ByName("GetCurrentSession")),
connect.WithClientOptions(opts...),
),
createSession: connect.NewClient[v1.CreateSessionRequest, v1.CreateSessionResponse](
httpClient,
baseURL+AuthServiceCreateSessionProcedure,
connect.WithSchema(authServiceMethods.ByName("CreateSession")),
connect.WithClientOptions(opts...),
),
deleteSession: connect.NewClient[v1.DeleteSessionRequest, emptypb.Empty](
httpClient,
baseURL+AuthServiceDeleteSessionProcedure,
connect.WithSchema(authServiceMethods.ByName("DeleteSession")),
connect.WithClientOptions(opts...),
),
}
}
// authServiceClient implements AuthServiceClient.
type authServiceClient struct {
getCurrentSession *connect.Client[v1.GetCurrentSessionRequest, v1.GetCurrentSessionResponse]
createSession *connect.Client[v1.CreateSessionRequest, v1.CreateSessionResponse]
deleteSession *connect.Client[v1.DeleteSessionRequest, emptypb.Empty]
}
// GetCurrentSession calls memos.api.v1.AuthService.GetCurrentSession.
func (c *authServiceClient) GetCurrentSession(ctx context.Context, req *connect.Request[v1.GetCurrentSessionRequest]) (*connect.Response[v1.GetCurrentSessionResponse], error) {
return c.getCurrentSession.CallUnary(ctx, req)
}
// CreateSession calls memos.api.v1.AuthService.CreateSession.
func (c *authServiceClient) CreateSession(ctx context.Context, req *connect.Request[v1.CreateSessionRequest]) (*connect.Response[v1.CreateSessionResponse], error) {
return c.createSession.CallUnary(ctx, req)
}
// DeleteSession calls memos.api.v1.AuthService.DeleteSession.
func (c *authServiceClient) DeleteSession(ctx context.Context, req *connect.Request[v1.DeleteSessionRequest]) (*connect.Response[emptypb.Empty], error) {
return c.deleteSession.CallUnary(ctx, req)
}
// AuthServiceHandler is an implementation of the memos.api.v1.AuthService service.
type AuthServiceHandler interface {
// GetCurrentSession returns the current active session information.
// This method is idempotent and safe, suitable for checking current session state.
GetCurrentSession(context.Context, *connect.Request[v1.GetCurrentSessionRequest]) (*connect.Response[v1.GetCurrentSessionResponse], error)
// CreateSession authenticates a user and creates a new session.
// Returns the authenticated user information upon successful authentication.
CreateSession(context.Context, *connect.Request[v1.CreateSessionRequest]) (*connect.Response[v1.CreateSessionResponse], error)
// DeleteSession terminates the current user session.
// This is an idempotent operation that invalidates the user's authentication.
DeleteSession(context.Context, *connect.Request[v1.DeleteSessionRequest]) (*connect.Response[emptypb.Empty], error)
}
// NewAuthServiceHandler builds an HTTP handler from the service implementation. It returns the path
// on which to mount the handler and the handler itself.
//
// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
// and JSON codecs. They also support gzip compression.
func NewAuthServiceHandler(svc AuthServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
authServiceMethods := v1.File_api_v1_auth_service_proto.Services().ByName("AuthService").Methods()
authServiceGetCurrentSessionHandler := connect.NewUnaryHandler(
AuthServiceGetCurrentSessionProcedure,
svc.GetCurrentSession,
connect.WithSchema(authServiceMethods.ByName("GetCurrentSession")),
connect.WithHandlerOptions(opts...),
)
authServiceCreateSessionHandler := connect.NewUnaryHandler(
AuthServiceCreateSessionProcedure,
svc.CreateSession,
connect.WithSchema(authServiceMethods.ByName("CreateSession")),
connect.WithHandlerOptions(opts...),
)
authServiceDeleteSessionHandler := connect.NewUnaryHandler(
AuthServiceDeleteSessionProcedure,
svc.DeleteSession,
connect.WithSchema(authServiceMethods.ByName("DeleteSession")),
connect.WithHandlerOptions(opts...),
)
return "/memos.api.v1.AuthService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case AuthServiceGetCurrentSessionProcedure:
authServiceGetCurrentSessionHandler.ServeHTTP(w, r)
case AuthServiceCreateSessionProcedure:
authServiceCreateSessionHandler.ServeHTTP(w, r)
case AuthServiceDeleteSessionProcedure:
authServiceDeleteSessionHandler.ServeHTTP(w, r)
default:
http.NotFound(w, r)
}
})
}
// UnimplementedAuthServiceHandler returns CodeUnimplemented from all methods.
type UnimplementedAuthServiceHandler struct{}
func (UnimplementedAuthServiceHandler) GetCurrentSession(context.Context, *connect.Request[v1.GetCurrentSessionRequest]) (*connect.Response[v1.GetCurrentSessionResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.AuthService.GetCurrentSession is not implemented"))
}
func (UnimplementedAuthServiceHandler) CreateSession(context.Context, *connect.Request[v1.CreateSessionRequest]) (*connect.Response[v1.CreateSessionResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.AuthService.CreateSession is not implemented"))
}
func (UnimplementedAuthServiceHandler) DeleteSession(context.Context, *connect.Request[v1.DeleteSessionRequest]) (*connect.Response[emptypb.Empty], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.AuthService.DeleteSession is not implemented"))
}

View File

@ -0,0 +1,237 @@
// Code generated by protoc-gen-connect-go. DO NOT EDIT.
//
// Source: api/v1/idp_service.proto
package apiv1connect
import (
connect "connectrpc.com/connect"
context "context"
errors "errors"
v1 "github.com/usememos/memos/proto/gen/api/v1"
emptypb "google.golang.org/protobuf/types/known/emptypb"
http "net/http"
strings "strings"
)
// This is a compile-time assertion to ensure that this generated file and the connect package are
// compatible. If you get a compiler error that this constant is not defined, this code was
// generated with a version of connect newer than the one compiled into your binary. You can fix the
// problem by either regenerating this code with an older version of connect or updating the connect
// version compiled into your binary.
const _ = connect.IsAtLeastVersion1_13_0
const (
// IdentityProviderServiceName is the fully-qualified name of the IdentityProviderService service.
IdentityProviderServiceName = "memos.api.v1.IdentityProviderService"
)
// These constants are the fully-qualified names of the RPCs defined in this package. They're
// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
//
// Note that these are different from the fully-qualified method names used by
// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
// period.
const (
// IdentityProviderServiceListIdentityProvidersProcedure is the fully-qualified name of the
// IdentityProviderService's ListIdentityProviders RPC.
IdentityProviderServiceListIdentityProvidersProcedure = "/memos.api.v1.IdentityProviderService/ListIdentityProviders"
// IdentityProviderServiceGetIdentityProviderProcedure is the fully-qualified name of the
// IdentityProviderService's GetIdentityProvider RPC.
IdentityProviderServiceGetIdentityProviderProcedure = "/memos.api.v1.IdentityProviderService/GetIdentityProvider"
// IdentityProviderServiceCreateIdentityProviderProcedure is the fully-qualified name of the
// IdentityProviderService's CreateIdentityProvider RPC.
IdentityProviderServiceCreateIdentityProviderProcedure = "/memos.api.v1.IdentityProviderService/CreateIdentityProvider"
// IdentityProviderServiceUpdateIdentityProviderProcedure is the fully-qualified name of the
// IdentityProviderService's UpdateIdentityProvider RPC.
IdentityProviderServiceUpdateIdentityProviderProcedure = "/memos.api.v1.IdentityProviderService/UpdateIdentityProvider"
// IdentityProviderServiceDeleteIdentityProviderProcedure is the fully-qualified name of the
// IdentityProviderService's DeleteIdentityProvider RPC.
IdentityProviderServiceDeleteIdentityProviderProcedure = "/memos.api.v1.IdentityProviderService/DeleteIdentityProvider"
)
// IdentityProviderServiceClient is a client for the memos.api.v1.IdentityProviderService service.
type IdentityProviderServiceClient interface {
// ListIdentityProviders lists identity providers.
ListIdentityProviders(context.Context, *connect.Request[v1.ListIdentityProvidersRequest]) (*connect.Response[v1.ListIdentityProvidersResponse], error)
// GetIdentityProvider gets an identity provider.
GetIdentityProvider(context.Context, *connect.Request[v1.GetIdentityProviderRequest]) (*connect.Response[v1.IdentityProvider], error)
// CreateIdentityProvider creates an identity provider.
CreateIdentityProvider(context.Context, *connect.Request[v1.CreateIdentityProviderRequest]) (*connect.Response[v1.IdentityProvider], error)
// UpdateIdentityProvider updates an identity provider.
UpdateIdentityProvider(context.Context, *connect.Request[v1.UpdateIdentityProviderRequest]) (*connect.Response[v1.IdentityProvider], error)
// DeleteIdentityProvider deletes an identity provider.
DeleteIdentityProvider(context.Context, *connect.Request[v1.DeleteIdentityProviderRequest]) (*connect.Response[emptypb.Empty], error)
}
// NewIdentityProviderServiceClient constructs a client for the memos.api.v1.IdentityProviderService
// service. By default, it uses the Connect protocol with the binary Protobuf Codec, asks for
// gzipped responses, and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply
// the connect.WithGRPC() or connect.WithGRPCWeb() options.
//
// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
// http://api.acme.com or https://acme.com/grpc).
func NewIdentityProviderServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) IdentityProviderServiceClient {
baseURL = strings.TrimRight(baseURL, "/")
identityProviderServiceMethods := v1.File_api_v1_idp_service_proto.Services().ByName("IdentityProviderService").Methods()
return &identityProviderServiceClient{
listIdentityProviders: connect.NewClient[v1.ListIdentityProvidersRequest, v1.ListIdentityProvidersResponse](
httpClient,
baseURL+IdentityProviderServiceListIdentityProvidersProcedure,
connect.WithSchema(identityProviderServiceMethods.ByName("ListIdentityProviders")),
connect.WithClientOptions(opts...),
),
getIdentityProvider: connect.NewClient[v1.GetIdentityProviderRequest, v1.IdentityProvider](
httpClient,
baseURL+IdentityProviderServiceGetIdentityProviderProcedure,
connect.WithSchema(identityProviderServiceMethods.ByName("GetIdentityProvider")),
connect.WithClientOptions(opts...),
),
createIdentityProvider: connect.NewClient[v1.CreateIdentityProviderRequest, v1.IdentityProvider](
httpClient,
baseURL+IdentityProviderServiceCreateIdentityProviderProcedure,
connect.WithSchema(identityProviderServiceMethods.ByName("CreateIdentityProvider")),
connect.WithClientOptions(opts...),
),
updateIdentityProvider: connect.NewClient[v1.UpdateIdentityProviderRequest, v1.IdentityProvider](
httpClient,
baseURL+IdentityProviderServiceUpdateIdentityProviderProcedure,
connect.WithSchema(identityProviderServiceMethods.ByName("UpdateIdentityProvider")),
connect.WithClientOptions(opts...),
),
deleteIdentityProvider: connect.NewClient[v1.DeleteIdentityProviderRequest, emptypb.Empty](
httpClient,
baseURL+IdentityProviderServiceDeleteIdentityProviderProcedure,
connect.WithSchema(identityProviderServiceMethods.ByName("DeleteIdentityProvider")),
connect.WithClientOptions(opts...),
),
}
}
// identityProviderServiceClient implements IdentityProviderServiceClient.
type identityProviderServiceClient struct {
listIdentityProviders *connect.Client[v1.ListIdentityProvidersRequest, v1.ListIdentityProvidersResponse]
getIdentityProvider *connect.Client[v1.GetIdentityProviderRequest, v1.IdentityProvider]
createIdentityProvider *connect.Client[v1.CreateIdentityProviderRequest, v1.IdentityProvider]
updateIdentityProvider *connect.Client[v1.UpdateIdentityProviderRequest, v1.IdentityProvider]
deleteIdentityProvider *connect.Client[v1.DeleteIdentityProviderRequest, emptypb.Empty]
}
// ListIdentityProviders calls memos.api.v1.IdentityProviderService.ListIdentityProviders.
func (c *identityProviderServiceClient) ListIdentityProviders(ctx context.Context, req *connect.Request[v1.ListIdentityProvidersRequest]) (*connect.Response[v1.ListIdentityProvidersResponse], error) {
return c.listIdentityProviders.CallUnary(ctx, req)
}
// GetIdentityProvider calls memos.api.v1.IdentityProviderService.GetIdentityProvider.
func (c *identityProviderServiceClient) GetIdentityProvider(ctx context.Context, req *connect.Request[v1.GetIdentityProviderRequest]) (*connect.Response[v1.IdentityProvider], error) {
return c.getIdentityProvider.CallUnary(ctx, req)
}
// CreateIdentityProvider calls memos.api.v1.IdentityProviderService.CreateIdentityProvider.
func (c *identityProviderServiceClient) CreateIdentityProvider(ctx context.Context, req *connect.Request[v1.CreateIdentityProviderRequest]) (*connect.Response[v1.IdentityProvider], error) {
return c.createIdentityProvider.CallUnary(ctx, req)
}
// UpdateIdentityProvider calls memos.api.v1.IdentityProviderService.UpdateIdentityProvider.
func (c *identityProviderServiceClient) UpdateIdentityProvider(ctx context.Context, req *connect.Request[v1.UpdateIdentityProviderRequest]) (*connect.Response[v1.IdentityProvider], error) {
return c.updateIdentityProvider.CallUnary(ctx, req)
}
// DeleteIdentityProvider calls memos.api.v1.IdentityProviderService.DeleteIdentityProvider.
func (c *identityProviderServiceClient) DeleteIdentityProvider(ctx context.Context, req *connect.Request[v1.DeleteIdentityProviderRequest]) (*connect.Response[emptypb.Empty], error) {
return c.deleteIdentityProvider.CallUnary(ctx, req)
}
// IdentityProviderServiceHandler is an implementation of the memos.api.v1.IdentityProviderService
// service.
type IdentityProviderServiceHandler interface {
// ListIdentityProviders lists identity providers.
ListIdentityProviders(context.Context, *connect.Request[v1.ListIdentityProvidersRequest]) (*connect.Response[v1.ListIdentityProvidersResponse], error)
// GetIdentityProvider gets an identity provider.
GetIdentityProvider(context.Context, *connect.Request[v1.GetIdentityProviderRequest]) (*connect.Response[v1.IdentityProvider], error)
// CreateIdentityProvider creates an identity provider.
CreateIdentityProvider(context.Context, *connect.Request[v1.CreateIdentityProviderRequest]) (*connect.Response[v1.IdentityProvider], error)
// UpdateIdentityProvider updates an identity provider.
UpdateIdentityProvider(context.Context, *connect.Request[v1.UpdateIdentityProviderRequest]) (*connect.Response[v1.IdentityProvider], error)
// DeleteIdentityProvider deletes an identity provider.
DeleteIdentityProvider(context.Context, *connect.Request[v1.DeleteIdentityProviderRequest]) (*connect.Response[emptypb.Empty], error)
}
// NewIdentityProviderServiceHandler builds an HTTP handler from the service implementation. It
// returns the path on which to mount the handler and the handler itself.
//
// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
// and JSON codecs. They also support gzip compression.
func NewIdentityProviderServiceHandler(svc IdentityProviderServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
identityProviderServiceMethods := v1.File_api_v1_idp_service_proto.Services().ByName("IdentityProviderService").Methods()
identityProviderServiceListIdentityProvidersHandler := connect.NewUnaryHandler(
IdentityProviderServiceListIdentityProvidersProcedure,
svc.ListIdentityProviders,
connect.WithSchema(identityProviderServiceMethods.ByName("ListIdentityProviders")),
connect.WithHandlerOptions(opts...),
)
identityProviderServiceGetIdentityProviderHandler := connect.NewUnaryHandler(
IdentityProviderServiceGetIdentityProviderProcedure,
svc.GetIdentityProvider,
connect.WithSchema(identityProviderServiceMethods.ByName("GetIdentityProvider")),
connect.WithHandlerOptions(opts...),
)
identityProviderServiceCreateIdentityProviderHandler := connect.NewUnaryHandler(
IdentityProviderServiceCreateIdentityProviderProcedure,
svc.CreateIdentityProvider,
connect.WithSchema(identityProviderServiceMethods.ByName("CreateIdentityProvider")),
connect.WithHandlerOptions(opts...),
)
identityProviderServiceUpdateIdentityProviderHandler := connect.NewUnaryHandler(
IdentityProviderServiceUpdateIdentityProviderProcedure,
svc.UpdateIdentityProvider,
connect.WithSchema(identityProviderServiceMethods.ByName("UpdateIdentityProvider")),
connect.WithHandlerOptions(opts...),
)
identityProviderServiceDeleteIdentityProviderHandler := connect.NewUnaryHandler(
IdentityProviderServiceDeleteIdentityProviderProcedure,
svc.DeleteIdentityProvider,
connect.WithSchema(identityProviderServiceMethods.ByName("DeleteIdentityProvider")),
connect.WithHandlerOptions(opts...),
)
return "/memos.api.v1.IdentityProviderService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case IdentityProviderServiceListIdentityProvidersProcedure:
identityProviderServiceListIdentityProvidersHandler.ServeHTTP(w, r)
case IdentityProviderServiceGetIdentityProviderProcedure:
identityProviderServiceGetIdentityProviderHandler.ServeHTTP(w, r)
case IdentityProviderServiceCreateIdentityProviderProcedure:
identityProviderServiceCreateIdentityProviderHandler.ServeHTTP(w, r)
case IdentityProviderServiceUpdateIdentityProviderProcedure:
identityProviderServiceUpdateIdentityProviderHandler.ServeHTTP(w, r)
case IdentityProviderServiceDeleteIdentityProviderProcedure:
identityProviderServiceDeleteIdentityProviderHandler.ServeHTTP(w, r)
default:
http.NotFound(w, r)
}
})
}
// UnimplementedIdentityProviderServiceHandler returns CodeUnimplemented from all methods.
type UnimplementedIdentityProviderServiceHandler struct{}
func (UnimplementedIdentityProviderServiceHandler) ListIdentityProviders(context.Context, *connect.Request[v1.ListIdentityProvidersRequest]) (*connect.Response[v1.ListIdentityProvidersResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.IdentityProviderService.ListIdentityProviders is not implemented"))
}
func (UnimplementedIdentityProviderServiceHandler) GetIdentityProvider(context.Context, *connect.Request[v1.GetIdentityProviderRequest]) (*connect.Response[v1.IdentityProvider], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.IdentityProviderService.GetIdentityProvider is not implemented"))
}
func (UnimplementedIdentityProviderServiceHandler) CreateIdentityProvider(context.Context, *connect.Request[v1.CreateIdentityProviderRequest]) (*connect.Response[v1.IdentityProvider], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.IdentityProviderService.CreateIdentityProvider is not implemented"))
}
func (UnimplementedIdentityProviderServiceHandler) UpdateIdentityProvider(context.Context, *connect.Request[v1.UpdateIdentityProviderRequest]) (*connect.Response[v1.IdentityProvider], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.IdentityProviderService.UpdateIdentityProvider is not implemented"))
}
func (UnimplementedIdentityProviderServiceHandler) DeleteIdentityProvider(context.Context, *connect.Request[v1.DeleteIdentityProviderRequest]) (*connect.Response[emptypb.Empty], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.IdentityProviderService.DeleteIdentityProvider is not implemented"))
}

View File

@ -0,0 +1,173 @@
// Code generated by protoc-gen-connect-go. DO NOT EDIT.
//
// Source: api/v1/instance_service.proto
package apiv1connect
import (
connect "connectrpc.com/connect"
context "context"
errors "errors"
v1 "github.com/usememos/memos/proto/gen/api/v1"
http "net/http"
strings "strings"
)
// This is a compile-time assertion to ensure that this generated file and the connect package are
// compatible. If you get a compiler error that this constant is not defined, this code was
// generated with a version of connect newer than the one compiled into your binary. You can fix the
// problem by either regenerating this code with an older version of connect or updating the connect
// version compiled into your binary.
const _ = connect.IsAtLeastVersion1_13_0
const (
// InstanceServiceName is the fully-qualified name of the InstanceService service.
InstanceServiceName = "memos.api.v1.InstanceService"
)
// These constants are the fully-qualified names of the RPCs defined in this package. They're
// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
//
// Note that these are different from the fully-qualified method names used by
// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
// period.
const (
// InstanceServiceGetInstanceProfileProcedure is the fully-qualified name of the InstanceService's
// GetInstanceProfile RPC.
InstanceServiceGetInstanceProfileProcedure = "/memos.api.v1.InstanceService/GetInstanceProfile"
// InstanceServiceGetInstanceSettingProcedure is the fully-qualified name of the InstanceService's
// GetInstanceSetting RPC.
InstanceServiceGetInstanceSettingProcedure = "/memos.api.v1.InstanceService/GetInstanceSetting"
// InstanceServiceUpdateInstanceSettingProcedure is the fully-qualified name of the
// InstanceService's UpdateInstanceSetting RPC.
InstanceServiceUpdateInstanceSettingProcedure = "/memos.api.v1.InstanceService/UpdateInstanceSetting"
)
// InstanceServiceClient is a client for the memos.api.v1.InstanceService service.
type InstanceServiceClient interface {
// Gets the instance profile.
GetInstanceProfile(context.Context, *connect.Request[v1.GetInstanceProfileRequest]) (*connect.Response[v1.InstanceProfile], error)
// Gets an instance setting.
GetInstanceSetting(context.Context, *connect.Request[v1.GetInstanceSettingRequest]) (*connect.Response[v1.InstanceSetting], error)
// Updates an instance setting.
UpdateInstanceSetting(context.Context, *connect.Request[v1.UpdateInstanceSettingRequest]) (*connect.Response[v1.InstanceSetting], error)
}
// NewInstanceServiceClient constructs a client for the memos.api.v1.InstanceService service. By
// default, it uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses,
// and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the
// connect.WithGRPC() or connect.WithGRPCWeb() options.
//
// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
// http://api.acme.com or https://acme.com/grpc).
func NewInstanceServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) InstanceServiceClient {
baseURL = strings.TrimRight(baseURL, "/")
instanceServiceMethods := v1.File_api_v1_instance_service_proto.Services().ByName("InstanceService").Methods()
return &instanceServiceClient{
getInstanceProfile: connect.NewClient[v1.GetInstanceProfileRequest, v1.InstanceProfile](
httpClient,
baseURL+InstanceServiceGetInstanceProfileProcedure,
connect.WithSchema(instanceServiceMethods.ByName("GetInstanceProfile")),
connect.WithClientOptions(opts...),
),
getInstanceSetting: connect.NewClient[v1.GetInstanceSettingRequest, v1.InstanceSetting](
httpClient,
baseURL+InstanceServiceGetInstanceSettingProcedure,
connect.WithSchema(instanceServiceMethods.ByName("GetInstanceSetting")),
connect.WithClientOptions(opts...),
),
updateInstanceSetting: connect.NewClient[v1.UpdateInstanceSettingRequest, v1.InstanceSetting](
httpClient,
baseURL+InstanceServiceUpdateInstanceSettingProcedure,
connect.WithSchema(instanceServiceMethods.ByName("UpdateInstanceSetting")),
connect.WithClientOptions(opts...),
),
}
}
// instanceServiceClient implements InstanceServiceClient.
type instanceServiceClient struct {
getInstanceProfile *connect.Client[v1.GetInstanceProfileRequest, v1.InstanceProfile]
getInstanceSetting *connect.Client[v1.GetInstanceSettingRequest, v1.InstanceSetting]
updateInstanceSetting *connect.Client[v1.UpdateInstanceSettingRequest, v1.InstanceSetting]
}
// GetInstanceProfile calls memos.api.v1.InstanceService.GetInstanceProfile.
func (c *instanceServiceClient) GetInstanceProfile(ctx context.Context, req *connect.Request[v1.GetInstanceProfileRequest]) (*connect.Response[v1.InstanceProfile], error) {
return c.getInstanceProfile.CallUnary(ctx, req)
}
// GetInstanceSetting calls memos.api.v1.InstanceService.GetInstanceSetting.
func (c *instanceServiceClient) GetInstanceSetting(ctx context.Context, req *connect.Request[v1.GetInstanceSettingRequest]) (*connect.Response[v1.InstanceSetting], error) {
return c.getInstanceSetting.CallUnary(ctx, req)
}
// UpdateInstanceSetting calls memos.api.v1.InstanceService.UpdateInstanceSetting.
func (c *instanceServiceClient) UpdateInstanceSetting(ctx context.Context, req *connect.Request[v1.UpdateInstanceSettingRequest]) (*connect.Response[v1.InstanceSetting], error) {
return c.updateInstanceSetting.CallUnary(ctx, req)
}
// InstanceServiceHandler is an implementation of the memos.api.v1.InstanceService service.
type InstanceServiceHandler interface {
// Gets the instance profile.
GetInstanceProfile(context.Context, *connect.Request[v1.GetInstanceProfileRequest]) (*connect.Response[v1.InstanceProfile], error)
// Gets an instance setting.
GetInstanceSetting(context.Context, *connect.Request[v1.GetInstanceSettingRequest]) (*connect.Response[v1.InstanceSetting], error)
// Updates an instance setting.
UpdateInstanceSetting(context.Context, *connect.Request[v1.UpdateInstanceSettingRequest]) (*connect.Response[v1.InstanceSetting], error)
}
// NewInstanceServiceHandler builds an HTTP handler from the service implementation. It returns the
// path on which to mount the handler and the handler itself.
//
// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
// and JSON codecs. They also support gzip compression.
func NewInstanceServiceHandler(svc InstanceServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
instanceServiceMethods := v1.File_api_v1_instance_service_proto.Services().ByName("InstanceService").Methods()
instanceServiceGetInstanceProfileHandler := connect.NewUnaryHandler(
InstanceServiceGetInstanceProfileProcedure,
svc.GetInstanceProfile,
connect.WithSchema(instanceServiceMethods.ByName("GetInstanceProfile")),
connect.WithHandlerOptions(opts...),
)
instanceServiceGetInstanceSettingHandler := connect.NewUnaryHandler(
InstanceServiceGetInstanceSettingProcedure,
svc.GetInstanceSetting,
connect.WithSchema(instanceServiceMethods.ByName("GetInstanceSetting")),
connect.WithHandlerOptions(opts...),
)
instanceServiceUpdateInstanceSettingHandler := connect.NewUnaryHandler(
InstanceServiceUpdateInstanceSettingProcedure,
svc.UpdateInstanceSetting,
connect.WithSchema(instanceServiceMethods.ByName("UpdateInstanceSetting")),
connect.WithHandlerOptions(opts...),
)
return "/memos.api.v1.InstanceService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case InstanceServiceGetInstanceProfileProcedure:
instanceServiceGetInstanceProfileHandler.ServeHTTP(w, r)
case InstanceServiceGetInstanceSettingProcedure:
instanceServiceGetInstanceSettingHandler.ServeHTTP(w, r)
case InstanceServiceUpdateInstanceSettingProcedure:
instanceServiceUpdateInstanceSettingHandler.ServeHTTP(w, r)
default:
http.NotFound(w, r)
}
})
}
// UnimplementedInstanceServiceHandler returns CodeUnimplemented from all methods.
type UnimplementedInstanceServiceHandler struct{}
func (UnimplementedInstanceServiceHandler) GetInstanceProfile(context.Context, *connect.Request[v1.GetInstanceProfileRequest]) (*connect.Response[v1.InstanceProfile], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.InstanceService.GetInstanceProfile is not implemented"))
}
func (UnimplementedInstanceServiceHandler) GetInstanceSetting(context.Context, *connect.Request[v1.GetInstanceSettingRequest]) (*connect.Response[v1.InstanceSetting], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.InstanceService.GetInstanceSetting is not implemented"))
}
func (UnimplementedInstanceServiceHandler) UpdateInstanceSetting(context.Context, *connect.Request[v1.UpdateInstanceSettingRequest]) (*connect.Response[v1.InstanceSetting], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.InstanceService.UpdateInstanceSetting is not implemented"))
}

View File

@ -0,0 +1,510 @@
// Code generated by protoc-gen-connect-go. DO NOT EDIT.
//
// Source: api/v1/memo_service.proto
package apiv1connect
import (
connect "connectrpc.com/connect"
context "context"
errors "errors"
v1 "github.com/usememos/memos/proto/gen/api/v1"
emptypb "google.golang.org/protobuf/types/known/emptypb"
http "net/http"
strings "strings"
)
// This is a compile-time assertion to ensure that this generated file and the connect package are
// compatible. If you get a compiler error that this constant is not defined, this code was
// generated with a version of connect newer than the one compiled into your binary. You can fix the
// problem by either regenerating this code with an older version of connect or updating the connect
// version compiled into your binary.
const _ = connect.IsAtLeastVersion1_13_0
const (
// MemoServiceName is the fully-qualified name of the MemoService service.
MemoServiceName = "memos.api.v1.MemoService"
)
// These constants are the fully-qualified names of the RPCs defined in this package. They're
// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
//
// Note that these are different from the fully-qualified method names used by
// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
// period.
const (
// MemoServiceCreateMemoProcedure is the fully-qualified name of the MemoService's CreateMemo RPC.
MemoServiceCreateMemoProcedure = "/memos.api.v1.MemoService/CreateMemo"
// MemoServiceListMemosProcedure is the fully-qualified name of the MemoService's ListMemos RPC.
MemoServiceListMemosProcedure = "/memos.api.v1.MemoService/ListMemos"
// MemoServiceGetMemoProcedure is the fully-qualified name of the MemoService's GetMemo RPC.
MemoServiceGetMemoProcedure = "/memos.api.v1.MemoService/GetMemo"
// MemoServiceUpdateMemoProcedure is the fully-qualified name of the MemoService's UpdateMemo RPC.
MemoServiceUpdateMemoProcedure = "/memos.api.v1.MemoService/UpdateMemo"
// MemoServiceDeleteMemoProcedure is the fully-qualified name of the MemoService's DeleteMemo RPC.
MemoServiceDeleteMemoProcedure = "/memos.api.v1.MemoService/DeleteMemo"
// MemoServiceSetMemoAttachmentsProcedure is the fully-qualified name of the MemoService's
// SetMemoAttachments RPC.
MemoServiceSetMemoAttachmentsProcedure = "/memos.api.v1.MemoService/SetMemoAttachments"
// MemoServiceListMemoAttachmentsProcedure is the fully-qualified name of the MemoService's
// ListMemoAttachments RPC.
MemoServiceListMemoAttachmentsProcedure = "/memos.api.v1.MemoService/ListMemoAttachments"
// MemoServiceSetMemoRelationsProcedure is the fully-qualified name of the MemoService's
// SetMemoRelations RPC.
MemoServiceSetMemoRelationsProcedure = "/memos.api.v1.MemoService/SetMemoRelations"
// MemoServiceListMemoRelationsProcedure is the fully-qualified name of the MemoService's
// ListMemoRelations RPC.
MemoServiceListMemoRelationsProcedure = "/memos.api.v1.MemoService/ListMemoRelations"
// MemoServiceCreateMemoCommentProcedure is the fully-qualified name of the MemoService's
// CreateMemoComment RPC.
MemoServiceCreateMemoCommentProcedure = "/memos.api.v1.MemoService/CreateMemoComment"
// MemoServiceListMemoCommentsProcedure is the fully-qualified name of the MemoService's
// ListMemoComments RPC.
MemoServiceListMemoCommentsProcedure = "/memos.api.v1.MemoService/ListMemoComments"
// MemoServiceListMemoReactionsProcedure is the fully-qualified name of the MemoService's
// ListMemoReactions RPC.
MemoServiceListMemoReactionsProcedure = "/memos.api.v1.MemoService/ListMemoReactions"
// MemoServiceUpsertMemoReactionProcedure is the fully-qualified name of the MemoService's
// UpsertMemoReaction RPC.
MemoServiceUpsertMemoReactionProcedure = "/memos.api.v1.MemoService/UpsertMemoReaction"
// MemoServiceDeleteMemoReactionProcedure is the fully-qualified name of the MemoService's
// DeleteMemoReaction RPC.
MemoServiceDeleteMemoReactionProcedure = "/memos.api.v1.MemoService/DeleteMemoReaction"
)
// MemoServiceClient is a client for the memos.api.v1.MemoService service.
type MemoServiceClient interface {
// CreateMemo creates a memo.
CreateMemo(context.Context, *connect.Request[v1.CreateMemoRequest]) (*connect.Response[v1.Memo], error)
// ListMemos lists memos with pagination and filter.
ListMemos(context.Context, *connect.Request[v1.ListMemosRequest]) (*connect.Response[v1.ListMemosResponse], error)
// GetMemo gets a memo.
GetMemo(context.Context, *connect.Request[v1.GetMemoRequest]) (*connect.Response[v1.Memo], error)
// UpdateMemo updates a memo.
UpdateMemo(context.Context, *connect.Request[v1.UpdateMemoRequest]) (*connect.Response[v1.Memo], error)
// DeleteMemo deletes a memo.
DeleteMemo(context.Context, *connect.Request[v1.DeleteMemoRequest]) (*connect.Response[emptypb.Empty], error)
// SetMemoAttachments sets attachments for a memo.
SetMemoAttachments(context.Context, *connect.Request[v1.SetMemoAttachmentsRequest]) (*connect.Response[emptypb.Empty], error)
// ListMemoAttachments lists attachments for a memo.
ListMemoAttachments(context.Context, *connect.Request[v1.ListMemoAttachmentsRequest]) (*connect.Response[v1.ListMemoAttachmentsResponse], error)
// SetMemoRelations sets relations for a memo.
SetMemoRelations(context.Context, *connect.Request[v1.SetMemoRelationsRequest]) (*connect.Response[emptypb.Empty], error)
// ListMemoRelations lists relations for a memo.
ListMemoRelations(context.Context, *connect.Request[v1.ListMemoRelationsRequest]) (*connect.Response[v1.ListMemoRelationsResponse], error)
// CreateMemoComment creates a comment for a memo.
CreateMemoComment(context.Context, *connect.Request[v1.CreateMemoCommentRequest]) (*connect.Response[v1.Memo], error)
// ListMemoComments lists comments for a memo.
ListMemoComments(context.Context, *connect.Request[v1.ListMemoCommentsRequest]) (*connect.Response[v1.ListMemoCommentsResponse], error)
// ListMemoReactions lists reactions for a memo.
ListMemoReactions(context.Context, *connect.Request[v1.ListMemoReactionsRequest]) (*connect.Response[v1.ListMemoReactionsResponse], error)
// UpsertMemoReaction upserts a reaction for a memo.
UpsertMemoReaction(context.Context, *connect.Request[v1.UpsertMemoReactionRequest]) (*connect.Response[v1.Reaction], error)
// DeleteMemoReaction deletes a reaction for a memo.
DeleteMemoReaction(context.Context, *connect.Request[v1.DeleteMemoReactionRequest]) (*connect.Response[emptypb.Empty], error)
}
// NewMemoServiceClient constructs a client for the memos.api.v1.MemoService service. By default, it
// uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends
// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or
// connect.WithGRPCWeb() options.
//
// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
// http://api.acme.com or https://acme.com/grpc).
func NewMemoServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) MemoServiceClient {
baseURL = strings.TrimRight(baseURL, "/")
memoServiceMethods := v1.File_api_v1_memo_service_proto.Services().ByName("MemoService").Methods()
return &memoServiceClient{
createMemo: connect.NewClient[v1.CreateMemoRequest, v1.Memo](
httpClient,
baseURL+MemoServiceCreateMemoProcedure,
connect.WithSchema(memoServiceMethods.ByName("CreateMemo")),
connect.WithClientOptions(opts...),
),
listMemos: connect.NewClient[v1.ListMemosRequest, v1.ListMemosResponse](
httpClient,
baseURL+MemoServiceListMemosProcedure,
connect.WithSchema(memoServiceMethods.ByName("ListMemos")),
connect.WithClientOptions(opts...),
),
getMemo: connect.NewClient[v1.GetMemoRequest, v1.Memo](
httpClient,
baseURL+MemoServiceGetMemoProcedure,
connect.WithSchema(memoServiceMethods.ByName("GetMemo")),
connect.WithClientOptions(opts...),
),
updateMemo: connect.NewClient[v1.UpdateMemoRequest, v1.Memo](
httpClient,
baseURL+MemoServiceUpdateMemoProcedure,
connect.WithSchema(memoServiceMethods.ByName("UpdateMemo")),
connect.WithClientOptions(opts...),
),
deleteMemo: connect.NewClient[v1.DeleteMemoRequest, emptypb.Empty](
httpClient,
baseURL+MemoServiceDeleteMemoProcedure,
connect.WithSchema(memoServiceMethods.ByName("DeleteMemo")),
connect.WithClientOptions(opts...),
),
setMemoAttachments: connect.NewClient[v1.SetMemoAttachmentsRequest, emptypb.Empty](
httpClient,
baseURL+MemoServiceSetMemoAttachmentsProcedure,
connect.WithSchema(memoServiceMethods.ByName("SetMemoAttachments")),
connect.WithClientOptions(opts...),
),
listMemoAttachments: connect.NewClient[v1.ListMemoAttachmentsRequest, v1.ListMemoAttachmentsResponse](
httpClient,
baseURL+MemoServiceListMemoAttachmentsProcedure,
connect.WithSchema(memoServiceMethods.ByName("ListMemoAttachments")),
connect.WithClientOptions(opts...),
),
setMemoRelations: connect.NewClient[v1.SetMemoRelationsRequest, emptypb.Empty](
httpClient,
baseURL+MemoServiceSetMemoRelationsProcedure,
connect.WithSchema(memoServiceMethods.ByName("SetMemoRelations")),
connect.WithClientOptions(opts...),
),
listMemoRelations: connect.NewClient[v1.ListMemoRelationsRequest, v1.ListMemoRelationsResponse](
httpClient,
baseURL+MemoServiceListMemoRelationsProcedure,
connect.WithSchema(memoServiceMethods.ByName("ListMemoRelations")),
connect.WithClientOptions(opts...),
),
createMemoComment: connect.NewClient[v1.CreateMemoCommentRequest, v1.Memo](
httpClient,
baseURL+MemoServiceCreateMemoCommentProcedure,
connect.WithSchema(memoServiceMethods.ByName("CreateMemoComment")),
connect.WithClientOptions(opts...),
),
listMemoComments: connect.NewClient[v1.ListMemoCommentsRequest, v1.ListMemoCommentsResponse](
httpClient,
baseURL+MemoServiceListMemoCommentsProcedure,
connect.WithSchema(memoServiceMethods.ByName("ListMemoComments")),
connect.WithClientOptions(opts...),
),
listMemoReactions: connect.NewClient[v1.ListMemoReactionsRequest, v1.ListMemoReactionsResponse](
httpClient,
baseURL+MemoServiceListMemoReactionsProcedure,
connect.WithSchema(memoServiceMethods.ByName("ListMemoReactions")),
connect.WithClientOptions(opts...),
),
upsertMemoReaction: connect.NewClient[v1.UpsertMemoReactionRequest, v1.Reaction](
httpClient,
baseURL+MemoServiceUpsertMemoReactionProcedure,
connect.WithSchema(memoServiceMethods.ByName("UpsertMemoReaction")),
connect.WithClientOptions(opts...),
),
deleteMemoReaction: connect.NewClient[v1.DeleteMemoReactionRequest, emptypb.Empty](
httpClient,
baseURL+MemoServiceDeleteMemoReactionProcedure,
connect.WithSchema(memoServiceMethods.ByName("DeleteMemoReaction")),
connect.WithClientOptions(opts...),
),
}
}
// memoServiceClient implements MemoServiceClient.
type memoServiceClient struct {
createMemo *connect.Client[v1.CreateMemoRequest, v1.Memo]
listMemos *connect.Client[v1.ListMemosRequest, v1.ListMemosResponse]
getMemo *connect.Client[v1.GetMemoRequest, v1.Memo]
updateMemo *connect.Client[v1.UpdateMemoRequest, v1.Memo]
deleteMemo *connect.Client[v1.DeleteMemoRequest, emptypb.Empty]
setMemoAttachments *connect.Client[v1.SetMemoAttachmentsRequest, emptypb.Empty]
listMemoAttachments *connect.Client[v1.ListMemoAttachmentsRequest, v1.ListMemoAttachmentsResponse]
setMemoRelations *connect.Client[v1.SetMemoRelationsRequest, emptypb.Empty]
listMemoRelations *connect.Client[v1.ListMemoRelationsRequest, v1.ListMemoRelationsResponse]
createMemoComment *connect.Client[v1.CreateMemoCommentRequest, v1.Memo]
listMemoComments *connect.Client[v1.ListMemoCommentsRequest, v1.ListMemoCommentsResponse]
listMemoReactions *connect.Client[v1.ListMemoReactionsRequest, v1.ListMemoReactionsResponse]
upsertMemoReaction *connect.Client[v1.UpsertMemoReactionRequest, v1.Reaction]
deleteMemoReaction *connect.Client[v1.DeleteMemoReactionRequest, emptypb.Empty]
}
// CreateMemo calls memos.api.v1.MemoService.CreateMemo.
func (c *memoServiceClient) CreateMemo(ctx context.Context, req *connect.Request[v1.CreateMemoRequest]) (*connect.Response[v1.Memo], error) {
return c.createMemo.CallUnary(ctx, req)
}
// ListMemos calls memos.api.v1.MemoService.ListMemos.
func (c *memoServiceClient) ListMemos(ctx context.Context, req *connect.Request[v1.ListMemosRequest]) (*connect.Response[v1.ListMemosResponse], error) {
return c.listMemos.CallUnary(ctx, req)
}
// GetMemo calls memos.api.v1.MemoService.GetMemo.
func (c *memoServiceClient) GetMemo(ctx context.Context, req *connect.Request[v1.GetMemoRequest]) (*connect.Response[v1.Memo], error) {
return c.getMemo.CallUnary(ctx, req)
}
// UpdateMemo calls memos.api.v1.MemoService.UpdateMemo.
func (c *memoServiceClient) UpdateMemo(ctx context.Context, req *connect.Request[v1.UpdateMemoRequest]) (*connect.Response[v1.Memo], error) {
return c.updateMemo.CallUnary(ctx, req)
}
// DeleteMemo calls memos.api.v1.MemoService.DeleteMemo.
func (c *memoServiceClient) DeleteMemo(ctx context.Context, req *connect.Request[v1.DeleteMemoRequest]) (*connect.Response[emptypb.Empty], error) {
return c.deleteMemo.CallUnary(ctx, req)
}
// SetMemoAttachments calls memos.api.v1.MemoService.SetMemoAttachments.
func (c *memoServiceClient) SetMemoAttachments(ctx context.Context, req *connect.Request[v1.SetMemoAttachmentsRequest]) (*connect.Response[emptypb.Empty], error) {
return c.setMemoAttachments.CallUnary(ctx, req)
}
// ListMemoAttachments calls memos.api.v1.MemoService.ListMemoAttachments.
func (c *memoServiceClient) ListMemoAttachments(ctx context.Context, req *connect.Request[v1.ListMemoAttachmentsRequest]) (*connect.Response[v1.ListMemoAttachmentsResponse], error) {
return c.listMemoAttachments.CallUnary(ctx, req)
}
// SetMemoRelations calls memos.api.v1.MemoService.SetMemoRelations.
func (c *memoServiceClient) SetMemoRelations(ctx context.Context, req *connect.Request[v1.SetMemoRelationsRequest]) (*connect.Response[emptypb.Empty], error) {
return c.setMemoRelations.CallUnary(ctx, req)
}
// ListMemoRelations calls memos.api.v1.MemoService.ListMemoRelations.
func (c *memoServiceClient) ListMemoRelations(ctx context.Context, req *connect.Request[v1.ListMemoRelationsRequest]) (*connect.Response[v1.ListMemoRelationsResponse], error) {
return c.listMemoRelations.CallUnary(ctx, req)
}
// CreateMemoComment calls memos.api.v1.MemoService.CreateMemoComment.
func (c *memoServiceClient) CreateMemoComment(ctx context.Context, req *connect.Request[v1.CreateMemoCommentRequest]) (*connect.Response[v1.Memo], error) {
return c.createMemoComment.CallUnary(ctx, req)
}
// ListMemoComments calls memos.api.v1.MemoService.ListMemoComments.
func (c *memoServiceClient) ListMemoComments(ctx context.Context, req *connect.Request[v1.ListMemoCommentsRequest]) (*connect.Response[v1.ListMemoCommentsResponse], error) {
return c.listMemoComments.CallUnary(ctx, req)
}
// ListMemoReactions calls memos.api.v1.MemoService.ListMemoReactions.
func (c *memoServiceClient) ListMemoReactions(ctx context.Context, req *connect.Request[v1.ListMemoReactionsRequest]) (*connect.Response[v1.ListMemoReactionsResponse], error) {
return c.listMemoReactions.CallUnary(ctx, req)
}
// UpsertMemoReaction calls memos.api.v1.MemoService.UpsertMemoReaction.
func (c *memoServiceClient) UpsertMemoReaction(ctx context.Context, req *connect.Request[v1.UpsertMemoReactionRequest]) (*connect.Response[v1.Reaction], error) {
return c.upsertMemoReaction.CallUnary(ctx, req)
}
// DeleteMemoReaction calls memos.api.v1.MemoService.DeleteMemoReaction.
func (c *memoServiceClient) DeleteMemoReaction(ctx context.Context, req *connect.Request[v1.DeleteMemoReactionRequest]) (*connect.Response[emptypb.Empty], error) {
return c.deleteMemoReaction.CallUnary(ctx, req)
}
// MemoServiceHandler is an implementation of the memos.api.v1.MemoService service.
type MemoServiceHandler interface {
// CreateMemo creates a memo.
CreateMemo(context.Context, *connect.Request[v1.CreateMemoRequest]) (*connect.Response[v1.Memo], error)
// ListMemos lists memos with pagination and filter.
ListMemos(context.Context, *connect.Request[v1.ListMemosRequest]) (*connect.Response[v1.ListMemosResponse], error)
// GetMemo gets a memo.
GetMemo(context.Context, *connect.Request[v1.GetMemoRequest]) (*connect.Response[v1.Memo], error)
// UpdateMemo updates a memo.
UpdateMemo(context.Context, *connect.Request[v1.UpdateMemoRequest]) (*connect.Response[v1.Memo], error)
// DeleteMemo deletes a memo.
DeleteMemo(context.Context, *connect.Request[v1.DeleteMemoRequest]) (*connect.Response[emptypb.Empty], error)
// SetMemoAttachments sets attachments for a memo.
SetMemoAttachments(context.Context, *connect.Request[v1.SetMemoAttachmentsRequest]) (*connect.Response[emptypb.Empty], error)
// ListMemoAttachments lists attachments for a memo.
ListMemoAttachments(context.Context, *connect.Request[v1.ListMemoAttachmentsRequest]) (*connect.Response[v1.ListMemoAttachmentsResponse], error)
// SetMemoRelations sets relations for a memo.
SetMemoRelations(context.Context, *connect.Request[v1.SetMemoRelationsRequest]) (*connect.Response[emptypb.Empty], error)
// ListMemoRelations lists relations for a memo.
ListMemoRelations(context.Context, *connect.Request[v1.ListMemoRelationsRequest]) (*connect.Response[v1.ListMemoRelationsResponse], error)
// CreateMemoComment creates a comment for a memo.
CreateMemoComment(context.Context, *connect.Request[v1.CreateMemoCommentRequest]) (*connect.Response[v1.Memo], error)
// ListMemoComments lists comments for a memo.
ListMemoComments(context.Context, *connect.Request[v1.ListMemoCommentsRequest]) (*connect.Response[v1.ListMemoCommentsResponse], error)
// ListMemoReactions lists reactions for a memo.
ListMemoReactions(context.Context, *connect.Request[v1.ListMemoReactionsRequest]) (*connect.Response[v1.ListMemoReactionsResponse], error)
// UpsertMemoReaction upserts a reaction for a memo.
UpsertMemoReaction(context.Context, *connect.Request[v1.UpsertMemoReactionRequest]) (*connect.Response[v1.Reaction], error)
// DeleteMemoReaction deletes a reaction for a memo.
DeleteMemoReaction(context.Context, *connect.Request[v1.DeleteMemoReactionRequest]) (*connect.Response[emptypb.Empty], error)
}
// NewMemoServiceHandler builds an HTTP handler from the service implementation. It returns the path
// on which to mount the handler and the handler itself.
//
// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
// and JSON codecs. They also support gzip compression.
func NewMemoServiceHandler(svc MemoServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
memoServiceMethods := v1.File_api_v1_memo_service_proto.Services().ByName("MemoService").Methods()
memoServiceCreateMemoHandler := connect.NewUnaryHandler(
MemoServiceCreateMemoProcedure,
svc.CreateMemo,
connect.WithSchema(memoServiceMethods.ByName("CreateMemo")),
connect.WithHandlerOptions(opts...),
)
memoServiceListMemosHandler := connect.NewUnaryHandler(
MemoServiceListMemosProcedure,
svc.ListMemos,
connect.WithSchema(memoServiceMethods.ByName("ListMemos")),
connect.WithHandlerOptions(opts...),
)
memoServiceGetMemoHandler := connect.NewUnaryHandler(
MemoServiceGetMemoProcedure,
svc.GetMemo,
connect.WithSchema(memoServiceMethods.ByName("GetMemo")),
connect.WithHandlerOptions(opts...),
)
memoServiceUpdateMemoHandler := connect.NewUnaryHandler(
MemoServiceUpdateMemoProcedure,
svc.UpdateMemo,
connect.WithSchema(memoServiceMethods.ByName("UpdateMemo")),
connect.WithHandlerOptions(opts...),
)
memoServiceDeleteMemoHandler := connect.NewUnaryHandler(
MemoServiceDeleteMemoProcedure,
svc.DeleteMemo,
connect.WithSchema(memoServiceMethods.ByName("DeleteMemo")),
connect.WithHandlerOptions(opts...),
)
memoServiceSetMemoAttachmentsHandler := connect.NewUnaryHandler(
MemoServiceSetMemoAttachmentsProcedure,
svc.SetMemoAttachments,
connect.WithSchema(memoServiceMethods.ByName("SetMemoAttachments")),
connect.WithHandlerOptions(opts...),
)
memoServiceListMemoAttachmentsHandler := connect.NewUnaryHandler(
MemoServiceListMemoAttachmentsProcedure,
svc.ListMemoAttachments,
connect.WithSchema(memoServiceMethods.ByName("ListMemoAttachments")),
connect.WithHandlerOptions(opts...),
)
memoServiceSetMemoRelationsHandler := connect.NewUnaryHandler(
MemoServiceSetMemoRelationsProcedure,
svc.SetMemoRelations,
connect.WithSchema(memoServiceMethods.ByName("SetMemoRelations")),
connect.WithHandlerOptions(opts...),
)
memoServiceListMemoRelationsHandler := connect.NewUnaryHandler(
MemoServiceListMemoRelationsProcedure,
svc.ListMemoRelations,
connect.WithSchema(memoServiceMethods.ByName("ListMemoRelations")),
connect.WithHandlerOptions(opts...),
)
memoServiceCreateMemoCommentHandler := connect.NewUnaryHandler(
MemoServiceCreateMemoCommentProcedure,
svc.CreateMemoComment,
connect.WithSchema(memoServiceMethods.ByName("CreateMemoComment")),
connect.WithHandlerOptions(opts...),
)
memoServiceListMemoCommentsHandler := connect.NewUnaryHandler(
MemoServiceListMemoCommentsProcedure,
svc.ListMemoComments,
connect.WithSchema(memoServiceMethods.ByName("ListMemoComments")),
connect.WithHandlerOptions(opts...),
)
memoServiceListMemoReactionsHandler := connect.NewUnaryHandler(
MemoServiceListMemoReactionsProcedure,
svc.ListMemoReactions,
connect.WithSchema(memoServiceMethods.ByName("ListMemoReactions")),
connect.WithHandlerOptions(opts...),
)
memoServiceUpsertMemoReactionHandler := connect.NewUnaryHandler(
MemoServiceUpsertMemoReactionProcedure,
svc.UpsertMemoReaction,
connect.WithSchema(memoServiceMethods.ByName("UpsertMemoReaction")),
connect.WithHandlerOptions(opts...),
)
memoServiceDeleteMemoReactionHandler := connect.NewUnaryHandler(
MemoServiceDeleteMemoReactionProcedure,
svc.DeleteMemoReaction,
connect.WithSchema(memoServiceMethods.ByName("DeleteMemoReaction")),
connect.WithHandlerOptions(opts...),
)
return "/memos.api.v1.MemoService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case MemoServiceCreateMemoProcedure:
memoServiceCreateMemoHandler.ServeHTTP(w, r)
case MemoServiceListMemosProcedure:
memoServiceListMemosHandler.ServeHTTP(w, r)
case MemoServiceGetMemoProcedure:
memoServiceGetMemoHandler.ServeHTTP(w, r)
case MemoServiceUpdateMemoProcedure:
memoServiceUpdateMemoHandler.ServeHTTP(w, r)
case MemoServiceDeleteMemoProcedure:
memoServiceDeleteMemoHandler.ServeHTTP(w, r)
case MemoServiceSetMemoAttachmentsProcedure:
memoServiceSetMemoAttachmentsHandler.ServeHTTP(w, r)
case MemoServiceListMemoAttachmentsProcedure:
memoServiceListMemoAttachmentsHandler.ServeHTTP(w, r)
case MemoServiceSetMemoRelationsProcedure:
memoServiceSetMemoRelationsHandler.ServeHTTP(w, r)
case MemoServiceListMemoRelationsProcedure:
memoServiceListMemoRelationsHandler.ServeHTTP(w, r)
case MemoServiceCreateMemoCommentProcedure:
memoServiceCreateMemoCommentHandler.ServeHTTP(w, r)
case MemoServiceListMemoCommentsProcedure:
memoServiceListMemoCommentsHandler.ServeHTTP(w, r)
case MemoServiceListMemoReactionsProcedure:
memoServiceListMemoReactionsHandler.ServeHTTP(w, r)
case MemoServiceUpsertMemoReactionProcedure:
memoServiceUpsertMemoReactionHandler.ServeHTTP(w, r)
case MemoServiceDeleteMemoReactionProcedure:
memoServiceDeleteMemoReactionHandler.ServeHTTP(w, r)
default:
http.NotFound(w, r)
}
})
}
// UnimplementedMemoServiceHandler returns CodeUnimplemented from all methods.
type UnimplementedMemoServiceHandler struct{}
func (UnimplementedMemoServiceHandler) CreateMemo(context.Context, *connect.Request[v1.CreateMemoRequest]) (*connect.Response[v1.Memo], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.CreateMemo is not implemented"))
}
func (UnimplementedMemoServiceHandler) ListMemos(context.Context, *connect.Request[v1.ListMemosRequest]) (*connect.Response[v1.ListMemosResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.ListMemos is not implemented"))
}
func (UnimplementedMemoServiceHandler) GetMemo(context.Context, *connect.Request[v1.GetMemoRequest]) (*connect.Response[v1.Memo], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.GetMemo is not implemented"))
}
func (UnimplementedMemoServiceHandler) UpdateMemo(context.Context, *connect.Request[v1.UpdateMemoRequest]) (*connect.Response[v1.Memo], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.UpdateMemo is not implemented"))
}
func (UnimplementedMemoServiceHandler) DeleteMemo(context.Context, *connect.Request[v1.DeleteMemoRequest]) (*connect.Response[emptypb.Empty], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.DeleteMemo is not implemented"))
}
func (UnimplementedMemoServiceHandler) SetMemoAttachments(context.Context, *connect.Request[v1.SetMemoAttachmentsRequest]) (*connect.Response[emptypb.Empty], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.SetMemoAttachments is not implemented"))
}
func (UnimplementedMemoServiceHandler) ListMemoAttachments(context.Context, *connect.Request[v1.ListMemoAttachmentsRequest]) (*connect.Response[v1.ListMemoAttachmentsResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.ListMemoAttachments is not implemented"))
}
func (UnimplementedMemoServiceHandler) SetMemoRelations(context.Context, *connect.Request[v1.SetMemoRelationsRequest]) (*connect.Response[emptypb.Empty], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.SetMemoRelations is not implemented"))
}
func (UnimplementedMemoServiceHandler) ListMemoRelations(context.Context, *connect.Request[v1.ListMemoRelationsRequest]) (*connect.Response[v1.ListMemoRelationsResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.ListMemoRelations is not implemented"))
}
func (UnimplementedMemoServiceHandler) CreateMemoComment(context.Context, *connect.Request[v1.CreateMemoCommentRequest]) (*connect.Response[v1.Memo], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.CreateMemoComment is not implemented"))
}
func (UnimplementedMemoServiceHandler) ListMemoComments(context.Context, *connect.Request[v1.ListMemoCommentsRequest]) (*connect.Response[v1.ListMemoCommentsResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.ListMemoComments is not implemented"))
}
func (UnimplementedMemoServiceHandler) ListMemoReactions(context.Context, *connect.Request[v1.ListMemoReactionsRequest]) (*connect.Response[v1.ListMemoReactionsResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.ListMemoReactions is not implemented"))
}
func (UnimplementedMemoServiceHandler) UpsertMemoReaction(context.Context, *connect.Request[v1.UpsertMemoReactionRequest]) (*connect.Response[v1.Reaction], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.UpsertMemoReaction is not implemented"))
}
func (UnimplementedMemoServiceHandler) DeleteMemoReaction(context.Context, *connect.Request[v1.DeleteMemoReactionRequest]) (*connect.Response[emptypb.Empty], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.DeleteMemoReaction is not implemented"))
}

View File

@ -0,0 +1,236 @@
// Code generated by protoc-gen-connect-go. DO NOT EDIT.
//
// Source: api/v1/shortcut_service.proto
package apiv1connect
import (
connect "connectrpc.com/connect"
context "context"
errors "errors"
v1 "github.com/usememos/memos/proto/gen/api/v1"
emptypb "google.golang.org/protobuf/types/known/emptypb"
http "net/http"
strings "strings"
)
// This is a compile-time assertion to ensure that this generated file and the connect package are
// compatible. If you get a compiler error that this constant is not defined, this code was
// generated with a version of connect newer than the one compiled into your binary. You can fix the
// problem by either regenerating this code with an older version of connect or updating the connect
// version compiled into your binary.
const _ = connect.IsAtLeastVersion1_13_0
const (
// ShortcutServiceName is the fully-qualified name of the ShortcutService service.
ShortcutServiceName = "memos.api.v1.ShortcutService"
)
// These constants are the fully-qualified names of the RPCs defined in this package. They're
// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
//
// Note that these are different from the fully-qualified method names used by
// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
// period.
const (
// ShortcutServiceListShortcutsProcedure is the fully-qualified name of the ShortcutService's
// ListShortcuts RPC.
ShortcutServiceListShortcutsProcedure = "/memos.api.v1.ShortcutService/ListShortcuts"
// ShortcutServiceGetShortcutProcedure is the fully-qualified name of the ShortcutService's
// GetShortcut RPC.
ShortcutServiceGetShortcutProcedure = "/memos.api.v1.ShortcutService/GetShortcut"
// ShortcutServiceCreateShortcutProcedure is the fully-qualified name of the ShortcutService's
// CreateShortcut RPC.
ShortcutServiceCreateShortcutProcedure = "/memos.api.v1.ShortcutService/CreateShortcut"
// ShortcutServiceUpdateShortcutProcedure is the fully-qualified name of the ShortcutService's
// UpdateShortcut RPC.
ShortcutServiceUpdateShortcutProcedure = "/memos.api.v1.ShortcutService/UpdateShortcut"
// ShortcutServiceDeleteShortcutProcedure is the fully-qualified name of the ShortcutService's
// DeleteShortcut RPC.
ShortcutServiceDeleteShortcutProcedure = "/memos.api.v1.ShortcutService/DeleteShortcut"
)
// ShortcutServiceClient is a client for the memos.api.v1.ShortcutService service.
type ShortcutServiceClient interface {
// ListShortcuts returns a list of shortcuts for a user.
ListShortcuts(context.Context, *connect.Request[v1.ListShortcutsRequest]) (*connect.Response[v1.ListShortcutsResponse], error)
// GetShortcut gets a shortcut by name.
GetShortcut(context.Context, *connect.Request[v1.GetShortcutRequest]) (*connect.Response[v1.Shortcut], error)
// CreateShortcut creates a new shortcut for a user.
CreateShortcut(context.Context, *connect.Request[v1.CreateShortcutRequest]) (*connect.Response[v1.Shortcut], error)
// UpdateShortcut updates a shortcut for a user.
UpdateShortcut(context.Context, *connect.Request[v1.UpdateShortcutRequest]) (*connect.Response[v1.Shortcut], error)
// DeleteShortcut deletes a shortcut for a user.
DeleteShortcut(context.Context, *connect.Request[v1.DeleteShortcutRequest]) (*connect.Response[emptypb.Empty], error)
}
// NewShortcutServiceClient constructs a client for the memos.api.v1.ShortcutService service. By
// default, it uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses,
// and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the
// connect.WithGRPC() or connect.WithGRPCWeb() options.
//
// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
// http://api.acme.com or https://acme.com/grpc).
func NewShortcutServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) ShortcutServiceClient {
baseURL = strings.TrimRight(baseURL, "/")
shortcutServiceMethods := v1.File_api_v1_shortcut_service_proto.Services().ByName("ShortcutService").Methods()
return &shortcutServiceClient{
listShortcuts: connect.NewClient[v1.ListShortcutsRequest, v1.ListShortcutsResponse](
httpClient,
baseURL+ShortcutServiceListShortcutsProcedure,
connect.WithSchema(shortcutServiceMethods.ByName("ListShortcuts")),
connect.WithClientOptions(opts...),
),
getShortcut: connect.NewClient[v1.GetShortcutRequest, v1.Shortcut](
httpClient,
baseURL+ShortcutServiceGetShortcutProcedure,
connect.WithSchema(shortcutServiceMethods.ByName("GetShortcut")),
connect.WithClientOptions(opts...),
),
createShortcut: connect.NewClient[v1.CreateShortcutRequest, v1.Shortcut](
httpClient,
baseURL+ShortcutServiceCreateShortcutProcedure,
connect.WithSchema(shortcutServiceMethods.ByName("CreateShortcut")),
connect.WithClientOptions(opts...),
),
updateShortcut: connect.NewClient[v1.UpdateShortcutRequest, v1.Shortcut](
httpClient,
baseURL+ShortcutServiceUpdateShortcutProcedure,
connect.WithSchema(shortcutServiceMethods.ByName("UpdateShortcut")),
connect.WithClientOptions(opts...),
),
deleteShortcut: connect.NewClient[v1.DeleteShortcutRequest, emptypb.Empty](
httpClient,
baseURL+ShortcutServiceDeleteShortcutProcedure,
connect.WithSchema(shortcutServiceMethods.ByName("DeleteShortcut")),
connect.WithClientOptions(opts...),
),
}
}
// shortcutServiceClient implements ShortcutServiceClient.
type shortcutServiceClient struct {
listShortcuts *connect.Client[v1.ListShortcutsRequest, v1.ListShortcutsResponse]
getShortcut *connect.Client[v1.GetShortcutRequest, v1.Shortcut]
createShortcut *connect.Client[v1.CreateShortcutRequest, v1.Shortcut]
updateShortcut *connect.Client[v1.UpdateShortcutRequest, v1.Shortcut]
deleteShortcut *connect.Client[v1.DeleteShortcutRequest, emptypb.Empty]
}
// ListShortcuts calls memos.api.v1.ShortcutService.ListShortcuts.
func (c *shortcutServiceClient) ListShortcuts(ctx context.Context, req *connect.Request[v1.ListShortcutsRequest]) (*connect.Response[v1.ListShortcutsResponse], error) {
return c.listShortcuts.CallUnary(ctx, req)
}
// GetShortcut calls memos.api.v1.ShortcutService.GetShortcut.
func (c *shortcutServiceClient) GetShortcut(ctx context.Context, req *connect.Request[v1.GetShortcutRequest]) (*connect.Response[v1.Shortcut], error) {
return c.getShortcut.CallUnary(ctx, req)
}
// CreateShortcut calls memos.api.v1.ShortcutService.CreateShortcut.
func (c *shortcutServiceClient) CreateShortcut(ctx context.Context, req *connect.Request[v1.CreateShortcutRequest]) (*connect.Response[v1.Shortcut], error) {
return c.createShortcut.CallUnary(ctx, req)
}
// UpdateShortcut calls memos.api.v1.ShortcutService.UpdateShortcut.
func (c *shortcutServiceClient) UpdateShortcut(ctx context.Context, req *connect.Request[v1.UpdateShortcutRequest]) (*connect.Response[v1.Shortcut], error) {
return c.updateShortcut.CallUnary(ctx, req)
}
// DeleteShortcut calls memos.api.v1.ShortcutService.DeleteShortcut.
func (c *shortcutServiceClient) DeleteShortcut(ctx context.Context, req *connect.Request[v1.DeleteShortcutRequest]) (*connect.Response[emptypb.Empty], error) {
return c.deleteShortcut.CallUnary(ctx, req)
}
// ShortcutServiceHandler is an implementation of the memos.api.v1.ShortcutService service.
type ShortcutServiceHandler interface {
// ListShortcuts returns a list of shortcuts for a user.
ListShortcuts(context.Context, *connect.Request[v1.ListShortcutsRequest]) (*connect.Response[v1.ListShortcutsResponse], error)
// GetShortcut gets a shortcut by name.
GetShortcut(context.Context, *connect.Request[v1.GetShortcutRequest]) (*connect.Response[v1.Shortcut], error)
// CreateShortcut creates a new shortcut for a user.
CreateShortcut(context.Context, *connect.Request[v1.CreateShortcutRequest]) (*connect.Response[v1.Shortcut], error)
// UpdateShortcut updates a shortcut for a user.
UpdateShortcut(context.Context, *connect.Request[v1.UpdateShortcutRequest]) (*connect.Response[v1.Shortcut], error)
// DeleteShortcut deletes a shortcut for a user.
DeleteShortcut(context.Context, *connect.Request[v1.DeleteShortcutRequest]) (*connect.Response[emptypb.Empty], error)
}
// NewShortcutServiceHandler builds an HTTP handler from the service implementation. It returns the
// path on which to mount the handler and the handler itself.
//
// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
// and JSON codecs. They also support gzip compression.
func NewShortcutServiceHandler(svc ShortcutServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
shortcutServiceMethods := v1.File_api_v1_shortcut_service_proto.Services().ByName("ShortcutService").Methods()
shortcutServiceListShortcutsHandler := connect.NewUnaryHandler(
ShortcutServiceListShortcutsProcedure,
svc.ListShortcuts,
connect.WithSchema(shortcutServiceMethods.ByName("ListShortcuts")),
connect.WithHandlerOptions(opts...),
)
shortcutServiceGetShortcutHandler := connect.NewUnaryHandler(
ShortcutServiceGetShortcutProcedure,
svc.GetShortcut,
connect.WithSchema(shortcutServiceMethods.ByName("GetShortcut")),
connect.WithHandlerOptions(opts...),
)
shortcutServiceCreateShortcutHandler := connect.NewUnaryHandler(
ShortcutServiceCreateShortcutProcedure,
svc.CreateShortcut,
connect.WithSchema(shortcutServiceMethods.ByName("CreateShortcut")),
connect.WithHandlerOptions(opts...),
)
shortcutServiceUpdateShortcutHandler := connect.NewUnaryHandler(
ShortcutServiceUpdateShortcutProcedure,
svc.UpdateShortcut,
connect.WithSchema(shortcutServiceMethods.ByName("UpdateShortcut")),
connect.WithHandlerOptions(opts...),
)
shortcutServiceDeleteShortcutHandler := connect.NewUnaryHandler(
ShortcutServiceDeleteShortcutProcedure,
svc.DeleteShortcut,
connect.WithSchema(shortcutServiceMethods.ByName("DeleteShortcut")),
connect.WithHandlerOptions(opts...),
)
return "/memos.api.v1.ShortcutService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case ShortcutServiceListShortcutsProcedure:
shortcutServiceListShortcutsHandler.ServeHTTP(w, r)
case ShortcutServiceGetShortcutProcedure:
shortcutServiceGetShortcutHandler.ServeHTTP(w, r)
case ShortcutServiceCreateShortcutProcedure:
shortcutServiceCreateShortcutHandler.ServeHTTP(w, r)
case ShortcutServiceUpdateShortcutProcedure:
shortcutServiceUpdateShortcutHandler.ServeHTTP(w, r)
case ShortcutServiceDeleteShortcutProcedure:
shortcutServiceDeleteShortcutHandler.ServeHTTP(w, r)
default:
http.NotFound(w, r)
}
})
}
// UnimplementedShortcutServiceHandler returns CodeUnimplemented from all methods.
type UnimplementedShortcutServiceHandler struct{}
func (UnimplementedShortcutServiceHandler) ListShortcuts(context.Context, *connect.Request[v1.ListShortcutsRequest]) (*connect.Response[v1.ListShortcutsResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.ShortcutService.ListShortcuts is not implemented"))
}
func (UnimplementedShortcutServiceHandler) GetShortcut(context.Context, *connect.Request[v1.GetShortcutRequest]) (*connect.Response[v1.Shortcut], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.ShortcutService.GetShortcut is not implemented"))
}
func (UnimplementedShortcutServiceHandler) CreateShortcut(context.Context, *connect.Request[v1.CreateShortcutRequest]) (*connect.Response[v1.Shortcut], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.ShortcutService.CreateShortcut is not implemented"))
}
func (UnimplementedShortcutServiceHandler) UpdateShortcut(context.Context, *connect.Request[v1.UpdateShortcutRequest]) (*connect.Response[v1.Shortcut], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.ShortcutService.UpdateShortcut is not implemented"))
}
func (UnimplementedShortcutServiceHandler) DeleteShortcut(context.Context, *connect.Request[v1.DeleteShortcutRequest]) (*connect.Response[emptypb.Empty], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.ShortcutService.DeleteShortcut is not implemented"))
}

View File

@ -0,0 +1,764 @@
// Code generated by protoc-gen-connect-go. DO NOT EDIT.
//
// Source: api/v1/user_service.proto
package apiv1connect
import (
connect "connectrpc.com/connect"
context "context"
errors "errors"
v1 "github.com/usememos/memos/proto/gen/api/v1"
emptypb "google.golang.org/protobuf/types/known/emptypb"
http "net/http"
strings "strings"
)
// This is a compile-time assertion to ensure that this generated file and the connect package are
// compatible. If you get a compiler error that this constant is not defined, this code was
// generated with a version of connect newer than the one compiled into your binary. You can fix the
// problem by either regenerating this code with an older version of connect or updating the connect
// version compiled into your binary.
const _ = connect.IsAtLeastVersion1_13_0
const (
// UserServiceName is the fully-qualified name of the UserService service.
UserServiceName = "memos.api.v1.UserService"
)
// These constants are the fully-qualified names of the RPCs defined in this package. They're
// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
//
// Note that these are different from the fully-qualified method names used by
// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
// period.
const (
// UserServiceListUsersProcedure is the fully-qualified name of the UserService's ListUsers RPC.
UserServiceListUsersProcedure = "/memos.api.v1.UserService/ListUsers"
// UserServiceGetUserProcedure is the fully-qualified name of the UserService's GetUser RPC.
UserServiceGetUserProcedure = "/memos.api.v1.UserService/GetUser"
// UserServiceCreateUserProcedure is the fully-qualified name of the UserService's CreateUser RPC.
UserServiceCreateUserProcedure = "/memos.api.v1.UserService/CreateUser"
// UserServiceUpdateUserProcedure is the fully-qualified name of the UserService's UpdateUser RPC.
UserServiceUpdateUserProcedure = "/memos.api.v1.UserService/UpdateUser"
// UserServiceDeleteUserProcedure is the fully-qualified name of the UserService's DeleteUser RPC.
UserServiceDeleteUserProcedure = "/memos.api.v1.UserService/DeleteUser"
// UserServiceListAllUserStatsProcedure is the fully-qualified name of the UserService's
// ListAllUserStats RPC.
UserServiceListAllUserStatsProcedure = "/memos.api.v1.UserService/ListAllUserStats"
// UserServiceGetUserStatsProcedure is the fully-qualified name of the UserService's GetUserStats
// RPC.
UserServiceGetUserStatsProcedure = "/memos.api.v1.UserService/GetUserStats"
// UserServiceGetUserSettingProcedure is the fully-qualified name of the UserService's
// GetUserSetting RPC.
UserServiceGetUserSettingProcedure = "/memos.api.v1.UserService/GetUserSetting"
// UserServiceUpdateUserSettingProcedure is the fully-qualified name of the UserService's
// UpdateUserSetting RPC.
UserServiceUpdateUserSettingProcedure = "/memos.api.v1.UserService/UpdateUserSetting"
// UserServiceListUserSettingsProcedure is the fully-qualified name of the UserService's
// ListUserSettings RPC.
UserServiceListUserSettingsProcedure = "/memos.api.v1.UserService/ListUserSettings"
// UserServiceListUserAccessTokensProcedure is the fully-qualified name of the UserService's
// ListUserAccessTokens RPC.
UserServiceListUserAccessTokensProcedure = "/memos.api.v1.UserService/ListUserAccessTokens"
// UserServiceCreateUserAccessTokenProcedure is the fully-qualified name of the UserService's
// CreateUserAccessToken RPC.
UserServiceCreateUserAccessTokenProcedure = "/memos.api.v1.UserService/CreateUserAccessToken"
// UserServiceDeleteUserAccessTokenProcedure is the fully-qualified name of the UserService's
// DeleteUserAccessToken RPC.
UserServiceDeleteUserAccessTokenProcedure = "/memos.api.v1.UserService/DeleteUserAccessToken"
// UserServiceListUserSessionsProcedure is the fully-qualified name of the UserService's
// ListUserSessions RPC.
UserServiceListUserSessionsProcedure = "/memos.api.v1.UserService/ListUserSessions"
// UserServiceRevokeUserSessionProcedure is the fully-qualified name of the UserService's
// RevokeUserSession RPC.
UserServiceRevokeUserSessionProcedure = "/memos.api.v1.UserService/RevokeUserSession"
// UserServiceListUserWebhooksProcedure is the fully-qualified name of the UserService's
// ListUserWebhooks RPC.
UserServiceListUserWebhooksProcedure = "/memos.api.v1.UserService/ListUserWebhooks"
// UserServiceCreateUserWebhookProcedure is the fully-qualified name of the UserService's
// CreateUserWebhook RPC.
UserServiceCreateUserWebhookProcedure = "/memos.api.v1.UserService/CreateUserWebhook"
// UserServiceUpdateUserWebhookProcedure is the fully-qualified name of the UserService's
// UpdateUserWebhook RPC.
UserServiceUpdateUserWebhookProcedure = "/memos.api.v1.UserService/UpdateUserWebhook"
// UserServiceDeleteUserWebhookProcedure is the fully-qualified name of the UserService's
// DeleteUserWebhook RPC.
UserServiceDeleteUserWebhookProcedure = "/memos.api.v1.UserService/DeleteUserWebhook"
// UserServiceListUserNotificationsProcedure is the fully-qualified name of the UserService's
// ListUserNotifications RPC.
UserServiceListUserNotificationsProcedure = "/memos.api.v1.UserService/ListUserNotifications"
// UserServiceUpdateUserNotificationProcedure is the fully-qualified name of the UserService's
// UpdateUserNotification RPC.
UserServiceUpdateUserNotificationProcedure = "/memos.api.v1.UserService/UpdateUserNotification"
// UserServiceDeleteUserNotificationProcedure is the fully-qualified name of the UserService's
// DeleteUserNotification RPC.
UserServiceDeleteUserNotificationProcedure = "/memos.api.v1.UserService/DeleteUserNotification"
)
// UserServiceClient is a client for the memos.api.v1.UserService service.
type UserServiceClient interface {
// ListUsers returns a list of users.
ListUsers(context.Context, *connect.Request[v1.ListUsersRequest]) (*connect.Response[v1.ListUsersResponse], error)
// GetUser gets a user by ID or username.
// Supports both numeric IDs and username strings:
// - users/{id} (e.g., users/101)
// - users/{username} (e.g., users/steven)
GetUser(context.Context, *connect.Request[v1.GetUserRequest]) (*connect.Response[v1.User], error)
// CreateUser creates a new user.
CreateUser(context.Context, *connect.Request[v1.CreateUserRequest]) (*connect.Response[v1.User], error)
// UpdateUser updates a user.
UpdateUser(context.Context, *connect.Request[v1.UpdateUserRequest]) (*connect.Response[v1.User], error)
// DeleteUser deletes a user.
DeleteUser(context.Context, *connect.Request[v1.DeleteUserRequest]) (*connect.Response[emptypb.Empty], error)
// ListAllUserStats returns statistics for all users.
ListAllUserStats(context.Context, *connect.Request[v1.ListAllUserStatsRequest]) (*connect.Response[v1.ListAllUserStatsResponse], error)
// GetUserStats returns statistics for a specific user.
GetUserStats(context.Context, *connect.Request[v1.GetUserStatsRequest]) (*connect.Response[v1.UserStats], error)
// GetUserSetting returns the user setting.
GetUserSetting(context.Context, *connect.Request[v1.GetUserSettingRequest]) (*connect.Response[v1.UserSetting], error)
// UpdateUserSetting updates the user setting.
UpdateUserSetting(context.Context, *connect.Request[v1.UpdateUserSettingRequest]) (*connect.Response[v1.UserSetting], error)
// ListUserSettings returns a list of user settings.
ListUserSettings(context.Context, *connect.Request[v1.ListUserSettingsRequest]) (*connect.Response[v1.ListUserSettingsResponse], error)
// ListUserAccessTokens returns a list of access tokens for a user.
ListUserAccessTokens(context.Context, *connect.Request[v1.ListUserAccessTokensRequest]) (*connect.Response[v1.ListUserAccessTokensResponse], error)
// CreateUserAccessToken creates a new access token for a user.
CreateUserAccessToken(context.Context, *connect.Request[v1.CreateUserAccessTokenRequest]) (*connect.Response[v1.UserAccessToken], error)
// DeleteUserAccessToken deletes an access token.
DeleteUserAccessToken(context.Context, *connect.Request[v1.DeleteUserAccessTokenRequest]) (*connect.Response[emptypb.Empty], error)
// ListUserSessions returns a list of active sessions for a user.
ListUserSessions(context.Context, *connect.Request[v1.ListUserSessionsRequest]) (*connect.Response[v1.ListUserSessionsResponse], error)
// RevokeUserSession revokes a specific session for a user.
RevokeUserSession(context.Context, *connect.Request[v1.RevokeUserSessionRequest]) (*connect.Response[emptypb.Empty], error)
// ListUserWebhooks returns a list of webhooks for a user.
ListUserWebhooks(context.Context, *connect.Request[v1.ListUserWebhooksRequest]) (*connect.Response[v1.ListUserWebhooksResponse], error)
// CreateUserWebhook creates a new webhook for a user.
CreateUserWebhook(context.Context, *connect.Request[v1.CreateUserWebhookRequest]) (*connect.Response[v1.UserWebhook], error)
// UpdateUserWebhook updates an existing webhook for a user.
UpdateUserWebhook(context.Context, *connect.Request[v1.UpdateUserWebhookRequest]) (*connect.Response[v1.UserWebhook], error)
// DeleteUserWebhook deletes a webhook for a user.
DeleteUserWebhook(context.Context, *connect.Request[v1.DeleteUserWebhookRequest]) (*connect.Response[emptypb.Empty], error)
// ListUserNotifications lists notifications for a user.
ListUserNotifications(context.Context, *connect.Request[v1.ListUserNotificationsRequest]) (*connect.Response[v1.ListUserNotificationsResponse], error)
// UpdateUserNotification updates a notification.
UpdateUserNotification(context.Context, *connect.Request[v1.UpdateUserNotificationRequest]) (*connect.Response[v1.UserNotification], error)
// DeleteUserNotification deletes a notification.
DeleteUserNotification(context.Context, *connect.Request[v1.DeleteUserNotificationRequest]) (*connect.Response[emptypb.Empty], error)
}
// NewUserServiceClient constructs a client for the memos.api.v1.UserService service. By default, it
// uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends
// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or
// connect.WithGRPCWeb() options.
//
// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
// http://api.acme.com or https://acme.com/grpc).
func NewUserServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) UserServiceClient {
baseURL = strings.TrimRight(baseURL, "/")
userServiceMethods := v1.File_api_v1_user_service_proto.Services().ByName("UserService").Methods()
return &userServiceClient{
listUsers: connect.NewClient[v1.ListUsersRequest, v1.ListUsersResponse](
httpClient,
baseURL+UserServiceListUsersProcedure,
connect.WithSchema(userServiceMethods.ByName("ListUsers")),
connect.WithClientOptions(opts...),
),
getUser: connect.NewClient[v1.GetUserRequest, v1.User](
httpClient,
baseURL+UserServiceGetUserProcedure,
connect.WithSchema(userServiceMethods.ByName("GetUser")),
connect.WithClientOptions(opts...),
),
createUser: connect.NewClient[v1.CreateUserRequest, v1.User](
httpClient,
baseURL+UserServiceCreateUserProcedure,
connect.WithSchema(userServiceMethods.ByName("CreateUser")),
connect.WithClientOptions(opts...),
),
updateUser: connect.NewClient[v1.UpdateUserRequest, v1.User](
httpClient,
baseURL+UserServiceUpdateUserProcedure,
connect.WithSchema(userServiceMethods.ByName("UpdateUser")),
connect.WithClientOptions(opts...),
),
deleteUser: connect.NewClient[v1.DeleteUserRequest, emptypb.Empty](
httpClient,
baseURL+UserServiceDeleteUserProcedure,
connect.WithSchema(userServiceMethods.ByName("DeleteUser")),
connect.WithClientOptions(opts...),
),
listAllUserStats: connect.NewClient[v1.ListAllUserStatsRequest, v1.ListAllUserStatsResponse](
httpClient,
baseURL+UserServiceListAllUserStatsProcedure,
connect.WithSchema(userServiceMethods.ByName("ListAllUserStats")),
connect.WithClientOptions(opts...),
),
getUserStats: connect.NewClient[v1.GetUserStatsRequest, v1.UserStats](
httpClient,
baseURL+UserServiceGetUserStatsProcedure,
connect.WithSchema(userServiceMethods.ByName("GetUserStats")),
connect.WithClientOptions(opts...),
),
getUserSetting: connect.NewClient[v1.GetUserSettingRequest, v1.UserSetting](
httpClient,
baseURL+UserServiceGetUserSettingProcedure,
connect.WithSchema(userServiceMethods.ByName("GetUserSetting")),
connect.WithClientOptions(opts...),
),
updateUserSetting: connect.NewClient[v1.UpdateUserSettingRequest, v1.UserSetting](
httpClient,
baseURL+UserServiceUpdateUserSettingProcedure,
connect.WithSchema(userServiceMethods.ByName("UpdateUserSetting")),
connect.WithClientOptions(opts...),
),
listUserSettings: connect.NewClient[v1.ListUserSettingsRequest, v1.ListUserSettingsResponse](
httpClient,
baseURL+UserServiceListUserSettingsProcedure,
connect.WithSchema(userServiceMethods.ByName("ListUserSettings")),
connect.WithClientOptions(opts...),
),
listUserAccessTokens: connect.NewClient[v1.ListUserAccessTokensRequest, v1.ListUserAccessTokensResponse](
httpClient,
baseURL+UserServiceListUserAccessTokensProcedure,
connect.WithSchema(userServiceMethods.ByName("ListUserAccessTokens")),
connect.WithClientOptions(opts...),
),
createUserAccessToken: connect.NewClient[v1.CreateUserAccessTokenRequest, v1.UserAccessToken](
httpClient,
baseURL+UserServiceCreateUserAccessTokenProcedure,
connect.WithSchema(userServiceMethods.ByName("CreateUserAccessToken")),
connect.WithClientOptions(opts...),
),
deleteUserAccessToken: connect.NewClient[v1.DeleteUserAccessTokenRequest, emptypb.Empty](
httpClient,
baseURL+UserServiceDeleteUserAccessTokenProcedure,
connect.WithSchema(userServiceMethods.ByName("DeleteUserAccessToken")),
connect.WithClientOptions(opts...),
),
listUserSessions: connect.NewClient[v1.ListUserSessionsRequest, v1.ListUserSessionsResponse](
httpClient,
baseURL+UserServiceListUserSessionsProcedure,
connect.WithSchema(userServiceMethods.ByName("ListUserSessions")),
connect.WithClientOptions(opts...),
),
revokeUserSession: connect.NewClient[v1.RevokeUserSessionRequest, emptypb.Empty](
httpClient,
baseURL+UserServiceRevokeUserSessionProcedure,
connect.WithSchema(userServiceMethods.ByName("RevokeUserSession")),
connect.WithClientOptions(opts...),
),
listUserWebhooks: connect.NewClient[v1.ListUserWebhooksRequest, v1.ListUserWebhooksResponse](
httpClient,
baseURL+UserServiceListUserWebhooksProcedure,
connect.WithSchema(userServiceMethods.ByName("ListUserWebhooks")),
connect.WithClientOptions(opts...),
),
createUserWebhook: connect.NewClient[v1.CreateUserWebhookRequest, v1.UserWebhook](
httpClient,
baseURL+UserServiceCreateUserWebhookProcedure,
connect.WithSchema(userServiceMethods.ByName("CreateUserWebhook")),
connect.WithClientOptions(opts...),
),
updateUserWebhook: connect.NewClient[v1.UpdateUserWebhookRequest, v1.UserWebhook](
httpClient,
baseURL+UserServiceUpdateUserWebhookProcedure,
connect.WithSchema(userServiceMethods.ByName("UpdateUserWebhook")),
connect.WithClientOptions(opts...),
),
deleteUserWebhook: connect.NewClient[v1.DeleteUserWebhookRequest, emptypb.Empty](
httpClient,
baseURL+UserServiceDeleteUserWebhookProcedure,
connect.WithSchema(userServiceMethods.ByName("DeleteUserWebhook")),
connect.WithClientOptions(opts...),
),
listUserNotifications: connect.NewClient[v1.ListUserNotificationsRequest, v1.ListUserNotificationsResponse](
httpClient,
baseURL+UserServiceListUserNotificationsProcedure,
connect.WithSchema(userServiceMethods.ByName("ListUserNotifications")),
connect.WithClientOptions(opts...),
),
updateUserNotification: connect.NewClient[v1.UpdateUserNotificationRequest, v1.UserNotification](
httpClient,
baseURL+UserServiceUpdateUserNotificationProcedure,
connect.WithSchema(userServiceMethods.ByName("UpdateUserNotification")),
connect.WithClientOptions(opts...),
),
deleteUserNotification: connect.NewClient[v1.DeleteUserNotificationRequest, emptypb.Empty](
httpClient,
baseURL+UserServiceDeleteUserNotificationProcedure,
connect.WithSchema(userServiceMethods.ByName("DeleteUserNotification")),
connect.WithClientOptions(opts...),
),
}
}
// userServiceClient implements UserServiceClient.
type userServiceClient struct {
listUsers *connect.Client[v1.ListUsersRequest, v1.ListUsersResponse]
getUser *connect.Client[v1.GetUserRequest, v1.User]
createUser *connect.Client[v1.CreateUserRequest, v1.User]
updateUser *connect.Client[v1.UpdateUserRequest, v1.User]
deleteUser *connect.Client[v1.DeleteUserRequest, emptypb.Empty]
listAllUserStats *connect.Client[v1.ListAllUserStatsRequest, v1.ListAllUserStatsResponse]
getUserStats *connect.Client[v1.GetUserStatsRequest, v1.UserStats]
getUserSetting *connect.Client[v1.GetUserSettingRequest, v1.UserSetting]
updateUserSetting *connect.Client[v1.UpdateUserSettingRequest, v1.UserSetting]
listUserSettings *connect.Client[v1.ListUserSettingsRequest, v1.ListUserSettingsResponse]
listUserAccessTokens *connect.Client[v1.ListUserAccessTokensRequest, v1.ListUserAccessTokensResponse]
createUserAccessToken *connect.Client[v1.CreateUserAccessTokenRequest, v1.UserAccessToken]
deleteUserAccessToken *connect.Client[v1.DeleteUserAccessTokenRequest, emptypb.Empty]
listUserSessions *connect.Client[v1.ListUserSessionsRequest, v1.ListUserSessionsResponse]
revokeUserSession *connect.Client[v1.RevokeUserSessionRequest, emptypb.Empty]
listUserWebhooks *connect.Client[v1.ListUserWebhooksRequest, v1.ListUserWebhooksResponse]
createUserWebhook *connect.Client[v1.CreateUserWebhookRequest, v1.UserWebhook]
updateUserWebhook *connect.Client[v1.UpdateUserWebhookRequest, v1.UserWebhook]
deleteUserWebhook *connect.Client[v1.DeleteUserWebhookRequest, emptypb.Empty]
listUserNotifications *connect.Client[v1.ListUserNotificationsRequest, v1.ListUserNotificationsResponse]
updateUserNotification *connect.Client[v1.UpdateUserNotificationRequest, v1.UserNotification]
deleteUserNotification *connect.Client[v1.DeleteUserNotificationRequest, emptypb.Empty]
}
// ListUsers calls memos.api.v1.UserService.ListUsers.
func (c *userServiceClient) ListUsers(ctx context.Context, req *connect.Request[v1.ListUsersRequest]) (*connect.Response[v1.ListUsersResponse], error) {
return c.listUsers.CallUnary(ctx, req)
}
// GetUser calls memos.api.v1.UserService.GetUser.
func (c *userServiceClient) GetUser(ctx context.Context, req *connect.Request[v1.GetUserRequest]) (*connect.Response[v1.User], error) {
return c.getUser.CallUnary(ctx, req)
}
// CreateUser calls memos.api.v1.UserService.CreateUser.
func (c *userServiceClient) CreateUser(ctx context.Context, req *connect.Request[v1.CreateUserRequest]) (*connect.Response[v1.User], error) {
return c.createUser.CallUnary(ctx, req)
}
// UpdateUser calls memos.api.v1.UserService.UpdateUser.
func (c *userServiceClient) UpdateUser(ctx context.Context, req *connect.Request[v1.UpdateUserRequest]) (*connect.Response[v1.User], error) {
return c.updateUser.CallUnary(ctx, req)
}
// DeleteUser calls memos.api.v1.UserService.DeleteUser.
func (c *userServiceClient) DeleteUser(ctx context.Context, req *connect.Request[v1.DeleteUserRequest]) (*connect.Response[emptypb.Empty], error) {
return c.deleteUser.CallUnary(ctx, req)
}
// ListAllUserStats calls memos.api.v1.UserService.ListAllUserStats.
func (c *userServiceClient) ListAllUserStats(ctx context.Context, req *connect.Request[v1.ListAllUserStatsRequest]) (*connect.Response[v1.ListAllUserStatsResponse], error) {
return c.listAllUserStats.CallUnary(ctx, req)
}
// GetUserStats calls memos.api.v1.UserService.GetUserStats.
func (c *userServiceClient) GetUserStats(ctx context.Context, req *connect.Request[v1.GetUserStatsRequest]) (*connect.Response[v1.UserStats], error) {
return c.getUserStats.CallUnary(ctx, req)
}
// GetUserSetting calls memos.api.v1.UserService.GetUserSetting.
func (c *userServiceClient) GetUserSetting(ctx context.Context, req *connect.Request[v1.GetUserSettingRequest]) (*connect.Response[v1.UserSetting], error) {
return c.getUserSetting.CallUnary(ctx, req)
}
// UpdateUserSetting calls memos.api.v1.UserService.UpdateUserSetting.
func (c *userServiceClient) UpdateUserSetting(ctx context.Context, req *connect.Request[v1.UpdateUserSettingRequest]) (*connect.Response[v1.UserSetting], error) {
return c.updateUserSetting.CallUnary(ctx, req)
}
// ListUserSettings calls memos.api.v1.UserService.ListUserSettings.
func (c *userServiceClient) ListUserSettings(ctx context.Context, req *connect.Request[v1.ListUserSettingsRequest]) (*connect.Response[v1.ListUserSettingsResponse], error) {
return c.listUserSettings.CallUnary(ctx, req)
}
// ListUserAccessTokens calls memos.api.v1.UserService.ListUserAccessTokens.
func (c *userServiceClient) ListUserAccessTokens(ctx context.Context, req *connect.Request[v1.ListUserAccessTokensRequest]) (*connect.Response[v1.ListUserAccessTokensResponse], error) {
return c.listUserAccessTokens.CallUnary(ctx, req)
}
// CreateUserAccessToken calls memos.api.v1.UserService.CreateUserAccessToken.
func (c *userServiceClient) CreateUserAccessToken(ctx context.Context, req *connect.Request[v1.CreateUserAccessTokenRequest]) (*connect.Response[v1.UserAccessToken], error) {
return c.createUserAccessToken.CallUnary(ctx, req)
}
// DeleteUserAccessToken calls memos.api.v1.UserService.DeleteUserAccessToken.
func (c *userServiceClient) DeleteUserAccessToken(ctx context.Context, req *connect.Request[v1.DeleteUserAccessTokenRequest]) (*connect.Response[emptypb.Empty], error) {
return c.deleteUserAccessToken.CallUnary(ctx, req)
}
// ListUserSessions calls memos.api.v1.UserService.ListUserSessions.
func (c *userServiceClient) ListUserSessions(ctx context.Context, req *connect.Request[v1.ListUserSessionsRequest]) (*connect.Response[v1.ListUserSessionsResponse], error) {
return c.listUserSessions.CallUnary(ctx, req)
}
// RevokeUserSession calls memos.api.v1.UserService.RevokeUserSession.
func (c *userServiceClient) RevokeUserSession(ctx context.Context, req *connect.Request[v1.RevokeUserSessionRequest]) (*connect.Response[emptypb.Empty], error) {
return c.revokeUserSession.CallUnary(ctx, req)
}
// ListUserWebhooks calls memos.api.v1.UserService.ListUserWebhooks.
func (c *userServiceClient) ListUserWebhooks(ctx context.Context, req *connect.Request[v1.ListUserWebhooksRequest]) (*connect.Response[v1.ListUserWebhooksResponse], error) {
return c.listUserWebhooks.CallUnary(ctx, req)
}
// CreateUserWebhook calls memos.api.v1.UserService.CreateUserWebhook.
func (c *userServiceClient) CreateUserWebhook(ctx context.Context, req *connect.Request[v1.CreateUserWebhookRequest]) (*connect.Response[v1.UserWebhook], error) {
return c.createUserWebhook.CallUnary(ctx, req)
}
// UpdateUserWebhook calls memos.api.v1.UserService.UpdateUserWebhook.
func (c *userServiceClient) UpdateUserWebhook(ctx context.Context, req *connect.Request[v1.UpdateUserWebhookRequest]) (*connect.Response[v1.UserWebhook], error) {
return c.updateUserWebhook.CallUnary(ctx, req)
}
// DeleteUserWebhook calls memos.api.v1.UserService.DeleteUserWebhook.
func (c *userServiceClient) DeleteUserWebhook(ctx context.Context, req *connect.Request[v1.DeleteUserWebhookRequest]) (*connect.Response[emptypb.Empty], error) {
return c.deleteUserWebhook.CallUnary(ctx, req)
}
// ListUserNotifications calls memos.api.v1.UserService.ListUserNotifications.
func (c *userServiceClient) ListUserNotifications(ctx context.Context, req *connect.Request[v1.ListUserNotificationsRequest]) (*connect.Response[v1.ListUserNotificationsResponse], error) {
return c.listUserNotifications.CallUnary(ctx, req)
}
// UpdateUserNotification calls memos.api.v1.UserService.UpdateUserNotification.
func (c *userServiceClient) UpdateUserNotification(ctx context.Context, req *connect.Request[v1.UpdateUserNotificationRequest]) (*connect.Response[v1.UserNotification], error) {
return c.updateUserNotification.CallUnary(ctx, req)
}
// DeleteUserNotification calls memos.api.v1.UserService.DeleteUserNotification.
func (c *userServiceClient) DeleteUserNotification(ctx context.Context, req *connect.Request[v1.DeleteUserNotificationRequest]) (*connect.Response[emptypb.Empty], error) {
return c.deleteUserNotification.CallUnary(ctx, req)
}
// UserServiceHandler is an implementation of the memos.api.v1.UserService service.
type UserServiceHandler interface {
// ListUsers returns a list of users.
ListUsers(context.Context, *connect.Request[v1.ListUsersRequest]) (*connect.Response[v1.ListUsersResponse], error)
// GetUser gets a user by ID or username.
// Supports both numeric IDs and username strings:
// - users/{id} (e.g., users/101)
// - users/{username} (e.g., users/steven)
GetUser(context.Context, *connect.Request[v1.GetUserRequest]) (*connect.Response[v1.User], error)
// CreateUser creates a new user.
CreateUser(context.Context, *connect.Request[v1.CreateUserRequest]) (*connect.Response[v1.User], error)
// UpdateUser updates a user.
UpdateUser(context.Context, *connect.Request[v1.UpdateUserRequest]) (*connect.Response[v1.User], error)
// DeleteUser deletes a user.
DeleteUser(context.Context, *connect.Request[v1.DeleteUserRequest]) (*connect.Response[emptypb.Empty], error)
// ListAllUserStats returns statistics for all users.
ListAllUserStats(context.Context, *connect.Request[v1.ListAllUserStatsRequest]) (*connect.Response[v1.ListAllUserStatsResponse], error)
// GetUserStats returns statistics for a specific user.
GetUserStats(context.Context, *connect.Request[v1.GetUserStatsRequest]) (*connect.Response[v1.UserStats], error)
// GetUserSetting returns the user setting.
GetUserSetting(context.Context, *connect.Request[v1.GetUserSettingRequest]) (*connect.Response[v1.UserSetting], error)
// UpdateUserSetting updates the user setting.
UpdateUserSetting(context.Context, *connect.Request[v1.UpdateUserSettingRequest]) (*connect.Response[v1.UserSetting], error)
// ListUserSettings returns a list of user settings.
ListUserSettings(context.Context, *connect.Request[v1.ListUserSettingsRequest]) (*connect.Response[v1.ListUserSettingsResponse], error)
// ListUserAccessTokens returns a list of access tokens for a user.
ListUserAccessTokens(context.Context, *connect.Request[v1.ListUserAccessTokensRequest]) (*connect.Response[v1.ListUserAccessTokensResponse], error)
// CreateUserAccessToken creates a new access token for a user.
CreateUserAccessToken(context.Context, *connect.Request[v1.CreateUserAccessTokenRequest]) (*connect.Response[v1.UserAccessToken], error)
// DeleteUserAccessToken deletes an access token.
DeleteUserAccessToken(context.Context, *connect.Request[v1.DeleteUserAccessTokenRequest]) (*connect.Response[emptypb.Empty], error)
// ListUserSessions returns a list of active sessions for a user.
ListUserSessions(context.Context, *connect.Request[v1.ListUserSessionsRequest]) (*connect.Response[v1.ListUserSessionsResponse], error)
// RevokeUserSession revokes a specific session for a user.
RevokeUserSession(context.Context, *connect.Request[v1.RevokeUserSessionRequest]) (*connect.Response[emptypb.Empty], error)
// ListUserWebhooks returns a list of webhooks for a user.
ListUserWebhooks(context.Context, *connect.Request[v1.ListUserWebhooksRequest]) (*connect.Response[v1.ListUserWebhooksResponse], error)
// CreateUserWebhook creates a new webhook for a user.
CreateUserWebhook(context.Context, *connect.Request[v1.CreateUserWebhookRequest]) (*connect.Response[v1.UserWebhook], error)
// UpdateUserWebhook updates an existing webhook for a user.
UpdateUserWebhook(context.Context, *connect.Request[v1.UpdateUserWebhookRequest]) (*connect.Response[v1.UserWebhook], error)
// DeleteUserWebhook deletes a webhook for a user.
DeleteUserWebhook(context.Context, *connect.Request[v1.DeleteUserWebhookRequest]) (*connect.Response[emptypb.Empty], error)
// ListUserNotifications lists notifications for a user.
ListUserNotifications(context.Context, *connect.Request[v1.ListUserNotificationsRequest]) (*connect.Response[v1.ListUserNotificationsResponse], error)
// UpdateUserNotification updates a notification.
UpdateUserNotification(context.Context, *connect.Request[v1.UpdateUserNotificationRequest]) (*connect.Response[v1.UserNotification], error)
// DeleteUserNotification deletes a notification.
DeleteUserNotification(context.Context, *connect.Request[v1.DeleteUserNotificationRequest]) (*connect.Response[emptypb.Empty], error)
}
// NewUserServiceHandler builds an HTTP handler from the service implementation. It returns the path
// on which to mount the handler and the handler itself.
//
// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
// and JSON codecs. They also support gzip compression.
func NewUserServiceHandler(svc UserServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
userServiceMethods := v1.File_api_v1_user_service_proto.Services().ByName("UserService").Methods()
userServiceListUsersHandler := connect.NewUnaryHandler(
UserServiceListUsersProcedure,
svc.ListUsers,
connect.WithSchema(userServiceMethods.ByName("ListUsers")),
connect.WithHandlerOptions(opts...),
)
userServiceGetUserHandler := connect.NewUnaryHandler(
UserServiceGetUserProcedure,
svc.GetUser,
connect.WithSchema(userServiceMethods.ByName("GetUser")),
connect.WithHandlerOptions(opts...),
)
userServiceCreateUserHandler := connect.NewUnaryHandler(
UserServiceCreateUserProcedure,
svc.CreateUser,
connect.WithSchema(userServiceMethods.ByName("CreateUser")),
connect.WithHandlerOptions(opts...),
)
userServiceUpdateUserHandler := connect.NewUnaryHandler(
UserServiceUpdateUserProcedure,
svc.UpdateUser,
connect.WithSchema(userServiceMethods.ByName("UpdateUser")),
connect.WithHandlerOptions(opts...),
)
userServiceDeleteUserHandler := connect.NewUnaryHandler(
UserServiceDeleteUserProcedure,
svc.DeleteUser,
connect.WithSchema(userServiceMethods.ByName("DeleteUser")),
connect.WithHandlerOptions(opts...),
)
userServiceListAllUserStatsHandler := connect.NewUnaryHandler(
UserServiceListAllUserStatsProcedure,
svc.ListAllUserStats,
connect.WithSchema(userServiceMethods.ByName("ListAllUserStats")),
connect.WithHandlerOptions(opts...),
)
userServiceGetUserStatsHandler := connect.NewUnaryHandler(
UserServiceGetUserStatsProcedure,
svc.GetUserStats,
connect.WithSchema(userServiceMethods.ByName("GetUserStats")),
connect.WithHandlerOptions(opts...),
)
userServiceGetUserSettingHandler := connect.NewUnaryHandler(
UserServiceGetUserSettingProcedure,
svc.GetUserSetting,
connect.WithSchema(userServiceMethods.ByName("GetUserSetting")),
connect.WithHandlerOptions(opts...),
)
userServiceUpdateUserSettingHandler := connect.NewUnaryHandler(
UserServiceUpdateUserSettingProcedure,
svc.UpdateUserSetting,
connect.WithSchema(userServiceMethods.ByName("UpdateUserSetting")),
connect.WithHandlerOptions(opts...),
)
userServiceListUserSettingsHandler := connect.NewUnaryHandler(
UserServiceListUserSettingsProcedure,
svc.ListUserSettings,
connect.WithSchema(userServiceMethods.ByName("ListUserSettings")),
connect.WithHandlerOptions(opts...),
)
userServiceListUserAccessTokensHandler := connect.NewUnaryHandler(
UserServiceListUserAccessTokensProcedure,
svc.ListUserAccessTokens,
connect.WithSchema(userServiceMethods.ByName("ListUserAccessTokens")),
connect.WithHandlerOptions(opts...),
)
userServiceCreateUserAccessTokenHandler := connect.NewUnaryHandler(
UserServiceCreateUserAccessTokenProcedure,
svc.CreateUserAccessToken,
connect.WithSchema(userServiceMethods.ByName("CreateUserAccessToken")),
connect.WithHandlerOptions(opts...),
)
userServiceDeleteUserAccessTokenHandler := connect.NewUnaryHandler(
UserServiceDeleteUserAccessTokenProcedure,
svc.DeleteUserAccessToken,
connect.WithSchema(userServiceMethods.ByName("DeleteUserAccessToken")),
connect.WithHandlerOptions(opts...),
)
userServiceListUserSessionsHandler := connect.NewUnaryHandler(
UserServiceListUserSessionsProcedure,
svc.ListUserSessions,
connect.WithSchema(userServiceMethods.ByName("ListUserSessions")),
connect.WithHandlerOptions(opts...),
)
userServiceRevokeUserSessionHandler := connect.NewUnaryHandler(
UserServiceRevokeUserSessionProcedure,
svc.RevokeUserSession,
connect.WithSchema(userServiceMethods.ByName("RevokeUserSession")),
connect.WithHandlerOptions(opts...),
)
userServiceListUserWebhooksHandler := connect.NewUnaryHandler(
UserServiceListUserWebhooksProcedure,
svc.ListUserWebhooks,
connect.WithSchema(userServiceMethods.ByName("ListUserWebhooks")),
connect.WithHandlerOptions(opts...),
)
userServiceCreateUserWebhookHandler := connect.NewUnaryHandler(
UserServiceCreateUserWebhookProcedure,
svc.CreateUserWebhook,
connect.WithSchema(userServiceMethods.ByName("CreateUserWebhook")),
connect.WithHandlerOptions(opts...),
)
userServiceUpdateUserWebhookHandler := connect.NewUnaryHandler(
UserServiceUpdateUserWebhookProcedure,
svc.UpdateUserWebhook,
connect.WithSchema(userServiceMethods.ByName("UpdateUserWebhook")),
connect.WithHandlerOptions(opts...),
)
userServiceDeleteUserWebhookHandler := connect.NewUnaryHandler(
UserServiceDeleteUserWebhookProcedure,
svc.DeleteUserWebhook,
connect.WithSchema(userServiceMethods.ByName("DeleteUserWebhook")),
connect.WithHandlerOptions(opts...),
)
userServiceListUserNotificationsHandler := connect.NewUnaryHandler(
UserServiceListUserNotificationsProcedure,
svc.ListUserNotifications,
connect.WithSchema(userServiceMethods.ByName("ListUserNotifications")),
connect.WithHandlerOptions(opts...),
)
userServiceUpdateUserNotificationHandler := connect.NewUnaryHandler(
UserServiceUpdateUserNotificationProcedure,
svc.UpdateUserNotification,
connect.WithSchema(userServiceMethods.ByName("UpdateUserNotification")),
connect.WithHandlerOptions(opts...),
)
userServiceDeleteUserNotificationHandler := connect.NewUnaryHandler(
UserServiceDeleteUserNotificationProcedure,
svc.DeleteUserNotification,
connect.WithSchema(userServiceMethods.ByName("DeleteUserNotification")),
connect.WithHandlerOptions(opts...),
)
return "/memos.api.v1.UserService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case UserServiceListUsersProcedure:
userServiceListUsersHandler.ServeHTTP(w, r)
case UserServiceGetUserProcedure:
userServiceGetUserHandler.ServeHTTP(w, r)
case UserServiceCreateUserProcedure:
userServiceCreateUserHandler.ServeHTTP(w, r)
case UserServiceUpdateUserProcedure:
userServiceUpdateUserHandler.ServeHTTP(w, r)
case UserServiceDeleteUserProcedure:
userServiceDeleteUserHandler.ServeHTTP(w, r)
case UserServiceListAllUserStatsProcedure:
userServiceListAllUserStatsHandler.ServeHTTP(w, r)
case UserServiceGetUserStatsProcedure:
userServiceGetUserStatsHandler.ServeHTTP(w, r)
case UserServiceGetUserSettingProcedure:
userServiceGetUserSettingHandler.ServeHTTP(w, r)
case UserServiceUpdateUserSettingProcedure:
userServiceUpdateUserSettingHandler.ServeHTTP(w, r)
case UserServiceListUserSettingsProcedure:
userServiceListUserSettingsHandler.ServeHTTP(w, r)
case UserServiceListUserAccessTokensProcedure:
userServiceListUserAccessTokensHandler.ServeHTTP(w, r)
case UserServiceCreateUserAccessTokenProcedure:
userServiceCreateUserAccessTokenHandler.ServeHTTP(w, r)
case UserServiceDeleteUserAccessTokenProcedure:
userServiceDeleteUserAccessTokenHandler.ServeHTTP(w, r)
case UserServiceListUserSessionsProcedure:
userServiceListUserSessionsHandler.ServeHTTP(w, r)
case UserServiceRevokeUserSessionProcedure:
userServiceRevokeUserSessionHandler.ServeHTTP(w, r)
case UserServiceListUserWebhooksProcedure:
userServiceListUserWebhooksHandler.ServeHTTP(w, r)
case UserServiceCreateUserWebhookProcedure:
userServiceCreateUserWebhookHandler.ServeHTTP(w, r)
case UserServiceUpdateUserWebhookProcedure:
userServiceUpdateUserWebhookHandler.ServeHTTP(w, r)
case UserServiceDeleteUserWebhookProcedure:
userServiceDeleteUserWebhookHandler.ServeHTTP(w, r)
case UserServiceListUserNotificationsProcedure:
userServiceListUserNotificationsHandler.ServeHTTP(w, r)
case UserServiceUpdateUserNotificationProcedure:
userServiceUpdateUserNotificationHandler.ServeHTTP(w, r)
case UserServiceDeleteUserNotificationProcedure:
userServiceDeleteUserNotificationHandler.ServeHTTP(w, r)
default:
http.NotFound(w, r)
}
})
}
// UnimplementedUserServiceHandler returns CodeUnimplemented from all methods.
type UnimplementedUserServiceHandler struct{}
func (UnimplementedUserServiceHandler) ListUsers(context.Context, *connect.Request[v1.ListUsersRequest]) (*connect.Response[v1.ListUsersResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.ListUsers is not implemented"))
}
func (UnimplementedUserServiceHandler) GetUser(context.Context, *connect.Request[v1.GetUserRequest]) (*connect.Response[v1.User], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.GetUser is not implemented"))
}
func (UnimplementedUserServiceHandler) CreateUser(context.Context, *connect.Request[v1.CreateUserRequest]) (*connect.Response[v1.User], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.CreateUser is not implemented"))
}
func (UnimplementedUserServiceHandler) UpdateUser(context.Context, *connect.Request[v1.UpdateUserRequest]) (*connect.Response[v1.User], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.UpdateUser is not implemented"))
}
func (UnimplementedUserServiceHandler) DeleteUser(context.Context, *connect.Request[v1.DeleteUserRequest]) (*connect.Response[emptypb.Empty], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.DeleteUser is not implemented"))
}
func (UnimplementedUserServiceHandler) ListAllUserStats(context.Context, *connect.Request[v1.ListAllUserStatsRequest]) (*connect.Response[v1.ListAllUserStatsResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.ListAllUserStats is not implemented"))
}
func (UnimplementedUserServiceHandler) GetUserStats(context.Context, *connect.Request[v1.GetUserStatsRequest]) (*connect.Response[v1.UserStats], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.GetUserStats is not implemented"))
}
func (UnimplementedUserServiceHandler) GetUserSetting(context.Context, *connect.Request[v1.GetUserSettingRequest]) (*connect.Response[v1.UserSetting], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.GetUserSetting is not implemented"))
}
func (UnimplementedUserServiceHandler) UpdateUserSetting(context.Context, *connect.Request[v1.UpdateUserSettingRequest]) (*connect.Response[v1.UserSetting], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.UpdateUserSetting is not implemented"))
}
func (UnimplementedUserServiceHandler) ListUserSettings(context.Context, *connect.Request[v1.ListUserSettingsRequest]) (*connect.Response[v1.ListUserSettingsResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.ListUserSettings is not implemented"))
}
func (UnimplementedUserServiceHandler) ListUserAccessTokens(context.Context, *connect.Request[v1.ListUserAccessTokensRequest]) (*connect.Response[v1.ListUserAccessTokensResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.ListUserAccessTokens is not implemented"))
}
func (UnimplementedUserServiceHandler) CreateUserAccessToken(context.Context, *connect.Request[v1.CreateUserAccessTokenRequest]) (*connect.Response[v1.UserAccessToken], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.CreateUserAccessToken is not implemented"))
}
func (UnimplementedUserServiceHandler) DeleteUserAccessToken(context.Context, *connect.Request[v1.DeleteUserAccessTokenRequest]) (*connect.Response[emptypb.Empty], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.DeleteUserAccessToken is not implemented"))
}
func (UnimplementedUserServiceHandler) ListUserSessions(context.Context, *connect.Request[v1.ListUserSessionsRequest]) (*connect.Response[v1.ListUserSessionsResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.ListUserSessions is not implemented"))
}
func (UnimplementedUserServiceHandler) RevokeUserSession(context.Context, *connect.Request[v1.RevokeUserSessionRequest]) (*connect.Response[emptypb.Empty], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.RevokeUserSession is not implemented"))
}
func (UnimplementedUserServiceHandler) ListUserWebhooks(context.Context, *connect.Request[v1.ListUserWebhooksRequest]) (*connect.Response[v1.ListUserWebhooksResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.ListUserWebhooks is not implemented"))
}
func (UnimplementedUserServiceHandler) CreateUserWebhook(context.Context, *connect.Request[v1.CreateUserWebhookRequest]) (*connect.Response[v1.UserWebhook], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.CreateUserWebhook is not implemented"))
}
func (UnimplementedUserServiceHandler) UpdateUserWebhook(context.Context, *connect.Request[v1.UpdateUserWebhookRequest]) (*connect.Response[v1.UserWebhook], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.UpdateUserWebhook is not implemented"))
}
func (UnimplementedUserServiceHandler) DeleteUserWebhook(context.Context, *connect.Request[v1.DeleteUserWebhookRequest]) (*connect.Response[emptypb.Empty], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.DeleteUserWebhook is not implemented"))
}
func (UnimplementedUserServiceHandler) ListUserNotifications(context.Context, *connect.Request[v1.ListUserNotificationsRequest]) (*connect.Response[v1.ListUserNotificationsResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.ListUserNotifications is not implemented"))
}
func (UnimplementedUserServiceHandler) UpdateUserNotification(context.Context, *connect.Request[v1.UpdateUserNotificationRequest]) (*connect.Response[v1.UserNotification], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.UpdateUserNotification is not implemented"))
}
func (UnimplementedUserServiceHandler) DeleteUserNotification(context.Context, *connect.Request[v1.DeleteUserNotificationRequest]) (*connect.Response[emptypb.Empty], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.DeleteUserNotification is not implemented"))
}

View File

@ -0,0 +1,105 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.10
// protoc (unknown)
// source: google/api/annotations.proto
package annotations
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
descriptorpb "google.golang.org/protobuf/types/descriptorpb"
reflect "reflect"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
var file_google_api_annotations_proto_extTypes = []protoimpl.ExtensionInfo{
{
ExtendedType: (*descriptorpb.MethodOptions)(nil),
ExtensionType: (*HttpRule)(nil),
Field: 72295728,
Name: "google.api.http",
Tag: "bytes,72295728,opt,name=http",
Filename: "google/api/annotations.proto",
},
}
// Extension fields to descriptorpb.MethodOptions.
var (
// See `HttpRule`.
//
// optional google.api.HttpRule http = 72295728;
E_Http = &file_google_api_annotations_proto_extTypes[0]
)
var File_google_api_annotations_proto protoreflect.FileDescriptor
const file_google_api_annotations_proto_rawDesc = "" +
"\n" +
"\x1cgoogle/api/annotations.proto\x12\n" +
"google.api\x1a\x15google/api/http.proto\x1a google/protobuf/descriptor.proto:K\n" +
"\x04http\x12\x1e.google.protobuf.MethodOptions\x18\xb0ʼ\" \x01(\v2\x14.google.api.HttpRuleR\x04httpB\xae\x01\n" +
"\x0ecom.google.apiB\x10AnnotationsProtoP\x01ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\xa2\x02\x03GAX\xaa\x02\n" +
"Google.Api\xca\x02\n" +
"Google\\Api\xe2\x02\x16Google\\Api\\GPBMetadata\xea\x02\vGoogle::Apib\x06proto3"
var file_google_api_annotations_proto_goTypes = []any{
(*descriptorpb.MethodOptions)(nil), // 0: google.protobuf.MethodOptions
(*HttpRule)(nil), // 1: google.api.HttpRule
}
var file_google_api_annotations_proto_depIdxs = []int32{
0, // 0: google.api.http:extendee -> google.protobuf.MethodOptions
1, // 1: google.api.http:type_name -> google.api.HttpRule
2, // [2:2] is the sub-list for method output_type
2, // [2:2] is the sub-list for method input_type
1, // [1:2] is the sub-list for extension type_name
0, // [0:1] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_google_api_annotations_proto_init() }
func file_google_api_annotations_proto_init() {
if File_google_api_annotations_proto != nil {
return
}
file_google_api_http_proto_init()
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_google_api_annotations_proto_rawDesc), len(file_google_api_annotations_proto_rawDesc)),
NumEnums: 0,
NumMessages: 0,
NumExtensions: 1,
NumServices: 0,
},
GoTypes: file_google_api_annotations_proto_goTypes,
DependencyIndexes: file_google_api_annotations_proto_depIdxs,
ExtensionInfos: file_google_api_annotations_proto_extTypes,
}.Build()
File_google_api_annotations_proto = out.File
file_google_api_annotations_proto_goTypes = nil
file_google_api_annotations_proto_depIdxs = nil
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,252 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.10
// protoc (unknown)
// source: google/api/field_behavior.proto
package annotations
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
descriptorpb "google.golang.org/protobuf/types/descriptorpb"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// An indicator of the behavior of a given field (for example, that a field
// is required in requests, or given as output but ignored as input).
// This **does not** change the behavior in protocol buffers itself; it only
// denotes the behavior and may affect how API tooling handles the field.
//
// Note: This enum **may** receive new values in the future.
type FieldBehavior int32
const (
// Conventional default for enums. Do not use this.
FieldBehavior_FIELD_BEHAVIOR_UNSPECIFIED FieldBehavior = 0
// Specifically denotes a field as optional.
// While all fields in protocol buffers are optional, this may be specified
// for emphasis if appropriate.
FieldBehavior_OPTIONAL FieldBehavior = 1
// Denotes a field as required.
// This indicates that the field **must** be provided as part of the request,
// and failure to do so will cause an error (usually `INVALID_ARGUMENT`).
FieldBehavior_REQUIRED FieldBehavior = 2
// Denotes a field as output only.
// This indicates that the field is provided in responses, but including the
// field in a request does nothing (the server *must* ignore it and
// *must not* throw an error as a result of the field's presence).
FieldBehavior_OUTPUT_ONLY FieldBehavior = 3
// Denotes a field as input only.
// This indicates that the field is provided in requests, and the
// corresponding field is not included in output.
FieldBehavior_INPUT_ONLY FieldBehavior = 4
// Denotes a field as immutable.
// This indicates that the field may be set once in a request to create a
// resource, but may not be changed thereafter.
FieldBehavior_IMMUTABLE FieldBehavior = 5
// Denotes that a (repeated) field is an unordered list.
// This indicates that the service may provide the elements of the list
// in any arbitrary order, rather than the order the user originally
// provided. Additionally, the list's order may or may not be stable.
FieldBehavior_UNORDERED_LIST FieldBehavior = 6
// Denotes that this field returns a non-empty default value if not set.
// This indicates that if the user provides the empty value in a request,
// a non-empty value will be returned. The user will not be aware of what
// non-empty value to expect.
FieldBehavior_NON_EMPTY_DEFAULT FieldBehavior = 7
// Denotes that the field in a resource (a message annotated with
// google.api.resource) is used in the resource name to uniquely identify the
// resource. For AIP-compliant APIs, this should only be applied to the
// `name` field on the resource.
//
// This behavior should not be applied to references to other resources within
// the message.
//
// The identifier field of resources often have different field behavior
// depending on the request it is embedded in (e.g. for Create methods name
// is optional and unused, while for Update methods it is required). Instead
// of method-specific annotations, only `IDENTIFIER` is required.
FieldBehavior_IDENTIFIER FieldBehavior = 8
)
// Enum value maps for FieldBehavior.
var (
FieldBehavior_name = map[int32]string{
0: "FIELD_BEHAVIOR_UNSPECIFIED",
1: "OPTIONAL",
2: "REQUIRED",
3: "OUTPUT_ONLY",
4: "INPUT_ONLY",
5: "IMMUTABLE",
6: "UNORDERED_LIST",
7: "NON_EMPTY_DEFAULT",
8: "IDENTIFIER",
}
FieldBehavior_value = map[string]int32{
"FIELD_BEHAVIOR_UNSPECIFIED": 0,
"OPTIONAL": 1,
"REQUIRED": 2,
"OUTPUT_ONLY": 3,
"INPUT_ONLY": 4,
"IMMUTABLE": 5,
"UNORDERED_LIST": 6,
"NON_EMPTY_DEFAULT": 7,
"IDENTIFIER": 8,
}
)
func (x FieldBehavior) Enum() *FieldBehavior {
p := new(FieldBehavior)
*p = x
return p
}
func (x FieldBehavior) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (FieldBehavior) Descriptor() protoreflect.EnumDescriptor {
return file_google_api_field_behavior_proto_enumTypes[0].Descriptor()
}
func (FieldBehavior) Type() protoreflect.EnumType {
return &file_google_api_field_behavior_proto_enumTypes[0]
}
func (x FieldBehavior) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use FieldBehavior.Descriptor instead.
func (FieldBehavior) EnumDescriptor() ([]byte, []int) {
return file_google_api_field_behavior_proto_rawDescGZIP(), []int{0}
}
var file_google_api_field_behavior_proto_extTypes = []protoimpl.ExtensionInfo{
{
ExtendedType: (*descriptorpb.FieldOptions)(nil),
ExtensionType: ([]FieldBehavior)(nil),
Field: 1052,
Name: "google.api.field_behavior",
Tag: "varint,1052,rep,name=field_behavior,enum=google.api.FieldBehavior",
Filename: "google/api/field_behavior.proto",
},
}
// Extension fields to descriptorpb.FieldOptions.
var (
// A designation of a specific field behavior (required, output only, etc.)
// in protobuf messages.
//
// Examples:
//
// string name = 1 [(google.api.field_behavior) = REQUIRED];
// State state = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
// google.protobuf.Duration ttl = 1
// [(google.api.field_behavior) = INPUT_ONLY];
// google.protobuf.Timestamp expire_time = 1
// [(google.api.field_behavior) = OUTPUT_ONLY,
// (google.api.field_behavior) = IMMUTABLE];
//
// repeated google.api.FieldBehavior field_behavior = 1052;
E_FieldBehavior = &file_google_api_field_behavior_proto_extTypes[0]
)
var File_google_api_field_behavior_proto protoreflect.FileDescriptor
const file_google_api_field_behavior_proto_rawDesc = "" +
"\n" +
"\x1fgoogle/api/field_behavior.proto\x12\n" +
"google.api\x1a google/protobuf/descriptor.proto*\xb6\x01\n" +
"\rFieldBehavior\x12\x1e\n" +
"\x1aFIELD_BEHAVIOR_UNSPECIFIED\x10\x00\x12\f\n" +
"\bOPTIONAL\x10\x01\x12\f\n" +
"\bREQUIRED\x10\x02\x12\x0f\n" +
"\vOUTPUT_ONLY\x10\x03\x12\x0e\n" +
"\n" +
"INPUT_ONLY\x10\x04\x12\r\n" +
"\tIMMUTABLE\x10\x05\x12\x12\n" +
"\x0eUNORDERED_LIST\x10\x06\x12\x15\n" +
"\x11NON_EMPTY_DEFAULT\x10\a\x12\x0e\n" +
"\n" +
"IDENTIFIER\x10\b:d\n" +
"\x0efield_behavior\x12\x1d.google.protobuf.FieldOptions\x18\x9c\b \x03(\x0e2\x19.google.api.FieldBehaviorB\x02\x10\x00R\rfieldBehaviorB\xb0\x01\n" +
"\x0ecom.google.apiB\x12FieldBehaviorProtoP\x01ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\xa2\x02\x03GAX\xaa\x02\n" +
"Google.Api\xca\x02\n" +
"Google\\Api\xe2\x02\x16Google\\Api\\GPBMetadata\xea\x02\vGoogle::Apib\x06proto3"
var (
file_google_api_field_behavior_proto_rawDescOnce sync.Once
file_google_api_field_behavior_proto_rawDescData []byte
)
func file_google_api_field_behavior_proto_rawDescGZIP() []byte {
file_google_api_field_behavior_proto_rawDescOnce.Do(func() {
file_google_api_field_behavior_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_google_api_field_behavior_proto_rawDesc), len(file_google_api_field_behavior_proto_rawDesc)))
})
return file_google_api_field_behavior_proto_rawDescData
}
var file_google_api_field_behavior_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_google_api_field_behavior_proto_goTypes = []any{
(FieldBehavior)(0), // 0: google.api.FieldBehavior
(*descriptorpb.FieldOptions)(nil), // 1: google.protobuf.FieldOptions
}
var file_google_api_field_behavior_proto_depIdxs = []int32{
1, // 0: google.api.field_behavior:extendee -> google.protobuf.FieldOptions
0, // 1: google.api.field_behavior:type_name -> google.api.FieldBehavior
2, // [2:2] is the sub-list for method output_type
2, // [2:2] is the sub-list for method input_type
1, // [1:2] is the sub-list for extension type_name
0, // [0:1] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_google_api_field_behavior_proto_init() }
func file_google_api_field_behavior_proto_init() {
if File_google_api_field_behavior_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_google_api_field_behavior_proto_rawDesc), len(file_google_api_field_behavior_proto_rawDesc)),
NumEnums: 1,
NumMessages: 0,
NumExtensions: 1,
NumServices: 0,
},
GoTypes: file_google_api_field_behavior_proto_goTypes,
DependencyIndexes: file_google_api_field_behavior_proto_depIdxs,
EnumInfos: file_google_api_field_behavior_proto_enumTypes,
ExtensionInfos: file_google_api_field_behavior_proto_extTypes,
}.Build()
File_google_api_field_behavior_proto = out.File
file_google_api_field_behavior_proto_goTypes = nil
file_google_api_field_behavior_proto_depIdxs = nil
}

View File

@ -0,0 +1,718 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.10
// protoc (unknown)
// source: google/api/http.proto
package annotations
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// Defines the HTTP configuration for an API service. It contains a list of
// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method
// to one or more HTTP REST API methods.
type Http struct {
state protoimpl.MessageState `protogen:"open.v1"`
// A list of HTTP configuration rules that apply to individual API methods.
//
// **NOTE:** All service configuration rules follow "last one wins" order.
Rules []*HttpRule `protobuf:"bytes,1,rep,name=rules,proto3" json:"rules,omitempty"`
// When set to true, URL path parameters will be fully URI-decoded except in
// cases of single segment matches in reserved expansion, where "%2F" will be
// left encoded.
//
// The default behavior is to not decode RFC 6570 reserved characters in multi
// segment matches.
FullyDecodeReservedExpansion bool `protobuf:"varint,2,opt,name=fully_decode_reserved_expansion,json=fullyDecodeReservedExpansion,proto3" json:"fully_decode_reserved_expansion,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Http) Reset() {
*x = Http{}
mi := &file_google_api_http_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Http) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Http) ProtoMessage() {}
func (x *Http) ProtoReflect() protoreflect.Message {
mi := &file_google_api_http_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Http.ProtoReflect.Descriptor instead.
func (*Http) Descriptor() ([]byte, []int) {
return file_google_api_http_proto_rawDescGZIP(), []int{0}
}
func (x *Http) GetRules() []*HttpRule {
if x != nil {
return x.Rules
}
return nil
}
func (x *Http) GetFullyDecodeReservedExpansion() bool {
if x != nil {
return x.FullyDecodeReservedExpansion
}
return false
}
// gRPC Transcoding
//
// gRPC Transcoding is a feature for mapping between a gRPC method and one or
// more HTTP REST endpoints. It allows developers to build a single API service
// that supports both gRPC APIs and REST APIs. Many systems, including [Google
// APIs](https://github.com/googleapis/googleapis),
// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC
// Gateway](https://github.com/grpc-ecosystem/grpc-gateway),
// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature
// and use it for large scale production services.
//
// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies
// how different portions of the gRPC request message are mapped to the URL
// path, URL query parameters, and HTTP request body. It also controls how the
// gRPC response message is mapped to the HTTP response body. `HttpRule` is
// typically specified as an `google.api.http` annotation on the gRPC method.
//
// Each mapping specifies a URL path template and an HTTP method. The path
// template may refer to one or more fields in the gRPC request message, as long
// as each field is a non-repeated field with a primitive (non-message) type.
// The path template controls how fields of the request message are mapped to
// the URL path.
//
// Example:
//
// service Messaging {
// rpc GetMessage(GetMessageRequest) returns (Message) {
// option (google.api.http) = {
// get: "/v1/{name=messages/*}"
// };
// }
// }
// message GetMessageRequest {
// string name = 1; // Mapped to URL path.
// }
// message Message {
// string text = 1; // The resource content.
// }
//
// This enables an HTTP REST to gRPC mapping as below:
//
// - HTTP: `GET /v1/messages/123456`
// - gRPC: `GetMessage(name: "messages/123456")`
//
// Any fields in the request message which are not bound by the path template
// automatically become HTTP query parameters if there is no HTTP request body.
// For example:
//
// service Messaging {
// rpc GetMessage(GetMessageRequest) returns (Message) {
// option (google.api.http) = {
// get:"/v1/messages/{message_id}"
// };
// }
// }
// message GetMessageRequest {
// message SubMessage {
// string subfield = 1;
// }
// string message_id = 1; // Mapped to URL path.
// int64 revision = 2; // Mapped to URL query parameter `revision`.
// SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`.
// }
//
// This enables a HTTP JSON to RPC mapping as below:
//
// - HTTP: `GET /v1/messages/123456?revision=2&sub.subfield=foo`
// - gRPC: `GetMessage(message_id: "123456" revision: 2 sub:
// SubMessage(subfield: "foo"))`
//
// Note that fields which are mapped to URL query parameters must have a
// primitive type or a repeated primitive type or a non-repeated message type.
// In the case of a repeated type, the parameter can be repeated in the URL
// as `...?param=A&param=B`. In the case of a message type, each field of the
// message is mapped to a separate parameter, such as
// `...?foo.a=A&foo.b=B&foo.c=C`.
//
// For HTTP methods that allow a request body, the `body` field
// specifies the mapping. Consider a REST update method on the
// message resource collection:
//
// service Messaging {
// rpc UpdateMessage(UpdateMessageRequest) returns (Message) {
// option (google.api.http) = {
// patch: "/v1/messages/{message_id}"
// body: "message"
// };
// }
// }
// message UpdateMessageRequest {
// string message_id = 1; // mapped to the URL
// Message message = 2; // mapped to the body
// }
//
// The following HTTP JSON to RPC mapping is enabled, where the
// representation of the JSON in the request body is determined by
// protos JSON encoding:
//
// - HTTP: `PATCH /v1/messages/123456 { "text": "Hi!" }`
// - gRPC: `UpdateMessage(message_id: "123456" message { text: "Hi!" })`
//
// The special name `*` can be used in the body mapping to define that
// every field not bound by the path template should be mapped to the
// request body. This enables the following alternative definition of
// the update method:
//
// service Messaging {
// rpc UpdateMessage(Message) returns (Message) {
// option (google.api.http) = {
// patch: "/v1/messages/{message_id}"
// body: "*"
// };
// }
// }
// message Message {
// string message_id = 1;
// string text = 2;
// }
//
// The following HTTP JSON to RPC mapping is enabled:
//
// - HTTP: `PATCH /v1/messages/123456 { "text": "Hi!" }`
// - gRPC: `UpdateMessage(message_id: "123456" text: "Hi!")`
//
// Note that when using `*` in the body mapping, it is not possible to
// have HTTP parameters, as all fields not bound by the path end in
// the body. This makes this option more rarely used in practice when
// defining REST APIs. The common usage of `*` is in custom methods
// which don't use the URL at all for transferring data.
//
// It is possible to define multiple HTTP methods for one RPC by using
// the `additional_bindings` option. Example:
//
// service Messaging {
// rpc GetMessage(GetMessageRequest) returns (Message) {
// option (google.api.http) = {
// get: "/v1/messages/{message_id}"
// additional_bindings {
// get: "/v1/users/{user_id}/messages/{message_id}"
// }
// };
// }
// }
// message GetMessageRequest {
// string message_id = 1;
// string user_id = 2;
// }
//
// This enables the following two alternative HTTP JSON to RPC mappings:
//
// - HTTP: `GET /v1/messages/123456`
// - gRPC: `GetMessage(message_id: "123456")`
//
// - HTTP: `GET /v1/users/me/messages/123456`
// - gRPC: `GetMessage(user_id: "me" message_id: "123456")`
//
// # Rules for HTTP mapping
//
// 1. Leaf request fields (recursive expansion nested messages in the request
// message) are classified into three categories:
// - Fields referred by the path template. They are passed via the URL path.
// - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They
// are passed via the HTTP
// request body.
// - All other fields are passed via the URL query parameters, and the
// parameter name is the field path in the request message. A repeated
// field can be represented as multiple query parameters under the same
// name.
// 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL
// query parameter, all fields
// are passed via URL path and HTTP request body.
// 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP
// request body, all
// fields are passed via URL path and URL query parameters.
//
// Path template syntax
//
// Template = "/" Segments [ Verb ] ;
// Segments = Segment { "/" Segment } ;
// Segment = "*" | "**" | LITERAL | Variable ;
// Variable = "{" FieldPath [ "=" Segments ] "}" ;
// FieldPath = IDENT { "." IDENT } ;
// Verb = ":" LITERAL ;
//
// The syntax `*` matches a single URL path segment. The syntax `**` matches
// zero or more URL path segments, which must be the last part of the URL path
// except the `Verb`.
//
// The syntax `Variable` matches part of the URL path as specified by its
// template. A variable template must not contain other variables. If a variable
// matches a single path segment, its template may be omitted, e.g. `{var}`
// is equivalent to `{var=*}`.
//
// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL`
// contains any reserved character, such characters should be percent-encoded
// before the matching.
//
// If a variable contains exactly one path segment, such as `"{var}"` or
// `"{var=*}"`, when such a variable is expanded into a URL path on the client
// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The
// server side does the reverse decoding. Such variables show up in the
// [Discovery
// Document](https://developers.google.com/discovery/v1/reference/apis) as
// `{var}`.
//
// If a variable contains multiple path segments, such as `"{var=foo/*}"`
// or `"{var=**}"`, when such a variable is expanded into a URL path on the
// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded.
// The server side does the reverse decoding, except "%2F" and "%2f" are left
// unchanged. Such variables show up in the
// [Discovery
// Document](https://developers.google.com/discovery/v1/reference/apis) as
// `{+var}`.
//
// # Using gRPC API Service Configuration
//
// gRPC API Service Configuration (service config) is a configuration language
// for configuring a gRPC service to become a user-facing product. The
// service config is simply the YAML representation of the `google.api.Service`
// proto message.
//
// As an alternative to annotating your proto file, you can configure gRPC
// transcoding in your service config YAML files. You do this by specifying a
// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same
// effect as the proto annotation. This can be particularly useful if you
// have a proto that is reused in multiple services. Note that any transcoding
// specified in the service config will override any matching transcoding
// configuration in the proto.
//
// The following example selects a gRPC method and applies an `HttpRule` to it:
//
// http:
// rules:
// - selector: example.v1.Messaging.GetMessage
// get: /v1/messages/{message_id}/{sub.subfield}
//
// # Special notes
//
// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the
// proto to JSON conversion must follow the [proto3
// specification](https://developers.google.com/protocol-buffers/docs/proto3#json).
//
// While the single segment variable follows the semantics of
// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String
// Expansion, the multi segment variable **does not** follow RFC 6570 Section
// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion
// does not expand special characters like `?` and `#`, which would lead
// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding
// for multi segment variables.
//
// The path variables **must not** refer to any repeated or mapped field,
// because client libraries are not capable of handling such variable expansion.
//
// The path variables **must not** capture the leading "/" character. The reason
// is that the most common use case "{var}" does not capture the leading "/"
// character. For consistency, all path variables must share the same behavior.
//
// Repeated message fields must not be mapped to URL query parameters, because
// no client library can support such complicated mapping.
//
// If an API needs to use a JSON array for request or response body, it can map
// the request or response body to a repeated field. However, some gRPC
// Transcoding implementations may not support this feature.
type HttpRule struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Selects a method to which this rule applies.
//
// Refer to [selector][google.api.DocumentationRule.selector] for syntax
// details.
Selector string `protobuf:"bytes,1,opt,name=selector,proto3" json:"selector,omitempty"`
// Determines the URL pattern is matched by this rules. This pattern can be
// used with any of the {get|put|post|delete|patch} methods. A custom method
// can be defined using the 'custom' field.
//
// Types that are valid to be assigned to Pattern:
//
// *HttpRule_Get
// *HttpRule_Put
// *HttpRule_Post
// *HttpRule_Delete
// *HttpRule_Patch
// *HttpRule_Custom
Pattern isHttpRule_Pattern `protobuf_oneof:"pattern"`
// The name of the request field whose value is mapped to the HTTP request
// body, or `*` for mapping all request fields not captured by the path
// pattern to the HTTP body, or omitted for not having any HTTP request body.
//
// NOTE: the referred field must be present at the top-level of the request
// message type.
Body string `protobuf:"bytes,7,opt,name=body,proto3" json:"body,omitempty"`
// Optional. The name of the response field whose value is mapped to the HTTP
// response body. When omitted, the entire response message will be used
// as the HTTP response body.
//
// NOTE: The referred field must be present at the top-level of the response
// message type.
ResponseBody string `protobuf:"bytes,12,opt,name=response_body,json=responseBody,proto3" json:"response_body,omitempty"`
// Additional HTTP bindings for the selector. Nested bindings must
// not contain an `additional_bindings` field themselves (that is,
// the nesting may only be one level deep).
AdditionalBindings []*HttpRule `protobuf:"bytes,11,rep,name=additional_bindings,json=additionalBindings,proto3" json:"additional_bindings,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *HttpRule) Reset() {
*x = HttpRule{}
mi := &file_google_api_http_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *HttpRule) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*HttpRule) ProtoMessage() {}
func (x *HttpRule) ProtoReflect() protoreflect.Message {
mi := &file_google_api_http_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use HttpRule.ProtoReflect.Descriptor instead.
func (*HttpRule) Descriptor() ([]byte, []int) {
return file_google_api_http_proto_rawDescGZIP(), []int{1}
}
func (x *HttpRule) GetSelector() string {
if x != nil {
return x.Selector
}
return ""
}
func (x *HttpRule) GetPattern() isHttpRule_Pattern {
if x != nil {
return x.Pattern
}
return nil
}
func (x *HttpRule) GetGet() string {
if x != nil {
if x, ok := x.Pattern.(*HttpRule_Get); ok {
return x.Get
}
}
return ""
}
func (x *HttpRule) GetPut() string {
if x != nil {
if x, ok := x.Pattern.(*HttpRule_Put); ok {
return x.Put
}
}
return ""
}
func (x *HttpRule) GetPost() string {
if x != nil {
if x, ok := x.Pattern.(*HttpRule_Post); ok {
return x.Post
}
}
return ""
}
func (x *HttpRule) GetDelete() string {
if x != nil {
if x, ok := x.Pattern.(*HttpRule_Delete); ok {
return x.Delete
}
}
return ""
}
func (x *HttpRule) GetPatch() string {
if x != nil {
if x, ok := x.Pattern.(*HttpRule_Patch); ok {
return x.Patch
}
}
return ""
}
func (x *HttpRule) GetCustom() *CustomHttpPattern {
if x != nil {
if x, ok := x.Pattern.(*HttpRule_Custom); ok {
return x.Custom
}
}
return nil
}
func (x *HttpRule) GetBody() string {
if x != nil {
return x.Body
}
return ""
}
func (x *HttpRule) GetResponseBody() string {
if x != nil {
return x.ResponseBody
}
return ""
}
func (x *HttpRule) GetAdditionalBindings() []*HttpRule {
if x != nil {
return x.AdditionalBindings
}
return nil
}
type isHttpRule_Pattern interface {
isHttpRule_Pattern()
}
type HttpRule_Get struct {
// Maps to HTTP GET. Used for listing and getting information about
// resources.
Get string `protobuf:"bytes,2,opt,name=get,proto3,oneof"`
}
type HttpRule_Put struct {
// Maps to HTTP PUT. Used for replacing a resource.
Put string `protobuf:"bytes,3,opt,name=put,proto3,oneof"`
}
type HttpRule_Post struct {
// Maps to HTTP POST. Used for creating a resource or performing an action.
Post string `protobuf:"bytes,4,opt,name=post,proto3,oneof"`
}
type HttpRule_Delete struct {
// Maps to HTTP DELETE. Used for deleting a resource.
Delete string `protobuf:"bytes,5,opt,name=delete,proto3,oneof"`
}
type HttpRule_Patch struct {
// Maps to HTTP PATCH. Used for updating a resource.
Patch string `protobuf:"bytes,6,opt,name=patch,proto3,oneof"`
}
type HttpRule_Custom struct {
// The custom pattern is used for specifying an HTTP method that is not
// included in the `pattern` field, such as HEAD, or "*" to leave the
// HTTP method unspecified for this rule. The wild-card rule is useful
// for services that provide content to Web (HTML) clients.
Custom *CustomHttpPattern `protobuf:"bytes,8,opt,name=custom,proto3,oneof"`
}
func (*HttpRule_Get) isHttpRule_Pattern() {}
func (*HttpRule_Put) isHttpRule_Pattern() {}
func (*HttpRule_Post) isHttpRule_Pattern() {}
func (*HttpRule_Delete) isHttpRule_Pattern() {}
func (*HttpRule_Patch) isHttpRule_Pattern() {}
func (*HttpRule_Custom) isHttpRule_Pattern() {}
// A custom pattern is used for defining custom HTTP verb.
type CustomHttpPattern struct {
state protoimpl.MessageState `protogen:"open.v1"`
// The name of this custom HTTP verb.
Kind string `protobuf:"bytes,1,opt,name=kind,proto3" json:"kind,omitempty"`
// The path matched by this custom verb.
Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *CustomHttpPattern) Reset() {
*x = CustomHttpPattern{}
mi := &file_google_api_http_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *CustomHttpPattern) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CustomHttpPattern) ProtoMessage() {}
func (x *CustomHttpPattern) ProtoReflect() protoreflect.Message {
mi := &file_google_api_http_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CustomHttpPattern.ProtoReflect.Descriptor instead.
func (*CustomHttpPattern) Descriptor() ([]byte, []int) {
return file_google_api_http_proto_rawDescGZIP(), []int{2}
}
func (x *CustomHttpPattern) GetKind() string {
if x != nil {
return x.Kind
}
return ""
}
func (x *CustomHttpPattern) GetPath() string {
if x != nil {
return x.Path
}
return ""
}
var File_google_api_http_proto protoreflect.FileDescriptor
const file_google_api_http_proto_rawDesc = "" +
"\n" +
"\x15google/api/http.proto\x12\n" +
"google.api\"y\n" +
"\x04Http\x12*\n" +
"\x05rules\x18\x01 \x03(\v2\x14.google.api.HttpRuleR\x05rules\x12E\n" +
"\x1ffully_decode_reserved_expansion\x18\x02 \x01(\bR\x1cfullyDecodeReservedExpansion\"\xda\x02\n" +
"\bHttpRule\x12\x1a\n" +
"\bselector\x18\x01 \x01(\tR\bselector\x12\x12\n" +
"\x03get\x18\x02 \x01(\tH\x00R\x03get\x12\x12\n" +
"\x03put\x18\x03 \x01(\tH\x00R\x03put\x12\x14\n" +
"\x04post\x18\x04 \x01(\tH\x00R\x04post\x12\x18\n" +
"\x06delete\x18\x05 \x01(\tH\x00R\x06delete\x12\x16\n" +
"\x05patch\x18\x06 \x01(\tH\x00R\x05patch\x127\n" +
"\x06custom\x18\b \x01(\v2\x1d.google.api.CustomHttpPatternH\x00R\x06custom\x12\x12\n" +
"\x04body\x18\a \x01(\tR\x04body\x12#\n" +
"\rresponse_body\x18\f \x01(\tR\fresponseBody\x12E\n" +
"\x13additional_bindings\x18\v \x03(\v2\x14.google.api.HttpRuleR\x12additionalBindingsB\t\n" +
"\apattern\";\n" +
"\x11CustomHttpPattern\x12\x12\n" +
"\x04kind\x18\x01 \x01(\tR\x04kind\x12\x12\n" +
"\x04path\x18\x02 \x01(\tR\x04pathB\xa7\x01\n" +
"\x0ecom.google.apiB\tHttpProtoP\x01ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\xa2\x02\x03GAX\xaa\x02\n" +
"Google.Api\xca\x02\n" +
"Google\\Api\xe2\x02\x16Google\\Api\\GPBMetadata\xea\x02\vGoogle::Apib\x06proto3"
var (
file_google_api_http_proto_rawDescOnce sync.Once
file_google_api_http_proto_rawDescData []byte
)
func file_google_api_http_proto_rawDescGZIP() []byte {
file_google_api_http_proto_rawDescOnce.Do(func() {
file_google_api_http_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_google_api_http_proto_rawDesc), len(file_google_api_http_proto_rawDesc)))
})
return file_google_api_http_proto_rawDescData
}
var file_google_api_http_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_google_api_http_proto_goTypes = []any{
(*Http)(nil), // 0: google.api.Http
(*HttpRule)(nil), // 1: google.api.HttpRule
(*CustomHttpPattern)(nil), // 2: google.api.CustomHttpPattern
}
var file_google_api_http_proto_depIdxs = []int32{
1, // 0: google.api.Http.rules:type_name -> google.api.HttpRule
2, // 1: google.api.HttpRule.custom:type_name -> google.api.CustomHttpPattern
1, // 2: google.api.HttpRule.additional_bindings:type_name -> google.api.HttpRule
3, // [3:3] is the sub-list for method output_type
3, // [3:3] is the sub-list for method input_type
3, // [3:3] is the sub-list for extension type_name
3, // [3:3] is the sub-list for extension extendee
0, // [0:3] is the sub-list for field type_name
}
func init() { file_google_api_http_proto_init() }
func file_google_api_http_proto_init() {
if File_google_api_http_proto != nil {
return
}
file_google_api_http_proto_msgTypes[1].OneofWrappers = []any{
(*HttpRule_Get)(nil),
(*HttpRule_Put)(nil),
(*HttpRule_Post)(nil),
(*HttpRule_Delete)(nil),
(*HttpRule_Patch)(nil),
(*HttpRule_Custom)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_google_api_http_proto_rawDesc), len(file_google_api_http_proto_rawDesc)),
NumEnums: 0,
NumMessages: 3,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_google_api_http_proto_goTypes,
DependencyIndexes: file_google_api_http_proto_depIdxs,
MessageInfos: file_google_api_http_proto_msgTypes,
}.Build()
File_google_api_http_proto = out.File
file_google_api_http_proto_goTypes = nil
file_google_api_http_proto_depIdxs = nil
}

View File

@ -0,0 +1,592 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.10
// protoc (unknown)
// source: google/api/resource.proto
package annotations
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
descriptorpb "google.golang.org/protobuf/types/descriptorpb"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// A description of the historical or future-looking state of the
// resource pattern.
type ResourceDescriptor_History int32
const (
// The "unset" value.
ResourceDescriptor_HISTORY_UNSPECIFIED ResourceDescriptor_History = 0
// The resource originally had one pattern and launched as such, and
// additional patterns were added later.
ResourceDescriptor_ORIGINALLY_SINGLE_PATTERN ResourceDescriptor_History = 1
// The resource has one pattern, but the API owner expects to add more
// later. (This is the inverse of ORIGINALLY_SINGLE_PATTERN, and prevents
// that from being necessary once there are multiple patterns.)
ResourceDescriptor_FUTURE_MULTI_PATTERN ResourceDescriptor_History = 2
)
// Enum value maps for ResourceDescriptor_History.
var (
ResourceDescriptor_History_name = map[int32]string{
0: "HISTORY_UNSPECIFIED",
1: "ORIGINALLY_SINGLE_PATTERN",
2: "FUTURE_MULTI_PATTERN",
}
ResourceDescriptor_History_value = map[string]int32{
"HISTORY_UNSPECIFIED": 0,
"ORIGINALLY_SINGLE_PATTERN": 1,
"FUTURE_MULTI_PATTERN": 2,
}
)
func (x ResourceDescriptor_History) Enum() *ResourceDescriptor_History {
p := new(ResourceDescriptor_History)
*p = x
return p
}
func (x ResourceDescriptor_History) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (ResourceDescriptor_History) Descriptor() protoreflect.EnumDescriptor {
return file_google_api_resource_proto_enumTypes[0].Descriptor()
}
func (ResourceDescriptor_History) Type() protoreflect.EnumType {
return &file_google_api_resource_proto_enumTypes[0]
}
func (x ResourceDescriptor_History) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use ResourceDescriptor_History.Descriptor instead.
func (ResourceDescriptor_History) EnumDescriptor() ([]byte, []int) {
return file_google_api_resource_proto_rawDescGZIP(), []int{0, 0}
}
// A flag representing a specific style that a resource claims to conform to.
type ResourceDescriptor_Style int32
const (
// The unspecified value. Do not use.
ResourceDescriptor_STYLE_UNSPECIFIED ResourceDescriptor_Style = 0
// This resource is intended to be "declarative-friendly".
//
// Declarative-friendly resources must be more strictly consistent, and
// setting this to true communicates to tools that this resource should
// adhere to declarative-friendly expectations.
//
// Note: This is used by the API linter (linter.aip.dev) to enable
// additional checks.
ResourceDescriptor_DECLARATIVE_FRIENDLY ResourceDescriptor_Style = 1
)
// Enum value maps for ResourceDescriptor_Style.
var (
ResourceDescriptor_Style_name = map[int32]string{
0: "STYLE_UNSPECIFIED",
1: "DECLARATIVE_FRIENDLY",
}
ResourceDescriptor_Style_value = map[string]int32{
"STYLE_UNSPECIFIED": 0,
"DECLARATIVE_FRIENDLY": 1,
}
)
func (x ResourceDescriptor_Style) Enum() *ResourceDescriptor_Style {
p := new(ResourceDescriptor_Style)
*p = x
return p
}
func (x ResourceDescriptor_Style) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (ResourceDescriptor_Style) Descriptor() protoreflect.EnumDescriptor {
return file_google_api_resource_proto_enumTypes[1].Descriptor()
}
func (ResourceDescriptor_Style) Type() protoreflect.EnumType {
return &file_google_api_resource_proto_enumTypes[1]
}
func (x ResourceDescriptor_Style) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use ResourceDescriptor_Style.Descriptor instead.
func (ResourceDescriptor_Style) EnumDescriptor() ([]byte, []int) {
return file_google_api_resource_proto_rawDescGZIP(), []int{0, 1}
}
// A simple descriptor of a resource type.
//
// ResourceDescriptor annotates a resource message (either by means of a
// protobuf annotation or use in the service config), and associates the
// resource's schema, the resource type, and the pattern of the resource name.
//
// Example:
//
// message Topic {
// // Indicates this message defines a resource schema.
// // Declares the resource type in the format of {service}/{kind}.
// // For Kubernetes resources, the format is {api group}/{kind}.
// option (google.api.resource) = {
// type: "pubsub.googleapis.com/Topic"
// pattern: "projects/{project}/topics/{topic}"
// };
// }
//
// The ResourceDescriptor Yaml config will look like:
//
// resources:
// - type: "pubsub.googleapis.com/Topic"
// pattern: "projects/{project}/topics/{topic}"
//
// Sometimes, resources have multiple patterns, typically because they can
// live under multiple parents.
//
// Example:
//
// message LogEntry {
// option (google.api.resource) = {
// type: "logging.googleapis.com/LogEntry"
// pattern: "projects/{project}/logs/{log}"
// pattern: "folders/{folder}/logs/{log}"
// pattern: "organizations/{organization}/logs/{log}"
// pattern: "billingAccounts/{billing_account}/logs/{log}"
// };
// }
//
// The ResourceDescriptor Yaml config will look like:
//
// resources:
// - type: 'logging.googleapis.com/LogEntry'
// pattern: "projects/{project}/logs/{log}"
// pattern: "folders/{folder}/logs/{log}"
// pattern: "organizations/{organization}/logs/{log}"
// pattern: "billingAccounts/{billing_account}/logs/{log}"
type ResourceDescriptor struct {
state protoimpl.MessageState `protogen:"open.v1"`
// The resource type. It must be in the format of
// {service_name}/{resource_type_kind}. The `resource_type_kind` must be
// singular and must not include version numbers.
//
// Example: `storage.googleapis.com/Bucket`
//
// The value of the resource_type_kind must follow the regular expression
// /[A-Za-z][a-zA-Z0-9]+/. It should start with an upper case character and
// should use PascalCase (UpperCamelCase). The maximum number of
// characters allowed for the `resource_type_kind` is 100.
Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"`
// Optional. The relative resource name pattern associated with this resource
// type. The DNS prefix of the full resource name shouldn't be specified here.
//
// The path pattern must follow the syntax, which aligns with HTTP binding
// syntax:
//
// Template = Segment { "/" Segment } ;
// Segment = LITERAL | Variable ;
// Variable = "{" LITERAL "}" ;
//
// Examples:
//
// - "projects/{project}/topics/{topic}"
// - "projects/{project}/knowledgeBases/{knowledge_base}"
//
// The components in braces correspond to the IDs for each resource in the
// hierarchy. It is expected that, if multiple patterns are provided,
// the same component name (e.g. "project") refers to IDs of the same
// type of resource.
Pattern []string `protobuf:"bytes,2,rep,name=pattern,proto3" json:"pattern,omitempty"`
// Optional. The field on the resource that designates the resource name
// field. If omitted, this is assumed to be "name".
NameField string `protobuf:"bytes,3,opt,name=name_field,json=nameField,proto3" json:"name_field,omitempty"`
// Optional. The historical or future-looking state of the resource pattern.
//
// Example:
//
// // The InspectTemplate message originally only supported resource
// // names with organization, and project was added later.
// message InspectTemplate {
// option (google.api.resource) = {
// type: "dlp.googleapis.com/InspectTemplate"
// pattern:
// "organizations/{organization}/inspectTemplates/{inspect_template}"
// pattern: "projects/{project}/inspectTemplates/{inspect_template}"
// history: ORIGINALLY_SINGLE_PATTERN
// };
// }
History ResourceDescriptor_History `protobuf:"varint,4,opt,name=history,proto3,enum=google.api.ResourceDescriptor_History" json:"history,omitempty"`
// The plural name used in the resource name and permission names, such as
// 'projects' for the resource name of 'projects/{project}' and the permission
// name of 'cloudresourcemanager.googleapis.com/projects.get'. One exception
// to this is for Nested Collections that have stuttering names, as defined
// in [AIP-122](https://google.aip.dev/122#nested-collections), where the
// collection ID in the resource name pattern does not necessarily directly
// match the `plural` value.
//
// It is the same concept of the `plural` field in k8s CRD spec
// https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/
//
// Note: The plural form is required even for singleton resources. See
// https://aip.dev/156
Plural string `protobuf:"bytes,5,opt,name=plural,proto3" json:"plural,omitempty"`
// The same concept of the `singular` field in k8s CRD spec
// https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/
// Such as "project" for the `resourcemanager.googleapis.com/Project` type.
Singular string `protobuf:"bytes,6,opt,name=singular,proto3" json:"singular,omitempty"`
// Style flag(s) for this resource.
// These indicate that a resource is expected to conform to a given
// style. See the specific style flags for additional information.
Style []ResourceDescriptor_Style `protobuf:"varint,10,rep,packed,name=style,proto3,enum=google.api.ResourceDescriptor_Style" json:"style,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ResourceDescriptor) Reset() {
*x = ResourceDescriptor{}
mi := &file_google_api_resource_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ResourceDescriptor) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ResourceDescriptor) ProtoMessage() {}
func (x *ResourceDescriptor) ProtoReflect() protoreflect.Message {
mi := &file_google_api_resource_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ResourceDescriptor.ProtoReflect.Descriptor instead.
func (*ResourceDescriptor) Descriptor() ([]byte, []int) {
return file_google_api_resource_proto_rawDescGZIP(), []int{0}
}
func (x *ResourceDescriptor) GetType() string {
if x != nil {
return x.Type
}
return ""
}
func (x *ResourceDescriptor) GetPattern() []string {
if x != nil {
return x.Pattern
}
return nil
}
func (x *ResourceDescriptor) GetNameField() string {
if x != nil {
return x.NameField
}
return ""
}
func (x *ResourceDescriptor) GetHistory() ResourceDescriptor_History {
if x != nil {
return x.History
}
return ResourceDescriptor_HISTORY_UNSPECIFIED
}
func (x *ResourceDescriptor) GetPlural() string {
if x != nil {
return x.Plural
}
return ""
}
func (x *ResourceDescriptor) GetSingular() string {
if x != nil {
return x.Singular
}
return ""
}
func (x *ResourceDescriptor) GetStyle() []ResourceDescriptor_Style {
if x != nil {
return x.Style
}
return nil
}
// Defines a proto annotation that describes a string field that refers to
// an API resource.
type ResourceReference struct {
state protoimpl.MessageState `protogen:"open.v1"`
// The resource type that the annotated field references.
//
// Example:
//
// message Subscription {
// string topic = 2 [(google.api.resource_reference) = {
// type: "pubsub.googleapis.com/Topic"
// }];
// }
//
// Occasionally, a field may reference an arbitrary resource. In this case,
// APIs use the special value * in their resource reference.
//
// Example:
//
// message GetIamPolicyRequest {
// string resource = 2 [(google.api.resource_reference) = {
// type: "*"
// }];
// }
Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"`
// The resource type of a child collection that the annotated field
// references. This is useful for annotating the `parent` field that
// doesn't have a fixed resource type.
//
// Example:
//
// message ListLogEntriesRequest {
// string parent = 1 [(google.api.resource_reference) = {
// child_type: "logging.googleapis.com/LogEntry"
// };
// }
ChildType string `protobuf:"bytes,2,opt,name=child_type,json=childType,proto3" json:"child_type,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ResourceReference) Reset() {
*x = ResourceReference{}
mi := &file_google_api_resource_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ResourceReference) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ResourceReference) ProtoMessage() {}
func (x *ResourceReference) ProtoReflect() protoreflect.Message {
mi := &file_google_api_resource_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ResourceReference.ProtoReflect.Descriptor instead.
func (*ResourceReference) Descriptor() ([]byte, []int) {
return file_google_api_resource_proto_rawDescGZIP(), []int{1}
}
func (x *ResourceReference) GetType() string {
if x != nil {
return x.Type
}
return ""
}
func (x *ResourceReference) GetChildType() string {
if x != nil {
return x.ChildType
}
return ""
}
var file_google_api_resource_proto_extTypes = []protoimpl.ExtensionInfo{
{
ExtendedType: (*descriptorpb.FieldOptions)(nil),
ExtensionType: (*ResourceReference)(nil),
Field: 1055,
Name: "google.api.resource_reference",
Tag: "bytes,1055,opt,name=resource_reference",
Filename: "google/api/resource.proto",
},
{
ExtendedType: (*descriptorpb.FileOptions)(nil),
ExtensionType: ([]*ResourceDescriptor)(nil),
Field: 1053,
Name: "google.api.resource_definition",
Tag: "bytes,1053,rep,name=resource_definition",
Filename: "google/api/resource.proto",
},
{
ExtendedType: (*descriptorpb.MessageOptions)(nil),
ExtensionType: (*ResourceDescriptor)(nil),
Field: 1053,
Name: "google.api.resource",
Tag: "bytes,1053,opt,name=resource",
Filename: "google/api/resource.proto",
},
}
// Extension fields to descriptorpb.FieldOptions.
var (
// An annotation that describes a resource reference, see
// [ResourceReference][].
//
// optional google.api.ResourceReference resource_reference = 1055;
E_ResourceReference = &file_google_api_resource_proto_extTypes[0]
)
// Extension fields to descriptorpb.FileOptions.
var (
// An annotation that describes a resource definition without a corresponding
// message; see [ResourceDescriptor][].
//
// repeated google.api.ResourceDescriptor resource_definition = 1053;
E_ResourceDefinition = &file_google_api_resource_proto_extTypes[1]
)
// Extension fields to descriptorpb.MessageOptions.
var (
// An annotation that describes a resource definition, see
// [ResourceDescriptor][].
//
// optional google.api.ResourceDescriptor resource = 1053;
E_Resource = &file_google_api_resource_proto_extTypes[2]
)
var File_google_api_resource_proto protoreflect.FileDescriptor
const file_google_api_resource_proto_rawDesc = "" +
"\n" +
"\x19google/api/resource.proto\x12\n" +
"google.api\x1a google/protobuf/descriptor.proto\"\xaa\x03\n" +
"\x12ResourceDescriptor\x12\x12\n" +
"\x04type\x18\x01 \x01(\tR\x04type\x12\x18\n" +
"\apattern\x18\x02 \x03(\tR\apattern\x12\x1d\n" +
"\n" +
"name_field\x18\x03 \x01(\tR\tnameField\x12@\n" +
"\ahistory\x18\x04 \x01(\x0e2&.google.api.ResourceDescriptor.HistoryR\ahistory\x12\x16\n" +
"\x06plural\x18\x05 \x01(\tR\x06plural\x12\x1a\n" +
"\bsingular\x18\x06 \x01(\tR\bsingular\x12:\n" +
"\x05style\x18\n" +
" \x03(\x0e2$.google.api.ResourceDescriptor.StyleR\x05style\"[\n" +
"\aHistory\x12\x17\n" +
"\x13HISTORY_UNSPECIFIED\x10\x00\x12\x1d\n" +
"\x19ORIGINALLY_SINGLE_PATTERN\x10\x01\x12\x18\n" +
"\x14FUTURE_MULTI_PATTERN\x10\x02\"8\n" +
"\x05Style\x12\x15\n" +
"\x11STYLE_UNSPECIFIED\x10\x00\x12\x18\n" +
"\x14DECLARATIVE_FRIENDLY\x10\x01\"F\n" +
"\x11ResourceReference\x12\x12\n" +
"\x04type\x18\x01 \x01(\tR\x04type\x12\x1d\n" +
"\n" +
"child_type\x18\x02 \x01(\tR\tchildType:l\n" +
"\x12resource_reference\x12\x1d.google.protobuf.FieldOptions\x18\x9f\b \x01(\v2\x1d.google.api.ResourceReferenceR\x11resourceReference:n\n" +
"\x13resource_definition\x12\x1c.google.protobuf.FileOptions\x18\x9d\b \x03(\v2\x1e.google.api.ResourceDescriptorR\x12resourceDefinition:\\\n" +
"\bresource\x12\x1f.google.protobuf.MessageOptions\x18\x9d\b \x01(\v2\x1e.google.api.ResourceDescriptorR\bresourceB\xab\x01\n" +
"\x0ecom.google.apiB\rResourceProtoP\x01ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\xa2\x02\x03GAX\xaa\x02\n" +
"Google.Api\xca\x02\n" +
"Google\\Api\xe2\x02\x16Google\\Api\\GPBMetadata\xea\x02\vGoogle::Apib\x06proto3"
var (
file_google_api_resource_proto_rawDescOnce sync.Once
file_google_api_resource_proto_rawDescData []byte
)
func file_google_api_resource_proto_rawDescGZIP() []byte {
file_google_api_resource_proto_rawDescOnce.Do(func() {
file_google_api_resource_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_google_api_resource_proto_rawDesc), len(file_google_api_resource_proto_rawDesc)))
})
return file_google_api_resource_proto_rawDescData
}
var file_google_api_resource_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
var file_google_api_resource_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_google_api_resource_proto_goTypes = []any{
(ResourceDescriptor_History)(0), // 0: google.api.ResourceDescriptor.History
(ResourceDescriptor_Style)(0), // 1: google.api.ResourceDescriptor.Style
(*ResourceDescriptor)(nil), // 2: google.api.ResourceDescriptor
(*ResourceReference)(nil), // 3: google.api.ResourceReference
(*descriptorpb.FieldOptions)(nil), // 4: google.protobuf.FieldOptions
(*descriptorpb.FileOptions)(nil), // 5: google.protobuf.FileOptions
(*descriptorpb.MessageOptions)(nil), // 6: google.protobuf.MessageOptions
}
var file_google_api_resource_proto_depIdxs = []int32{
0, // 0: google.api.ResourceDescriptor.history:type_name -> google.api.ResourceDescriptor.History
1, // 1: google.api.ResourceDescriptor.style:type_name -> google.api.ResourceDescriptor.Style
4, // 2: google.api.resource_reference:extendee -> google.protobuf.FieldOptions
5, // 3: google.api.resource_definition:extendee -> google.protobuf.FileOptions
6, // 4: google.api.resource:extendee -> google.protobuf.MessageOptions
3, // 5: google.api.resource_reference:type_name -> google.api.ResourceReference
2, // 6: google.api.resource_definition:type_name -> google.api.ResourceDescriptor
2, // 7: google.api.resource:type_name -> google.api.ResourceDescriptor
8, // [8:8] is the sub-list for method output_type
8, // [8:8] is the sub-list for method input_type
5, // [5:8] is the sub-list for extension type_name
2, // [2:5] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
}
func init() { file_google_api_resource_proto_init() }
func file_google_api_resource_proto_init() {
if File_google_api_resource_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_google_api_resource_proto_rawDesc), len(file_google_api_resource_proto_rawDesc)),
NumEnums: 2,
NumMessages: 2,
NumExtensions: 3,
NumServices: 0,
},
GoTypes: file_google_api_resource_proto_goTypes,
DependencyIndexes: file_google_api_resource_proto_depIdxs,
EnumInfos: file_google_api_resource_proto_enumTypes,
MessageInfos: file_google_api_resource_proto_msgTypes,
ExtensionInfos: file_google_api_resource_proto_extTypes,
}.Build()
File_google_api_resource_proto = out.File
file_google_api_resource_proto_goTypes = nil
file_google_api_resource_proto_depIdxs = nil
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,197 @@
package auth
import (
"context"
"time"
"github.com/golang-jwt/jwt/v5"
"github.com/pkg/errors"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/usememos/memos/internal/util"
storepb "github.com/usememos/memos/proto/gen/store"
"github.com/usememos/memos/store"
)
// Authenticator provides shared authentication and authorization logic.
// Used by gRPC interceptor, Connect interceptor, and file server to ensure
// consistent authentication behavior across all API endpoints.
//
// Authentication methods:
// - Session cookie: Browser-based authentication with sliding expiration
// - JWT token: API token authentication for programmatic access
//
// This struct is safe for concurrent use.
type Authenticator struct {
store *store.Store
secret string
}
// NewAuthenticator creates a new Authenticator instance.
func NewAuthenticator(store *store.Store, secret string) *Authenticator {
return &Authenticator{
store: store,
secret: secret,
}
}
// AuthenticateBySession validates a session cookie and returns the authenticated user.
//
// Validation steps:
// 1. Parse cookie value to extract userID and sessionID
// 2. Verify user exists and is not archived
// 3. Verify session exists in user's sessions list
// 4. Check session hasn't expired (sliding expiration: 14 days from last access)
//
// Returns the user if authentication succeeds, or an error describing the failure.
func (a *Authenticator) AuthenticateBySession(ctx context.Context, sessionCookieValue string) (*store.User, error) {
if sessionCookieValue == "" {
return nil, errors.New("session cookie value not found")
}
userID, sessionID, err := ParseSessionCookieValue(sessionCookieValue)
if err != nil {
return nil, errors.Wrap(err, "invalid session cookie format")
}
user, err := a.store.GetUser(ctx, &store.FindUser{ID: &userID})
if err != nil {
return nil, errors.Wrap(err, "failed to get user")
}
if user == nil {
return nil, errors.New("user not found")
}
if user.RowStatus == store.Archived {
return nil, errors.New("user is archived")
}
sessions, err := a.store.GetUserSessions(ctx, user.ID)
if err != nil {
return nil, errors.Wrap(err, "failed to get user sessions")
}
if !validateSession(sessionID, sessions) {
return nil, errors.New("invalid or expired session")
}
return user, nil
}
// AuthenticateByJWT validates a JWT access token and returns the authenticated user.
//
// Validation steps:
// 1. Parse and verify JWT signature using server secret
// 2. Verify key ID matches expected version
// 3. Extract user ID from JWT claims (subject field)
// 4. Verify user exists and is not archived
// 5. Verify token exists in user's access_tokens list (for revocation support)
//
// Returns the user if authentication succeeds, or an error describing the failure.
func (a *Authenticator) AuthenticateByJWT(ctx context.Context, accessToken string) (*store.User, error) {
if accessToken == "" {
return nil, errors.New("access token not found")
}
claims := &ClaimsMessage{}
_, err := jwt.ParseWithClaims(accessToken, claims, func(t *jwt.Token) (any, error) {
if t.Method.Alg() != jwt.SigningMethodHS256.Name {
return nil, errors.Errorf("unexpected signing method: %v", t.Header["alg"])
}
kid, ok := t.Header["kid"].(string)
if !ok || kid != KeyID {
return nil, errors.Errorf("unexpected kid: %v", t.Header["kid"])
}
return []byte(a.secret), nil
})
if err != nil {
return nil, errors.New("invalid or expired access token")
}
userID, err := util.ConvertStringToInt32(claims.Subject)
if err != nil {
return nil, errors.Wrap(err, "malformed ID in token")
}
user, err := a.store.GetUser(ctx, &store.FindUser{ID: &userID})
if err != nil {
return nil, errors.Wrap(err, "failed to get user")
}
if user == nil {
return nil, errors.Errorf("user %d not found", userID)
}
if user.RowStatus == store.Archived {
return nil, errors.Errorf("user %d is archived", userID)
}
accessTokens, err := a.store.GetUserAccessTokens(ctx, user.ID)
if err != nil {
return nil, errors.Wrap(err, "failed to get user access tokens")
}
if !validateAccessToken(accessToken, accessTokens) {
return nil, errors.New("invalid access token")
}
return user, nil
}
// AuthorizeAndSetContext checks user authorization for the given procedure and sets context values.
//
// Authorization checks:
// - Admin-only methods require Host or Admin role (checked via isAdminOnly function)
//
// Context values set:
// - UserIDContextKey: Always set with the user's ID
// - SessionIDContextKey: Set if authenticated via session cookie
// - AccessTokenContextKey: Set if authenticated via JWT token
//
// Also updates session last accessed time for session-based auth (sliding expiration).
//
// Returns the updated context or an error if authorization fails.
func (a *Authenticator) AuthorizeAndSetContext(ctx context.Context, procedure string, user *store.User, sessionID, accessToken string, isAdminOnly func(string) bool) (context.Context, error) {
// Check admin-only method authorization
if isAdminOnly != nil && isAdminOnly(procedure) && user.Role != store.RoleHost && user.Role != store.RoleAdmin {
return nil, errors.Errorf("user %q is not authorized for this operation", user.Username)
}
// Set user ID in context (always)
ctx = context.WithValue(ctx, UserIDContextKey, user.ID)
// Set authentication method specific context values
if sessionID != "" {
ctx = context.WithValue(ctx, SessionIDContextKey, sessionID)
// Update session last accessed time for sliding expiration
_ = a.store.UpdateUserSessionLastAccessed(ctx, user.ID, sessionID, timestamppb.Now())
} else if accessToken != "" {
ctx = context.WithValue(ctx, AccessTokenContextKey, accessToken)
}
return ctx, nil
}
// validateSession checks if a session exists and is still valid.
// Uses sliding expiration: session is valid if last accessed within SessionSlidingDuration.
func validateSession(sessionID string, sessions []*storepb.SessionsUserSetting_Session) bool {
for _, session := range sessions {
if sessionID == session.SessionId {
if session.LastAccessedTime != nil {
expiration := session.LastAccessedTime.AsTime().Add(SessionSlidingDuration)
if expiration.Before(time.Now()) {
return false // Session expired
}
}
return true
}
}
return false // Session not found
}
// validateAccessToken checks if the token exists in the user's access tokens list.
// This enables token revocation: deleted tokens are removed from the list.
func validateAccessToken(token string, tokens []*storepb.AccessTokensUserSetting_AccessToken) bool {
for _, t := range tokens {
if token == t.AccessToken {
return true
}
}
return false
}

49
server/auth/context.go Normal file
View File

@ -0,0 +1,49 @@
package auth
import "context"
// ContextKey is the key type for context values.
// Using a custom type prevents collisions with other packages.
type ContextKey int
const (
// UserIDContextKey stores the authenticated user's ID.
// Set for both session-based and token-based authentication.
// Use GetUserID(ctx) to retrieve this value.
UserIDContextKey ContextKey = iota
// SessionIDContextKey stores the session ID for session-based auth.
// Only set when authenticated via session cookie.
SessionIDContextKey
// AccessTokenContextKey stores the JWT token for token-based auth.
// Only set when authenticated via Bearer token.
AccessTokenContextKey
)
// GetUserID retrieves the authenticated user's ID from the context.
// Returns 0 if no user ID is set (unauthenticated request).
func GetUserID(ctx context.Context) int32 {
if v, ok := ctx.Value(UserIDContextKey).(int32); ok {
return v
}
return 0
}
// GetSessionID retrieves the session ID from the context.
// Returns empty string if not authenticated via session cookie.
func GetSessionID(ctx context.Context) string {
if v, ok := ctx.Value(SessionIDContextKey).(string); ok {
return v
}
return ""
}
// GetAccessToken retrieves the JWT access token from the context.
// Returns empty string if not authenticated via bearer token.
func GetAccessToken(ctx context.Context) string {
if v, ok := ctx.Value(AccessTokenContextKey).(string); ok {
return v
}
return ""
}

35
server/auth/extract.go Normal file
View File

@ -0,0 +1,35 @@
package auth
import (
"net/http"
"strings"
)
// ExtractSessionCookieFromHeader extracts the session cookie value from an HTTP Cookie header.
// Returns empty string if the session cookie is not found.
func ExtractSessionCookieFromHeader(cookieHeader string) string {
if cookieHeader == "" {
return ""
}
// Use http.Request to parse cookies properly
req := &http.Request{Header: http.Header{"Cookie": []string{cookieHeader}}}
cookie, err := req.Cookie(SessionCookieName)
if err != nil {
return ""
}
return cookie.Value
}
// ExtractBearerToken extracts the JWT token from an Authorization header value.
// Expected format: "Bearer {token}"
// Returns empty string if no valid bearer token is found.
func ExtractBearerToken(authHeader string) string {
if authHeader == "" {
return ""
}
parts := strings.Fields(authHeader)
if len(parts) != 2 || !strings.EqualFold(parts[0], "bearer") {
return ""
}
return parts[1]
}

View File

@ -1,4 +1,13 @@
package v1
// Package auth provides authentication and authorization for the Memos server.
//
// This package is used by:
// - server/router/api/v1: gRPC and Connect API interceptors
// - server/router/fileserver: HTTP file server authentication
//
// Authentication methods supported:
// - Session cookie: Browser-based authentication with sliding expiration
// - JWT token: API token authentication for programmatic access
package auth
import (
"fmt"
@ -99,8 +108,8 @@ func generateToken(username string, userID int32, audience string, expirationTim
//
// Uses UUID v4 (random) for high entropy and uniqueness.
// Session IDs are stored in user settings and used to identify browser sessions.
func GenerateSessionID() (string, error) {
return util.GenUUID(), nil
func GenerateSessionID() string {
return util.GenUUID()
}
// BuildSessionCookieValue creates the session cookie value.

View File

@ -1,315 +1,125 @@
package v1
// gRPC Authentication Interceptor
//
// This file implements the authentication interceptor for gRPC requests.
// It extracts credentials from gRPC metadata and delegates to the shared Authenticator.
//
// Authentication flow:
// 1. Extract session cookie or bearer token from metadata
// 2. Validate credentials using Authenticator
// 3. Check authorization (admin-only methods)
// 4. Set user context and proceed with request
//
// For public methods (defined in acl_config.go), authentication is skipped.
import (
"context"
"net/http"
"strings"
"time"
"github.com/golang-jwt/jwt/v5"
"github.com/pkg/errors"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/usememos/memos/internal/util"
storepb "github.com/usememos/memos/proto/gen/store"
"github.com/usememos/memos/server/auth"
"github.com/usememos/memos/store"
)
// ContextKey is the key type of context value.
type ContextKey int
const (
// UserIDContextKey stores the authenticated user's ID in the context.
// Set for both session-based and token-based authentication.
UserIDContextKey ContextKey = iota
// sessionIDContextKey stores the session ID in the context.
// Only set for session-based authentication (cookie auth).
sessionIDContextKey
// accessTokenContextKey stores the JWT access token in the context.
// Only set for token-based authentication (Bearer token).
accessTokenContextKey
)
// GRPCAuthInterceptor is the auth interceptor for gRPC server.
// GRPCAuthInterceptor is the authentication interceptor for gRPC server.
// It validates incoming requests and sets user context for authenticated requests.
type GRPCAuthInterceptor struct {
Store *store.Store
secret string
authenticator *auth.Authenticator
}
// NewGRPCAuthInterceptor returns a new API auth interceptor.
// NewGRPCAuthInterceptor creates a new gRPC authentication interceptor.
func NewGRPCAuthInterceptor(store *store.Store, secret string) *GRPCAuthInterceptor {
return &GRPCAuthInterceptor{
Store: store,
secret: secret,
authenticator: auth.NewAuthenticator(store, secret),
}
}
// AuthenticationInterceptor is the unary interceptor for gRPC API.
//
// Authentication Strategy (in priority order):
// 1. Session Cookie: Check for "user_session" cookie with format "{userID}-{sessionID}"
// 2. Access Token: Check for "Authorization: Bearer {token}" header with JWT
// 3. Public Endpoints: Allow if method is in public allowlist
// 4. Reject: Return 401 Unauthenticated if none of the above succeed
// Authentication strategy (in priority order):
// 1. Session Cookie: "user_session" cookie with format "{userID}-{sessionID}"
// 2. Bearer Token: "Authorization: Bearer {jwt_token}" header
// 3. Public Methods: Allow without auth if method is in public allowlist
// 4. Reject: Return Unauthenticated error
//
// On successful authentication, sets context values:
// - UserIDContextKey: The authenticated user's ID (always set)
// - sessionIDContextKey: Session ID (only for cookie auth)
// - accessTokenContextKey: JWT token (only for Bearer token auth).
// On successful authentication, context values are set:
// - auth.UserIDContextKey: The authenticated user's ID
// - auth.SessionIDContextKey: Session ID (cookie auth only)
// - auth.AccessTokenContextKey: JWT token (bearer auth only).
func (in *GRPCAuthInterceptor) AuthenticationInterceptor(ctx context.Context, request any, serverInfo *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return nil, status.Errorf(codes.Unauthenticated, "failed to parse metadata from incoming context")
}
// Authentication Method 1: Session-based authentication (Cookie)
// Format: Cookie: user_session={userID}-{sessionID}
// Used by: Web browsers
if sessionCookieValue, err := getSessionIDFromMetadata(md); err == nil && sessionCookieValue != "" {
user, err := in.authenticateBySession(ctx, sessionCookieValue)
// Try session cookie authentication
if sessionCookie := extractSessionCookieFromMetadata(md); sessionCookie != "" {
user, err := in.authenticator.AuthenticateBySession(ctx, sessionCookie)
if err == nil && user != nil {
// Extract just the sessionID part for context storage
_, sessionID, parseErr := ParseSessionCookieValue(sessionCookieValue)
if parseErr != nil {
return nil, status.Errorf(codes.Internal, "failed to parse session cookie: %v", parseErr)
_, sessionID, _ := auth.ParseSessionCookieValue(sessionCookie)
ctx, err = in.authenticator.AuthorizeAndSetContext(ctx, serverInfo.FullMethod, user, sessionID, "", IsAdminOnlyMethod)
if err != nil {
return nil, toGRPCError(err, codes.PermissionDenied)
}
return in.handleAuthenticatedRequest(ctx, request, serverInfo, handler, user, sessionID, "")
return handler(ctx, request)
}
}
// Authentication Method 2: Token-based authentication (JWT)
// Format: Authorization: Bearer {jwt_token}
// Used by: Mobile apps, CLI tools, API clients
if accessToken, err := getAccessTokenFromMetadata(md); err == nil && accessToken != "" {
user, err := in.authenticateByJWT(ctx, accessToken)
// Try bearer token authentication
if token := extractBearerTokenFromMetadata(md); token != "" {
user, err := in.authenticator.AuthenticateByJWT(ctx, token)
if err == nil && user != nil {
return in.handleAuthenticatedRequest(ctx, request, serverInfo, handler, user, "", accessToken)
ctx, err = in.authenticator.AuthorizeAndSetContext(ctx, serverInfo.FullMethod, user, "", token, IsAdminOnlyMethod)
if err != nil {
return nil, toGRPCError(err, codes.PermissionDenied)
}
return handler(ctx, request)
}
}
// Authentication Method 3: Public endpoints
// Some endpoints don't require authentication (e.g., login, signup)
// Check if this method is in the allowlist
if isUnauthorizeAllowedMethod(serverInfo.FullMethod) {
// Allow public methods without authentication
if IsPublicMethod(serverInfo.FullMethod) {
return handler(ctx, request)
}
// If authentication is required but not found, reject the request
return nil, status.Errorf(codes.Unauthenticated, "authentication required")
}
// handleAuthenticatedRequest processes an authenticated request with the given user and auth info.
func (in *GRPCAuthInterceptor) handleAuthenticatedRequest(ctx context.Context, request any, serverInfo *grpc.UnaryServerInfo, handler grpc.UnaryHandler, user *store.User, sessionID, accessToken string) (any, error) {
// Check user status
if user.RowStatus == store.Archived {
return nil, errors.Errorf("user %q is archived", user.Username)
// toGRPCError converts an error to a gRPC status error with the given code.
// If the error is already a gRPC status error, it is returned as-is.
func toGRPCError(err error, code codes.Code) error {
if err == nil {
return nil
}
if isOnlyForAdminAllowedMethod(serverInfo.FullMethod) && user.Role != store.RoleHost && user.Role != store.RoleAdmin {
return nil, errors.Errorf("user %q is not admin", user.Username)
if _, ok := status.FromError(err); ok {
return err
}
// Set context values
ctx = context.WithValue(ctx, UserIDContextKey, user.ID)
if sessionID != "" {
// Session-based authentication
ctx = context.WithValue(ctx, sessionIDContextKey, sessionID)
// Update session last accessed time
_ = in.updateSessionLastAccessed(ctx, user.ID, sessionID)
} else if accessToken != "" {
// JWT access token-based authentication
ctx = context.WithValue(ctx, accessTokenContextKey, accessToken)
}
return handler(ctx, request)
return status.Errorf(code, "%v", err)
}
// authenticateByJWT authenticates a user using JWT access token from Authorization header.
//
// Validation steps:
// 1. Parse and verify JWT signature using server secret
// 2. Extract user ID from JWT claims (subject field)
// 3. Verify user exists and is not archived
// 4. Verify token exists in user's access_tokens list (for revocation support)
//
// Returns the authenticated user or an error.
func (in *GRPCAuthInterceptor) authenticateByJWT(ctx context.Context, accessToken string) (*store.User, error) {
if accessToken == "" {
return nil, status.Errorf(codes.Unauthenticated, "access token not found")
}
claims := &ClaimsMessage{}
_, err := jwt.ParseWithClaims(accessToken, claims, func(t *jwt.Token) (any, error) {
if t.Method.Alg() != jwt.SigningMethodHS256.Name {
return nil, status.Errorf(codes.Unauthenticated, "unexpected access token signing method=%v, expect %v", t.Header["alg"], jwt.SigningMethodHS256)
}
if kid, ok := t.Header["kid"].(string); ok {
if kid == "v1" {
return []byte(in.secret), nil
}
}
return nil, status.Errorf(codes.Unauthenticated, "unexpected access token kid=%v", t.Header["kid"])
})
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "Invalid or expired access token")
}
// Get user from JWT claims
userID, err := util.ConvertStringToInt32(claims.Subject)
if err != nil {
return nil, errors.Wrap(err, "malformed ID in the token")
}
user, err := in.Store.GetUser(ctx, &store.FindUser{
ID: &userID,
})
if err != nil {
return nil, errors.Wrap(err, "failed to get user")
}
if user == nil {
return nil, errors.Errorf("user %q not exists", userID)
}
if user.RowStatus == store.Archived {
return nil, errors.Errorf("user %q is archived", userID)
}
// Validate that this access token exists in the user's access tokens
accessTokens, err := in.Store.GetUserAccessTokens(ctx, user.ID)
if err != nil {
return nil, errors.Wrapf(err, "failed to get user access tokens")
}
if !validateAccessToken(accessToken, accessTokens) {
return nil, status.Errorf(codes.Unauthenticated, "invalid access token")
}
return user, nil
}
// authenticateBySession authenticates a user using session ID from cookie.
//
// Validation steps:
// 1. Parse cookie value to extract userID and sessionID
// 2. Verify user exists and is not archived
// 3. Verify session exists in user's sessions list
// 4. Check session hasn't expired (sliding expiration: 14 days from last access)
//
// Returns the authenticated user or an error.
func (in *GRPCAuthInterceptor) authenticateBySession(ctx context.Context, sessionCookieValue string) (*store.User, error) {
if sessionCookieValue == "" {
return nil, status.Errorf(codes.Unauthenticated, "session cookie value not found")
}
// Parse the cookie value to extract userID and sessionID
userID, sessionID, err := ParseSessionCookieValue(sessionCookieValue)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "invalid session cookie format: %v", err)
}
// Get the user directly using the userID from the cookie
user, err := in.Store.GetUser(ctx, &store.FindUser{
ID: &userID,
})
if err != nil {
return nil, errors.Wrap(err, "failed to get user")
}
if user == nil {
return nil, status.Errorf(codes.Unauthenticated, "user not found")
}
if user.RowStatus == store.Archived {
return nil, status.Errorf(codes.Unauthenticated, "user is archived")
}
// Get user sessions and validate the sessionID
sessions, err := in.Store.GetUserSessions(ctx, user.ID)
if err != nil {
return nil, errors.Wrap(err, "failed to get user sessions")
}
if !validateUserSession(sessionID, sessions) {
return nil, status.Errorf(codes.Unauthenticated, "invalid or expired session")
}
return user, nil
}
// updateSessionLastAccessed updates the last accessed time for a user session.
func (in *GRPCAuthInterceptor) updateSessionLastAccessed(ctx context.Context, userID int32, sessionID string) error {
return in.Store.UpdateUserSessionLastAccessed(ctx, userID, sessionID, timestamppb.Now())
}
// validateUserSession checks if a session exists and is still valid using sliding expiration.
//
// Sliding expiration logic:
// - Session is valid if: last_accessed_time + 14 days > current_time
// - Each API call updates last_accessed_time, extending the session
// - This provides better UX than fixed expiration (users stay logged in while active).
func validateUserSession(sessionID string, userSessions []*storepb.SessionsUserSetting_Session) bool {
for _, session := range userSessions {
if sessionID == session.SessionId {
// Use sliding expiration: check if last_accessed_time + 2 weeks > current_time
if session.LastAccessedTime != nil {
expirationTime := session.LastAccessedTime.AsTime().Add(SessionSlidingDuration)
if expirationTime.Before(time.Now()) {
return false
}
}
return true
// extractSessionCookieFromMetadata extracts the session cookie value from gRPC metadata.
// Checks both "grpcgateway-cookie" (from gRPC-Gateway) and "cookie" (native gRPC).
// Returns empty string if no session cookie is found.
func extractSessionCookieFromMetadata(md metadata.MD) string {
// gRPC-Gateway puts cookies in "grpcgateway-cookie", native gRPC uses "cookie"
for _, cookieHeader := range append(md.Get("grpcgateway-cookie"), md.Get("cookie")...) {
if cookie := auth.ExtractSessionCookieFromHeader(cookieHeader); cookie != "" {
return cookie
}
}
return false
return ""
}
// getSessionIDFromMetadata extracts session cookie value from metadata.
//
// Checks both "grpcgateway-cookie" (set by gRPC-Gateway) and "cookie" (set by native gRPC).
// Cookie format: user_session={userID}-{sessionID}.
func getSessionIDFromMetadata(md metadata.MD) (string, error) {
// Check the cookie header for session cookie value
var sessionCookieValue string
for _, t := range append(md.Get("grpcgateway-cookie"), md.Get("cookie")...) {
header := http.Header{}
header.Add("Cookie", t)
request := http.Request{Header: header}
if v, _ := request.Cookie(SessionCookieName); v != nil {
sessionCookieValue = v.Value
}
// extractBearerTokenFromMetadata extracts JWT token from Authorization header in gRPC metadata.
// Returns empty string if no valid bearer token is found.
func extractBearerTokenFromMetadata(md metadata.MD) string {
authHeaders := md.Get("Authorization")
if len(authHeaders) == 0 {
return ""
}
if sessionCookieValue == "" {
return "", errors.New("session cookie not found")
}
return sessionCookieValue, nil
}
// getAccessTokenFromMetadata extracts JWT access token from Authorization header.
//
// Expected header format: Authorization: Bearer {jwt_token}
// This follows the OAuth 2.0 Bearer token specification (RFC 6750).
func getAccessTokenFromMetadata(md metadata.MD) (string, error) {
// Check the HTTP request Authorization header.
authorizationHeaders := md.Get("Authorization")
if len(authorizationHeaders) == 0 {
return "", errors.New("authorization header not found")
}
authHeaderParts := strings.Fields(authorizationHeaders[0])
if len(authHeaderParts) != 2 || strings.ToLower(authHeaderParts[0]) != "bearer" {
return "", errors.New("authorization header format must be Bearer {token}")
}
return authHeaderParts[1], nil
}
// validateAccessToken checks if the provided JWT token exists in the user's access tokens list.
//
// This enables token revocation: when a user deletes a token from their settings,
// it's removed from this list and subsequent API calls with that token will fail.
func validateAccessToken(accessTokenString string, userAccessTokens []*storepb.AccessTokensUserSetting_AccessToken) bool {
for _, userAccessToken := range userAccessTokens {
if accessTokenString == userAccessToken.AccessToken {
return true
}
}
return false
return auth.ExtractBearerToken(authHeaders[0])
}

View File

@ -1,33 +1,56 @@
package v1
var authenticationAllowlistMethods = map[string]bool{
"/memos.api.v1.InstanceService/GetInstanceProfile": true,
"/memos.api.v1.InstanceService/GetInstanceSetting": true,
// Access Control List (ACL) Configuration
//
// This file defines which API methods require authentication and which require admin privileges.
// Used by both gRPC and Connect interceptors to enforce access control.
//
// Method names follow the gRPC full method format: "/{package}.{service}/{method}"
// Example: "/memos.api.v1.MemoService/CreateMemo"
// publicMethods lists methods that can be called without authentication.
// These are typically read-only endpoints for public content or login-related endpoints.
var publicMethods = map[string]bool{
// Instance info - needed before login
"/memos.api.v1.InstanceService/GetInstanceProfile": true,
"/memos.api.v1.InstanceService/GetInstanceSetting": true,
// Auth - login/session endpoints
"/memos.api.v1.AuthService/CreateSession": true,
"/memos.api.v1.AuthService/GetCurrentSession": true,
// User - public user info and registration
"/memos.api.v1.UserService/CreateUser": true, // Registration (also admin-only when not first user)
"/memos.api.v1.UserService/GetUser": true,
"/memos.api.v1.UserService/GetUserAvatar": true,
"/memos.api.v1.UserService/GetUserStats": true,
"/memos.api.v1.UserService/ListAllUserStats": true,
"/memos.api.v1.UserService/SearchUsers": true,
// Identity providers - needed for SSO login
"/memos.api.v1.IdentityProviderService/ListIdentityProviders": true,
"/memos.api.v1.AuthService/CreateSession": true,
"/memos.api.v1.AuthService/GetCurrentSession": true,
"/memos.api.v1.UserService/CreateUser": true,
"/memos.api.v1.UserService/GetUser": true,
"/memos.api.v1.UserService/GetUserAvatar": true,
"/memos.api.v1.UserService/GetUserStats": true,
"/memos.api.v1.UserService/ListAllUserStats": true,
"/memos.api.v1.UserService/SearchUsers": true,
"/memos.api.v1.MemoService/GetMemo": true,
"/memos.api.v1.MemoService/ListMemos": true,
"/memos.api.v1.AttachmentService/GetAttachmentBinary": true,
// Memo - public memo access
"/memos.api.v1.MemoService/GetMemo": true,
"/memos.api.v1.MemoService/ListMemos": true,
// Attachment - public attachment access
"/memos.api.v1.AttachmentService/GetAttachmentBinary": true,
}
// isUnauthorizeAllowedMethod returns whether the method is exempted from authentication.
func isUnauthorizeAllowedMethod(fullMethodName string) bool {
return authenticationAllowlistMethods[fullMethodName]
}
var allowedMethodsOnlyForAdmin = map[string]bool{
"/memos.api.v1.UserService/CreateUser": true,
// adminOnlyMethods lists methods that require admin (Host or Admin role) privileges.
// Regular users cannot call these methods even if authenticated.
var adminOnlyMethods = map[string]bool{
"/memos.api.v1.UserService/CreateUser": true, // Admin creates users (except first user registration)
"/memos.api.v1.InstanceService/UpdateInstanceSetting": true,
}
// isOnlyForAdminAllowedMethod returns true if the method is allowed to be called only by admin.
func isOnlyForAdminAllowedMethod(methodName string) bool {
return allowedMethodsOnlyForAdmin[methodName]
// IsPublicMethod returns true if the method can be called without authentication.
func IsPublicMethod(fullMethodName string) bool {
return publicMethods[fullMethodName]
}
// IsAdminOnlyMethod returns true if the method requires admin privileges.
func IsAdminOnlyMethod(fullMethodName string) bool {
return adminOnlyMethods[fullMethodName]
}

View File

@ -22,6 +22,7 @@ import (
"github.com/usememos/memos/plugin/idp/oauth2"
v1pb "github.com/usememos/memos/proto/gen/api/v1"
storepb "github.com/usememos/memos/proto/gen/store"
"github.com/usememos/memos/server/auth"
"github.com/usememos/memos/store"
)
@ -53,7 +54,7 @@ func (s *APIV1Service) GetCurrentSession(ctx context.Context, _ *v1pb.GetCurrent
var lastAccessedAt *timestamppb.Timestamp
// Update session last accessed time if we have a session ID and get the current session info
if sessionID, ok := ctx.Value(sessionIDContextKey).(string); ok && sessionID != "" {
if sessionID := auth.GetSessionID(ctx); sessionID != "" {
now := timestamppb.Now()
if err := s.Store.UpdateUserSessionLastAccessed(ctx, user.ID, sessionID, now); err != nil {
// Log error but don't fail the request
@ -221,10 +222,7 @@ func (s *APIV1Service) CreateSession(ctx context.Context, request *v1pb.CreateSe
// sliding expiration (14 days from last access) checked during authentication.
func (s *APIV1Service) doSignIn(ctx context.Context, user *store.User, expireTime time.Time) error {
// Generate unique session ID for web use
sessionID, err := GenerateSessionID()
if err != nil {
return status.Errorf(codes.Internal, "failed to generate session ID, error: %v", err)
}
sessionID := auth.GenerateSessionID()
// Track session in user settings
if err := s.trackUserSession(ctx, user.ID, sessionID); err != nil {
@ -234,7 +232,7 @@ func (s *APIV1Service) doSignIn(ctx context.Context, user *store.User, expireTim
}
// Set session cookie for web use (format: userID-sessionID)
sessionCookieValue := BuildSessionCookieValue(user.ID, sessionID)
sessionCookieValue := auth.BuildSessionCookieValue(user.ID, sessionID)
sessionCookie, err := s.buildSessionCookie(ctx, sessionCookieValue, expireTime)
if err != nil {
return status.Errorf(codes.Internal, "failed to build session cookie, error: %v", err)
@ -266,7 +264,7 @@ func (s *APIV1Service) DeleteSession(ctx context.Context, _ *v1pb.DeleteSessionR
}
// Check if we have a session ID (from cookie-based auth)
if sessionID, ok := ctx.Value(sessionIDContextKey).(string); ok && sessionID != "" {
if sessionID := auth.GetSessionID(ctx); sessionID != "" {
// Remove session from user settings
if err := s.Store.RemoveUserSession(ctx, user.ID, sessionID); err != nil {
slog.Error("failed to remove user session", "error", err)
@ -297,7 +295,7 @@ func (s *APIV1Service) clearAuthCookies(ctx context.Context) error {
func (*APIV1Service) buildSessionCookie(ctx context.Context, sessionCookieValue string, expireTime time.Time) (string, error) {
attrs := []string{
fmt.Sprintf("%s=%s", SessionCookieName, sessionCookieValue),
fmt.Sprintf("%s=%s", auth.SessionCookieName, sessionCookieValue),
"Path=/",
"HttpOnly",
}
@ -326,8 +324,8 @@ func (*APIV1Service) buildSessionCookie(ctx context.Context, sessionCookieValue
}
func (s *APIV1Service) GetCurrentUser(ctx context.Context) (*store.User, error) {
userID, ok := ctx.Value(UserIDContextKey).(int32)
if !ok {
userID := auth.GetUserID(ctx)
if userID == 0 {
return nil, nil
}
user, err := s.Store.GetUser(ctx, &store.FindUser{

View File

@ -0,0 +1,80 @@
package v1
import (
"net/http"
"connectrpc.com/connect"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/usememos/memos/proto/gen/api/v1/apiv1connect"
)
// ConnectServiceHandler wraps APIV1Service to implement Connect handler interfaces.
// It adapts the existing gRPC service implementations to work with Connect's
// request/response wrapper types.
//
// This wrapper pattern allows us to:
// - Reuse existing gRPC service implementations
// - Support both native gRPC and Connect protocols
// - Maintain a single source of truth for business logic.
type ConnectServiceHandler struct {
*APIV1Service
}
// NewConnectServiceHandler creates a new Connect service handler.
func NewConnectServiceHandler(svc *APIV1Service) *ConnectServiceHandler {
return &ConnectServiceHandler{APIV1Service: svc}
}
// RegisterConnectHandlers registers all Connect service handlers on the given mux.
func (s *ConnectServiceHandler) RegisterConnectHandlers(mux *http.ServeMux, opts ...connect.HandlerOption) {
// Register all service handlers
handlers := []struct {
path string
handler http.Handler
}{
wrap(apiv1connect.NewInstanceServiceHandler(s, opts...)),
wrap(apiv1connect.NewAuthServiceHandler(s, opts...)),
wrap(apiv1connect.NewUserServiceHandler(s, opts...)),
wrap(apiv1connect.NewMemoServiceHandler(s, opts...)),
wrap(apiv1connect.NewAttachmentServiceHandler(s, opts...)),
wrap(apiv1connect.NewShortcutServiceHandler(s, opts...)),
wrap(apiv1connect.NewActivityServiceHandler(s, opts...)),
wrap(apiv1connect.NewIdentityProviderServiceHandler(s, opts...)),
}
for _, h := range handlers {
mux.Handle(h.path, h.handler)
}
}
// wrap converts (path, handler) return value to a struct for cleaner iteration.
func wrap(path string, handler http.Handler) struct {
path string
handler http.Handler
} {
return struct {
path string
handler http.Handler
}{path, handler}
}
// convertGRPCError converts gRPC status errors to Connect errors.
// This preserves the error code semantics between the two protocols.
func convertGRPCError(err error) error {
if err == nil {
return nil
}
if st, ok := status.FromError(err); ok {
return connect.NewError(grpcCodeToConnectCode(st.Code()), err)
}
return connect.NewError(connect.CodeInternal, err)
}
// grpcCodeToConnectCode converts gRPC status codes to Connect error codes.
// gRPC and Connect use the same error code semantics, so this is a direct cast.
// See: https://connectrpc.com/docs/protocol/#error-codes
func grpcCodeToConnectCode(code codes.Code) connect.Code {
return connect.Code(code)
}

View File

@ -0,0 +1,203 @@
package v1
import (
"context"
"fmt"
"log/slog"
"runtime/debug"
"connectrpc.com/connect"
"github.com/pkg/errors"
"github.com/usememos/memos/server/auth"
"github.com/usememos/memos/store"
)
// LoggingInterceptor logs Connect RPC requests with appropriate log levels.
//
// Log levels:
// - INFO: Successful requests and expected client errors (not found, permission denied, etc.)
// - ERROR: Server errors (internal, unavailable, etc.)
type LoggingInterceptor struct {
logStacktrace bool
}
// NewLoggingInterceptor creates a new logging interceptor.
func NewLoggingInterceptor(logStacktrace bool) *LoggingInterceptor {
return &LoggingInterceptor{logStacktrace: logStacktrace}
}
func (in *LoggingInterceptor) WrapUnary(next connect.UnaryFunc) connect.UnaryFunc {
return func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) {
resp, err := next(ctx, req)
in.log(req.Spec().Procedure, err)
return resp, err
}
}
func (*LoggingInterceptor) WrapStreamingClient(next connect.StreamingClientFunc) connect.StreamingClientFunc {
return next // No-op for server-side interceptor
}
func (*LoggingInterceptor) WrapStreamingHandler(next connect.StreamingHandlerFunc) connect.StreamingHandlerFunc {
return next // Streaming not used in this service
}
func (in *LoggingInterceptor) log(procedure string, err error) {
level, msg := in.classifyError(err)
attrs := []slog.Attr{slog.String("method", procedure)}
if err != nil {
attrs = append(attrs, slog.String("error", err.Error()))
if in.logStacktrace {
attrs = append(attrs, slog.String("stacktrace", fmt.Sprintf("%+v", err)))
}
}
slog.LogAttrs(context.Background(), level, msg, attrs...)
}
func (*LoggingInterceptor) classifyError(err error) (slog.Level, string) {
if err == nil {
return slog.LevelInfo, "OK"
}
var connectErr *connect.Error
if !errors.As(err, &connectErr) {
return slog.LevelError, "unknown error"
}
// Client errors (expected, log at INFO)
switch connectErr.Code() {
case connect.CodeCanceled,
connect.CodeInvalidArgument,
connect.CodeNotFound,
connect.CodeAlreadyExists,
connect.CodePermissionDenied,
connect.CodeUnauthenticated,
connect.CodeResourceExhausted,
connect.CodeFailedPrecondition,
connect.CodeAborted,
connect.CodeOutOfRange:
return slog.LevelInfo, "client error"
default:
// Server errors
return slog.LevelError, "server error"
}
}
// RecoveryInterceptor recovers from panics in Connect handlers and returns an internal error.
type RecoveryInterceptor struct {
logStacktrace bool
}
// NewRecoveryInterceptor creates a new recovery interceptor.
func NewRecoveryInterceptor(logStacktrace bool) *RecoveryInterceptor {
return &RecoveryInterceptor{logStacktrace: logStacktrace}
}
func (in *RecoveryInterceptor) WrapUnary(next connect.UnaryFunc) connect.UnaryFunc {
return func(ctx context.Context, req connect.AnyRequest) (resp connect.AnyResponse, err error) {
defer func() {
if r := recover(); r != nil {
in.logPanic(req.Spec().Procedure, r)
err = connect.NewError(connect.CodeInternal, errors.New("internal server error"))
}
}()
return next(ctx, req)
}
}
func (*RecoveryInterceptor) WrapStreamingClient(next connect.StreamingClientFunc) connect.StreamingClientFunc {
return next
}
func (*RecoveryInterceptor) WrapStreamingHandler(next connect.StreamingHandlerFunc) connect.StreamingHandlerFunc {
return next
}
func (in *RecoveryInterceptor) logPanic(procedure string, panicValue any) {
attrs := []slog.Attr{
slog.String("method", procedure),
slog.Any("panic", panicValue),
}
if in.logStacktrace {
attrs = append(attrs, slog.String("stacktrace", string(debug.Stack())))
}
slog.LogAttrs(context.Background(), slog.LevelError, "panic recovered in Connect handler", attrs...)
}
// AuthInterceptor handles authentication for Connect handlers.
//
// It reuses the same authentication logic as GRPCAuthInterceptor by delegating
// to a shared Authenticator instance. This ensures consistent authentication
// behavior across both gRPC and Connect protocols.
type AuthInterceptor struct {
authenticator *auth.Authenticator
}
// NewAuthInterceptor creates a new auth interceptor.
func NewAuthInterceptor(store *store.Store, secret string) *AuthInterceptor {
return &AuthInterceptor{
authenticator: auth.NewAuthenticator(store, secret),
}
}
func (in *AuthInterceptor) WrapUnary(next connect.UnaryFunc) connect.UnaryFunc {
return func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) {
procedure := req.Spec().Procedure
header := req.Header()
// Try session cookie authentication first
if sessionCookie := auth.ExtractSessionCookieFromHeader(header.Get("Cookie")); sessionCookie != "" {
user, err := in.authenticator.AuthenticateBySession(ctx, sessionCookie)
if err == nil && user != nil {
_, sessionID, _ := auth.ParseSessionCookieValue(sessionCookie)
ctx, err = in.authenticator.AuthorizeAndSetContext(ctx, procedure, user, sessionID, "", IsAdminOnlyMethod)
if err != nil {
return nil, convertAuthError(err)
}
return next(ctx, req)
}
}
// Try JWT token authentication
if accessToken := auth.ExtractBearerToken(header.Get("Authorization")); accessToken != "" {
user, err := in.authenticator.AuthenticateByJWT(ctx, accessToken)
if err == nil && user != nil {
ctx, err = in.authenticator.AuthorizeAndSetContext(ctx, procedure, user, "", accessToken, IsAdminOnlyMethod)
if err != nil {
return nil, convertAuthError(err)
}
return next(ctx, req)
}
}
// Allow public methods without authentication
if IsPublicMethod(procedure) {
return next(ctx, req)
}
return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("authentication required"))
}
}
func (*AuthInterceptor) WrapStreamingClient(next connect.StreamingClientFunc) connect.StreamingClientFunc {
return next
}
func (*AuthInterceptor) WrapStreamingHandler(next connect.StreamingHandlerFunc) connect.StreamingHandlerFunc {
return next
}
// convertAuthError converts authentication/authorization errors to Connect errors.
func convertAuthError(err error) error {
if err == nil {
return nil
}
// Check if it's already a Connect error
var connectErr *connect.Error
if errors.As(err, &connectErr) {
return err
}
// Default to permission denied for auth errors
return connect.NewError(connect.CodePermissionDenied, err)
}

View File

@ -0,0 +1,502 @@
package v1
import (
"context"
"connectrpc.com/connect"
"google.golang.org/protobuf/types/known/emptypb"
v1pb "github.com/usememos/memos/proto/gen/api/v1"
)
// This file contains all Connect service handler method implementations.
// Each method delegates to the underlying gRPC service implementation,
// converting between Connect and gRPC request/response types.
// InstanceService
func (s *ConnectServiceHandler) GetInstanceProfile(ctx context.Context, req *connect.Request[v1pb.GetInstanceProfileRequest]) (*connect.Response[v1pb.InstanceProfile], error) {
resp, err := s.APIV1Service.GetInstanceProfile(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) GetInstanceSetting(ctx context.Context, req *connect.Request[v1pb.GetInstanceSettingRequest]) (*connect.Response[v1pb.InstanceSetting], error) {
resp, err := s.APIV1Service.GetInstanceSetting(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) UpdateInstanceSetting(ctx context.Context, req *connect.Request[v1pb.UpdateInstanceSettingRequest]) (*connect.Response[v1pb.InstanceSetting], error) {
resp, err := s.APIV1Service.UpdateInstanceSetting(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
// AuthService
func (s *ConnectServiceHandler) GetCurrentSession(ctx context.Context, req *connect.Request[v1pb.GetCurrentSessionRequest]) (*connect.Response[v1pb.GetCurrentSessionResponse], error) {
resp, err := s.APIV1Service.GetCurrentSession(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) CreateSession(ctx context.Context, req *connect.Request[v1pb.CreateSessionRequest]) (*connect.Response[v1pb.CreateSessionResponse], error) {
resp, err := s.APIV1Service.CreateSession(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) DeleteSession(ctx context.Context, req *connect.Request[v1pb.DeleteSessionRequest]) (*connect.Response[emptypb.Empty], error) {
resp, err := s.APIV1Service.DeleteSession(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
// UserService
func (s *ConnectServiceHandler) ListUsers(ctx context.Context, req *connect.Request[v1pb.ListUsersRequest]) (*connect.Response[v1pb.ListUsersResponse], error) {
resp, err := s.APIV1Service.ListUsers(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) GetUser(ctx context.Context, req *connect.Request[v1pb.GetUserRequest]) (*connect.Response[v1pb.User], error) {
resp, err := s.APIV1Service.GetUser(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) CreateUser(ctx context.Context, req *connect.Request[v1pb.CreateUserRequest]) (*connect.Response[v1pb.User], error) {
resp, err := s.APIV1Service.CreateUser(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) UpdateUser(ctx context.Context, req *connect.Request[v1pb.UpdateUserRequest]) (*connect.Response[v1pb.User], error) {
resp, err := s.APIV1Service.UpdateUser(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) DeleteUser(ctx context.Context, req *connect.Request[v1pb.DeleteUserRequest]) (*connect.Response[emptypb.Empty], error) {
resp, err := s.APIV1Service.DeleteUser(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) ListAllUserStats(ctx context.Context, req *connect.Request[v1pb.ListAllUserStatsRequest]) (*connect.Response[v1pb.ListAllUserStatsResponse], error) {
resp, err := s.APIV1Service.ListAllUserStats(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) GetUserStats(ctx context.Context, req *connect.Request[v1pb.GetUserStatsRequest]) (*connect.Response[v1pb.UserStats], error) {
resp, err := s.APIV1Service.GetUserStats(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) GetUserSetting(ctx context.Context, req *connect.Request[v1pb.GetUserSettingRequest]) (*connect.Response[v1pb.UserSetting], error) {
resp, err := s.APIV1Service.GetUserSetting(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) UpdateUserSetting(ctx context.Context, req *connect.Request[v1pb.UpdateUserSettingRequest]) (*connect.Response[v1pb.UserSetting], error) {
resp, err := s.APIV1Service.UpdateUserSetting(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) ListUserSettings(ctx context.Context, req *connect.Request[v1pb.ListUserSettingsRequest]) (*connect.Response[v1pb.ListUserSettingsResponse], error) {
resp, err := s.APIV1Service.ListUserSettings(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) ListUserAccessTokens(ctx context.Context, req *connect.Request[v1pb.ListUserAccessTokensRequest]) (*connect.Response[v1pb.ListUserAccessTokensResponse], error) {
resp, err := s.APIV1Service.ListUserAccessTokens(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) CreateUserAccessToken(ctx context.Context, req *connect.Request[v1pb.CreateUserAccessTokenRequest]) (*connect.Response[v1pb.UserAccessToken], error) {
resp, err := s.APIV1Service.CreateUserAccessToken(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) DeleteUserAccessToken(ctx context.Context, req *connect.Request[v1pb.DeleteUserAccessTokenRequest]) (*connect.Response[emptypb.Empty], error) {
resp, err := s.APIV1Service.DeleteUserAccessToken(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) ListUserSessions(ctx context.Context, req *connect.Request[v1pb.ListUserSessionsRequest]) (*connect.Response[v1pb.ListUserSessionsResponse], error) {
resp, err := s.APIV1Service.ListUserSessions(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) RevokeUserSession(ctx context.Context, req *connect.Request[v1pb.RevokeUserSessionRequest]) (*connect.Response[emptypb.Empty], error) {
resp, err := s.APIV1Service.RevokeUserSession(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) ListUserWebhooks(ctx context.Context, req *connect.Request[v1pb.ListUserWebhooksRequest]) (*connect.Response[v1pb.ListUserWebhooksResponse], error) {
resp, err := s.APIV1Service.ListUserWebhooks(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) CreateUserWebhook(ctx context.Context, req *connect.Request[v1pb.CreateUserWebhookRequest]) (*connect.Response[v1pb.UserWebhook], error) {
resp, err := s.APIV1Service.CreateUserWebhook(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) UpdateUserWebhook(ctx context.Context, req *connect.Request[v1pb.UpdateUserWebhookRequest]) (*connect.Response[v1pb.UserWebhook], error) {
resp, err := s.APIV1Service.UpdateUserWebhook(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) DeleteUserWebhook(ctx context.Context, req *connect.Request[v1pb.DeleteUserWebhookRequest]) (*connect.Response[emptypb.Empty], error) {
resp, err := s.APIV1Service.DeleteUserWebhook(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) ListUserNotifications(ctx context.Context, req *connect.Request[v1pb.ListUserNotificationsRequest]) (*connect.Response[v1pb.ListUserNotificationsResponse], error) {
resp, err := s.APIV1Service.ListUserNotifications(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) UpdateUserNotification(ctx context.Context, req *connect.Request[v1pb.UpdateUserNotificationRequest]) (*connect.Response[v1pb.UserNotification], error) {
resp, err := s.APIV1Service.UpdateUserNotification(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) DeleteUserNotification(ctx context.Context, req *connect.Request[v1pb.DeleteUserNotificationRequest]) (*connect.Response[emptypb.Empty], error) {
resp, err := s.APIV1Service.DeleteUserNotification(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
// MemoService
func (s *ConnectServiceHandler) CreateMemo(ctx context.Context, req *connect.Request[v1pb.CreateMemoRequest]) (*connect.Response[v1pb.Memo], error) {
resp, err := s.APIV1Service.CreateMemo(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) ListMemos(ctx context.Context, req *connect.Request[v1pb.ListMemosRequest]) (*connect.Response[v1pb.ListMemosResponse], error) {
resp, err := s.APIV1Service.ListMemos(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) GetMemo(ctx context.Context, req *connect.Request[v1pb.GetMemoRequest]) (*connect.Response[v1pb.Memo], error) {
resp, err := s.APIV1Service.GetMemo(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) UpdateMemo(ctx context.Context, req *connect.Request[v1pb.UpdateMemoRequest]) (*connect.Response[v1pb.Memo], error) {
resp, err := s.APIV1Service.UpdateMemo(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) DeleteMemo(ctx context.Context, req *connect.Request[v1pb.DeleteMemoRequest]) (*connect.Response[emptypb.Empty], error) {
resp, err := s.APIV1Service.DeleteMemo(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) SetMemoAttachments(ctx context.Context, req *connect.Request[v1pb.SetMemoAttachmentsRequest]) (*connect.Response[emptypb.Empty], error) {
resp, err := s.APIV1Service.SetMemoAttachments(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) ListMemoAttachments(ctx context.Context, req *connect.Request[v1pb.ListMemoAttachmentsRequest]) (*connect.Response[v1pb.ListMemoAttachmentsResponse], error) {
resp, err := s.APIV1Service.ListMemoAttachments(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) SetMemoRelations(ctx context.Context, req *connect.Request[v1pb.SetMemoRelationsRequest]) (*connect.Response[emptypb.Empty], error) {
resp, err := s.APIV1Service.SetMemoRelations(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) ListMemoRelations(ctx context.Context, req *connect.Request[v1pb.ListMemoRelationsRequest]) (*connect.Response[v1pb.ListMemoRelationsResponse], error) {
resp, err := s.APIV1Service.ListMemoRelations(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) CreateMemoComment(ctx context.Context, req *connect.Request[v1pb.CreateMemoCommentRequest]) (*connect.Response[v1pb.Memo], error) {
resp, err := s.APIV1Service.CreateMemoComment(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) ListMemoComments(ctx context.Context, req *connect.Request[v1pb.ListMemoCommentsRequest]) (*connect.Response[v1pb.ListMemoCommentsResponse], error) {
resp, err := s.APIV1Service.ListMemoComments(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) ListMemoReactions(ctx context.Context, req *connect.Request[v1pb.ListMemoReactionsRequest]) (*connect.Response[v1pb.ListMemoReactionsResponse], error) {
resp, err := s.APIV1Service.ListMemoReactions(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) UpsertMemoReaction(ctx context.Context, req *connect.Request[v1pb.UpsertMemoReactionRequest]) (*connect.Response[v1pb.Reaction], error) {
resp, err := s.APIV1Service.UpsertMemoReaction(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) DeleteMemoReaction(ctx context.Context, req *connect.Request[v1pb.DeleteMemoReactionRequest]) (*connect.Response[emptypb.Empty], error) {
resp, err := s.APIV1Service.DeleteMemoReaction(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
// AttachmentService
func (s *ConnectServiceHandler) CreateAttachment(ctx context.Context, req *connect.Request[v1pb.CreateAttachmentRequest]) (*connect.Response[v1pb.Attachment], error) {
resp, err := s.APIV1Service.CreateAttachment(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) ListAttachments(ctx context.Context, req *connect.Request[v1pb.ListAttachmentsRequest]) (*connect.Response[v1pb.ListAttachmentsResponse], error) {
resp, err := s.APIV1Service.ListAttachments(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) GetAttachment(ctx context.Context, req *connect.Request[v1pb.GetAttachmentRequest]) (*connect.Response[v1pb.Attachment], error) {
resp, err := s.APIV1Service.GetAttachment(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) UpdateAttachment(ctx context.Context, req *connect.Request[v1pb.UpdateAttachmentRequest]) (*connect.Response[v1pb.Attachment], error) {
resp, err := s.APIV1Service.UpdateAttachment(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) DeleteAttachment(ctx context.Context, req *connect.Request[v1pb.DeleteAttachmentRequest]) (*connect.Response[emptypb.Empty], error) {
resp, err := s.APIV1Service.DeleteAttachment(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
// ShortcutService
func (s *ConnectServiceHandler) ListShortcuts(ctx context.Context, req *connect.Request[v1pb.ListShortcutsRequest]) (*connect.Response[v1pb.ListShortcutsResponse], error) {
resp, err := s.APIV1Service.ListShortcuts(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) GetShortcut(ctx context.Context, req *connect.Request[v1pb.GetShortcutRequest]) (*connect.Response[v1pb.Shortcut], error) {
resp, err := s.APIV1Service.GetShortcut(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) CreateShortcut(ctx context.Context, req *connect.Request[v1pb.CreateShortcutRequest]) (*connect.Response[v1pb.Shortcut], error) {
resp, err := s.APIV1Service.CreateShortcut(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) UpdateShortcut(ctx context.Context, req *connect.Request[v1pb.UpdateShortcutRequest]) (*connect.Response[v1pb.Shortcut], error) {
resp, err := s.APIV1Service.UpdateShortcut(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) DeleteShortcut(ctx context.Context, req *connect.Request[v1pb.DeleteShortcutRequest]) (*connect.Response[emptypb.Empty], error) {
resp, err := s.APIV1Service.DeleteShortcut(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
// ActivityService
func (s *ConnectServiceHandler) ListActivities(ctx context.Context, req *connect.Request[v1pb.ListActivitiesRequest]) (*connect.Response[v1pb.ListActivitiesResponse], error) {
resp, err := s.APIV1Service.ListActivities(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) GetActivity(ctx context.Context, req *connect.Request[v1pb.GetActivityRequest]) (*connect.Response[v1pb.Activity], error) {
resp, err := s.APIV1Service.GetActivity(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
// IdentityProviderService
func (s *ConnectServiceHandler) ListIdentityProviders(ctx context.Context, req *connect.Request[v1pb.ListIdentityProvidersRequest]) (*connect.Response[v1pb.ListIdentityProvidersResponse], error) {
resp, err := s.APIV1Service.ListIdentityProviders(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) GetIdentityProvider(ctx context.Context, req *connect.Request[v1pb.GetIdentityProviderRequest]) (*connect.Response[v1pb.IdentityProvider], error) {
resp, err := s.APIV1Service.GetIdentityProvider(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) CreateIdentityProvider(ctx context.Context, req *connect.Request[v1pb.CreateIdentityProviderRequest]) (*connect.Response[v1pb.IdentityProvider], error) {
resp, err := s.APIV1Service.CreateIdentityProvider(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) UpdateIdentityProvider(ctx context.Context, req *connect.Request[v1pb.UpdateIdentityProviderRequest]) (*connect.Response[v1pb.IdentityProvider], error) {
resp, err := s.APIV1Service.UpdateIdentityProvider(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) DeleteIdentityProvider(ctx context.Context, req *connect.Request[v1pb.DeleteIdentityProviderRequest]) (*connect.Response[emptypb.Empty], error) {
resp, err := s.APIV1Service.DeleteIdentityProvider(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}

View File

@ -6,6 +6,7 @@ import (
"github.com/usememos/memos/internal/profile"
"github.com/usememos/memos/plugin/markdown"
"github.com/usememos/memos/server/auth"
apiv1 "github.com/usememos/memos/server/router/api/v1"
"github.com/usememos/memos/store"
teststore "github.com/usememos/memos/store/test"
@ -81,6 +82,6 @@ func (ts *TestService) CreateRegularUser(ctx context.Context, username string) (
// CreateUserContext creates a context with the given user's ID for authentication.
func (*TestService) CreateUserContext(ctx context.Context, userID int32) context.Context {
// Use the real context key from the parent package
return context.WithValue(ctx, apiv1.UserIDContextKey, userID)
// Use the context key from the auth package
return context.WithValue(ctx, auth.UserIDContextKey, userID)
}

View File

@ -27,6 +27,7 @@ import (
"github.com/usememos/memos/internal/util"
v1pb "github.com/usememos/memos/proto/gen/api/v1"
storepb "github.com/usememos/memos/proto/gen/store"
"github.com/usememos/memos/server/auth"
"github.com/usememos/memos/store"
)
@ -536,7 +537,7 @@ func (s *APIV1Service) ListUserAccessTokens(ctx context.Context, request *v1pb.L
accessTokens := []*v1pb.UserAccessToken{}
for _, userAccessToken := range userAccessTokens {
claims := &ClaimsMessage{}
claims := &auth.ClaimsMessage{}
_, err := jwt.ParseWithClaims(userAccessToken.AccessToken, claims, func(t *jwt.Token) (any, error) {
if t.Method.Alg() != jwt.SigningMethodHS256.Name {
return nil, errors.Errorf("unexpected access token signing method=%v, expect %v", t.Header["alg"], jwt.SigningMethodHS256)
@ -616,12 +617,12 @@ func (s *APIV1Service) CreateUserAccessToken(ctx context.Context, request *v1pb.
expiresAt = request.AccessToken.ExpiresAt.AsTime()
}
accessToken, err := GenerateAccessToken(currentUser.Username, currentUser.ID, expiresAt, []byte(s.Secret))
accessToken, err := auth.GenerateAccessToken(currentUser.Username, currentUser.ID, expiresAt, []byte(s.Secret))
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to generate access token: %v", err)
}
claims := &ClaimsMessage{}
claims := &auth.ClaimsMessage{}
_, err = jwt.ParseWithClaims(accessToken, claims, func(t *jwt.Token) (any, error) {
if t.Method.Alg() != jwt.SigningMethodHS256.Name {
return nil, errors.Errorf("unexpected access token signing method=%v, expect %v", t.Header["alg"], jwt.SigningMethodHS256)

View File

@ -4,9 +4,10 @@ import (
"context"
"fmt"
"math"
"net/http"
"connectrpc.com/connect"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/improbable-eng/grpc-web/go/grpcweb"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"golang.org/x/sync/semaphore"
@ -123,15 +124,28 @@ func (s *APIV1Service) RegisterGateway(ctx context.Context, echoServer *echo.Ech
gwGroup.Any("/api/v1/*", handler)
gwGroup.Any("/file/*", handler)
// GRPC web proxy.
options := []grpcweb.Option{
grpcweb.WithCorsForRegisteredEndpointsOnly(false),
grpcweb.WithOriginFunc(func(_ string) bool {
return true
}),
}
wrappedGrpc := grpcweb.WrapServer(s.grpcServer, options...)
echoServer.Any("/memos.api.v1.*", echo.WrapHandler(wrappedGrpc))
// Connect handlers for browser clients (replaces grpc-web).
logStacktraces := s.Profile.IsDev()
connectInterceptors := connect.WithInterceptors(
NewLoggingInterceptor(logStacktraces),
NewRecoveryInterceptor(logStacktraces),
NewAuthInterceptor(s.Store, s.Secret),
)
connectMux := http.NewServeMux()
connectHandler := NewConnectServiceHandler(s)
connectHandler.RegisterConnectHandlers(connectMux, connectInterceptors)
// Wrap with CORS for browser access
corsHandler := middleware.CORSWithConfig(middleware.CORSConfig{
AllowOriginFunc: func(_ string) (bool, error) {
return true, nil
},
AllowMethods: []string{http.MethodGet, http.MethodPost, http.MethodOptions},
AllowHeaders: []string{"*"},
AllowCredentials: true,
})
connectGroup := echoServer.Group("", corsHandler)
connectGroup.Any("/memos.api.v1.*", echo.WrapHandler(connectMux))
return nil
}

View File

@ -14,7 +14,6 @@ import (
"time"
"github.com/disintegration/imaging"
"github.com/golang-jwt/jwt/v5"
"github.com/labstack/echo/v4"
"github.com/pkg/errors"
"golang.org/x/sync/semaphore"
@ -23,7 +22,7 @@ import (
"github.com/usememos/memos/internal/util"
"github.com/usememos/memos/plugin/storage/s3"
storepb "github.com/usememos/memos/proto/gen/store"
apiv1 "github.com/usememos/memos/server/router/api/v1"
"github.com/usememos/memos/server/auth"
"github.com/usememos/memos/store"
)
@ -43,9 +42,9 @@ var SupportedThumbnailMimeTypes = []string{
// This service bypasses gRPC-Gateway to use native HTTP serving via http.ServeContent(),
// which is required for Safari video/audio playback.
type FileServerService struct {
Profile *profile.Profile
Store *store.Store
Secret string
Profile *profile.Profile
Store *store.Store
authenticator *auth.Authenticator
// thumbnailSemaphore limits concurrent thumbnail generation to prevent memory exhaustion
thumbnailSemaphore *semaphore.Weighted
@ -56,7 +55,7 @@ func NewFileServerService(profile *profile.Profile, store *store.Store, secret s
return &FileServerService{
Profile: profile,
Store: store,
Secret: secret,
authenticator: auth.NewAuthenticator(store, secret),
thumbnailSemaphore: semaphore.NewWeighted(3), // Limit to 3 concurrent thumbnail generations
}
}
@ -249,10 +248,11 @@ func (s *FileServerService) checkAttachmentPermission(ctx context.Context, c ech
// getCurrentUser retrieves the current authenticated user from the Echo context.
// It checks both session cookies and Bearer tokens for authentication.
// Uses the shared Authenticator for consistent authentication logic.
func (s *FileServerService) getCurrentUser(ctx context.Context, c echo.Context) (*store.User, error) {
// Try session cookie authentication first
if cookie, err := c.Cookie(apiv1.SessionCookieName); err == nil && cookie.Value != "" {
user, err := s.authenticateBySession(ctx, cookie.Value)
if cookie, err := c.Cookie(auth.SessionCookieName); err == nil && cookie.Value != "" {
user, err := s.authenticator.AuthenticateBySession(ctx, cookie.Value)
if err == nil && user != nil {
return user, nil
}
@ -262,8 +262,8 @@ func (s *FileServerService) getCurrentUser(ctx context.Context, c echo.Context)
authHeader := c.Request().Header.Get("Authorization")
if authHeader != "" {
parts := strings.Fields(authHeader)
if len(parts) == 2 && strings.ToLower(parts[0]) == "bearer" {
user, err := s.authenticateByJWT(ctx, parts[1])
if len(parts) == 2 && strings.EqualFold(parts[0], "bearer") {
user, err := s.authenticator.AuthenticateByJWT(ctx, parts[1])
if err == nil && user != nil {
return user, nil
}
@ -274,139 +274,6 @@ func (s *FileServerService) getCurrentUser(ctx context.Context, c echo.Context)
return nil, nil
}
// authenticateBySession authenticates a user using session ID from cookie.
func (s *FileServerService) authenticateBySession(ctx context.Context, sessionCookieValue string) (*store.User, error) {
if sessionCookieValue == "" {
return nil, errors.New("session cookie value not found")
}
// Parse the cookie value to extract userID and sessionID
userID, sessionID, err := s.parseSessionCookieValue(sessionCookieValue)
if err != nil {
return nil, errors.Wrap(err, "invalid session cookie format")
}
// Get the user
user, err := s.Store.GetUser(ctx, &store.FindUser{
ID: &userID,
})
if err != nil {
return nil, errors.Wrap(err, "failed to get user")
}
if user == nil {
return nil, errors.New("user not found")
}
if user.RowStatus == store.Archived {
return nil, errors.New("user is archived")
}
// Get user sessions and validate the sessionID
sessions, err := s.Store.GetUserSessions(ctx, user.ID)
if err != nil {
return nil, errors.Wrap(err, "failed to get user sessions")
}
if !s.validateUserSession(sessionID, sessions) {
return nil, errors.New("invalid or expired session")
}
return user, nil
}
// authenticateByJWT authenticates a user using JWT access token from Authorization header.
func (s *FileServerService) authenticateByJWT(ctx context.Context, accessToken string) (*store.User, error) {
if accessToken == "" {
return nil, errors.New("access token not found")
}
claims := &apiv1.ClaimsMessage{}
_, err := jwt.ParseWithClaims(accessToken, claims, func(t *jwt.Token) (any, error) {
if t.Method.Alg() != jwt.SigningMethodHS256.Name {
return nil, errors.Errorf("unexpected access token signing method=%v, expect %v", t.Header["alg"], jwt.SigningMethodHS256)
}
if kid, ok := t.Header["kid"].(string); ok {
if kid == apiv1.KeyID {
return []byte(s.Secret), nil
}
}
return nil, errors.Errorf("unexpected access token kid=%v", t.Header["kid"])
})
if err != nil {
return nil, errors.Wrap(err, "Invalid or expired access token")
}
// Get user from JWT claims
userID, err := util.ConvertStringToInt32(claims.Subject)
if err != nil {
return nil, errors.Wrap(err, "malformed ID in the token")
}
user, err := s.Store.GetUser(ctx, &store.FindUser{
ID: &userID,
})
if err != nil {
return nil, errors.Wrap(err, "failed to get user")
}
if user == nil {
return nil, errors.Errorf("user %q not exists", userID)
}
if user.RowStatus == store.Archived {
return nil, errors.Errorf("user %q is archived", userID)
}
// Validate that this access token exists in the user's access tokens
accessTokens, err := s.Store.GetUserAccessTokens(ctx, user.ID)
if err != nil {
return nil, errors.Wrapf(err, "failed to get user access tokens")
}
if !s.validateAccessToken(accessToken, accessTokens) {
return nil, errors.New("invalid access token")
}
return user, nil
}
// parseSessionCookieValue parses the session cookie value to extract userID and sessionID.
func (*FileServerService) parseSessionCookieValue(cookieValue string) (int32, string, error) {
parts := strings.SplitN(cookieValue, "-", 2)
if len(parts) != 2 {
return 0, "", errors.New("invalid session cookie format")
}
userID, err := util.ConvertStringToInt32(parts[0])
if err != nil {
return 0, "", errors.Errorf("invalid user ID in session cookie: %v", err)
}
return userID, parts[1], nil
}
// validateUserSession checks if a session exists and is still valid using sliding expiration.
func (*FileServerService) validateUserSession(sessionID string, userSessions []*storepb.SessionsUserSetting_Session) bool {
for _, session := range userSessions {
if sessionID == session.SessionId {
// Use sliding expiration: check if last_accessed_time + 14 days > current_time
if session.LastAccessedTime != nil {
expirationTime := session.LastAccessedTime.AsTime().Add(apiv1.SessionSlidingDuration)
if expirationTime.Before(time.Now()) {
return false
}
}
return true
}
}
return false
}
// validateAccessToken checks if the provided JWT token exists in the user's access tokens list.
func (*FileServerService) validateAccessToken(accessTokenString string, userAccessTokens []*storepb.AccessTokensUserSetting_AccessToken) bool {
for _, userAccessToken := range userAccessTokens {
if accessTokenString == userAccessToken.AccessToken {
return true
}
}
return false
}
// isImageType checks if the mime type is an image that supports thumbnails.
func (*FileServerService) isImageType(mimeType string) bool {
return mimeType == "image/png" || mimeType == "image/jpeg"

View File

@ -10,6 +10,8 @@
"format": "biome format --write src"
},
"dependencies": {
"@connectrpc/connect": "^2.1.1",
"@connectrpc/connect-web": "^2.1.1",
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
@ -84,7 +86,6 @@
"@types/uuid": "^10.0.0",
"@vitejs/plugin-react": "^4.7.0",
"long": "^5.3.2",
"nice-grpc-web": "^3.3.9",
"terser": "^5.44.1",
"tw-animate-css": "^1.4.0",
"typescript": "^5.9.3",

View File

@ -8,6 +8,12 @@ importers:
.:
dependencies:
'@connectrpc/connect':
specifier: ^2.1.1
version: 2.1.1(@bufbuild/protobuf@2.10.1)
'@connectrpc/connect-web':
specifier: ^2.1.1
version: 2.1.1(@bufbuild/protobuf@2.10.1)(@connectrpc/connect@2.1.1(@bufbuild/protobuf@2.10.1))
'@dnd-kit/core':
specifier: ^6.3.1
version: 6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@ -225,9 +231,6 @@ importers:
long:
specifier: ^5.3.2
version: 5.3.2
nice-grpc-web:
specifier: ^3.3.9
version: 3.3.9(ws@8.18.3)
terser:
specifier: ^5.44.1
version: 5.44.1
@ -410,6 +413,17 @@ packages:
'@chevrotain/utils@11.0.3':
resolution: {integrity: sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==}
'@connectrpc/connect-web@2.1.1':
resolution: {integrity: sha512-J8317Q2MaFRCT1jzVR1o06bZhDIBmU0UAzWx6xOIXzOq8+k71/+k7MUF7AwcBUX+34WIvbm5syRgC5HXQA8fOg==}
peerDependencies:
'@bufbuild/protobuf': ^2.7.0
'@connectrpc/connect': 2.1.1
'@connectrpc/connect@2.1.1':
resolution: {integrity: sha512-JzhkaTvM73m2K1URT6tv53k2RwngSmCXLZJgK580qNQOXRzZRR/BCMfZw3h+90JpnG6XksP5bYT+cz0rpUzUWQ==}
peerDependencies:
'@bufbuild/protobuf': ^2.7.0
'@dnd-kit/accessibility@3.1.1':
resolution: {integrity: sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==}
peerDependencies:
@ -1519,9 +1533,6 @@ packages:
'@xobotyi/scrollbar-width@1.9.5':
resolution: {integrity: sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==}
abort-controller-x@0.4.3:
resolution: {integrity: sha512-VtUwTNU8fpMwvWGn4xE93ywbogTYsuT+AUxAXOeelbXuQVIwNmC5YLeho9sH4vZ4ITW8414TTAOG1nW6uIVHCA==}
accessor-fn@1.5.3:
resolution: {integrity: sha512-rkAofCwe/FvYFUlMB0v0gWmhqtfAtV1IUkdPbfhTUyYniu5LrC0A0UJkTH0Jv3S8SvwkmfuAlY+mQIJATdocMA==}
engines: {node: '>=12'}
@ -2077,11 +2088,6 @@ packages:
resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
engines: {node: '>=12'}
isomorphic-ws@5.0.0:
resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==}
peerDependencies:
ws: '*'
jerrypick@1.1.2:
resolution: {integrity: sha512-YKnxXEekXKzhpf7CLYA0A+oDP8V0OhICNCr5lv96FvSsDEmrb0GKM776JgQvHTMjr7DTTPEVv/1Ciaw0uEWzBA==}
engines: {node: '>=12'}
@ -2090,9 +2096,6 @@ packages:
resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
hasBin: true
js-base64@3.7.8:
resolution: {integrity: sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==}
js-cookie@2.2.1:
resolution: {integrity: sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==}
@ -2430,12 +2433,6 @@ packages:
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
nice-grpc-common@2.0.2:
resolution: {integrity: sha512-7RNWbls5kAL1QVUOXvBsv1uO0wPQK3lHv+cY1gwkTzirnG1Nop4cBJZubpgziNbaVc/bl9QJcyvsf/NQxa3rjQ==}
nice-grpc-web@3.3.9:
resolution: {integrity: sha512-CiCQLdLTux9D4try8XlHW9tHIP/uLB+aciNKErDNLUM6kzhPFaVh8q+oTkoVGOjxOacEzlOwQRRjwQETAx5lVw==}
node-releases@2.0.27:
resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==}
@ -2819,9 +2816,6 @@ packages:
ts-easing@0.2.0:
resolution: {integrity: sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==}
ts-error@1.0.6:
resolution: {integrity: sha512-tLJxacIQUM82IR7JO1UUkKlYuUTmoY9HBJAmNWFzheSlDS5SPMcNIepejHJa4BpPQLAcbRhRf3GDJzyj6rbKvA==}
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
@ -2974,18 +2968,6 @@ packages:
web-namespaces@2.0.1:
resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==}
ws@8.18.3:
resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: '>=5.0.2'
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
@ -3175,6 +3157,15 @@ snapshots:
'@chevrotain/utils@11.0.3': {}
'@connectrpc/connect-web@2.1.1(@bufbuild/protobuf@2.10.1)(@connectrpc/connect@2.1.1(@bufbuild/protobuf@2.10.1))':
dependencies:
'@bufbuild/protobuf': 2.10.1
'@connectrpc/connect': 2.1.1(@bufbuild/protobuf@2.10.1)
'@connectrpc/connect@2.1.1(@bufbuild/protobuf@2.10.1)':
dependencies:
'@bufbuild/protobuf': 2.10.1
'@dnd-kit/accessibility@3.1.1(react@18.3.1)':
dependencies:
react: 18.3.1
@ -4221,8 +4212,6 @@ snapshots:
'@xobotyi/scrollbar-width@1.9.5': {}
abort-controller-x@0.4.3: {}
accessor-fn@1.5.3: {}
acorn@8.15.0: {}
@ -4857,16 +4846,10 @@ snapshots:
is-plain-obj@4.1.0: {}
isomorphic-ws@5.0.0(ws@8.18.3):
dependencies:
ws: 8.18.3
jerrypick@1.1.2: {}
jiti@2.6.1: {}
js-base64@3.7.8: {}
js-cookie@2.2.1: {}
js-tokens@4.0.0: {}
@ -5420,19 +5403,6 @@ snapshots:
nanoid@3.3.11: {}
nice-grpc-common@2.0.2:
dependencies:
ts-error: 1.0.6
nice-grpc-web@3.3.9(ws@8.18.3):
dependencies:
abort-controller-x: 0.4.3
isomorphic-ws: 5.0.0(ws@8.18.3)
js-base64: 3.7.8
nice-grpc-common: 2.0.2
transitivePeerDependencies:
- ws
node-releases@2.0.27: {}
object-assign@4.1.1: {}
@ -5877,8 +5847,6 @@ snapshots:
ts-easing@0.2.0: {}
ts-error@1.0.6: {}
tslib@2.8.1: {}
tw-animate-css@1.4.0: {}
@ -6010,8 +5978,6 @@ snapshots:
web-namespaces@2.0.1: {}
ws@8.18.3: {}
yallist@3.1.1: {}
yaml@1.10.2: {}

View File

@ -11,7 +11,7 @@ import {
} from "lucide-react";
import React, { useState } from "react";
import { cn } from "@/lib/utils";
import { Attachment } from "@/types/proto/api/v1/attachment_service";
import { Attachment } from "@/types/proto/api/v1/attachment_service_pb";
import { getAttachmentThumbnailUrl, getAttachmentType, getAttachmentUrl } from "@/utils/attachment";
import SquareDiv from "./kit/SquareDiv";
import PreviewImageDialog from "./PreviewImageDialog";

View File

@ -5,7 +5,7 @@ import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { userStore } from "@/store";
import { User } from "@/types/proto/api/v1/user_service";
import { User } from "@/types/proto/api/v1/user_service_pb";
import { useTranslate } from "@/utils/i18n";
interface Props {
@ -61,7 +61,7 @@ function ChangeMemberPasswordDialog({ open, onOpenChange, user, onSuccess }: Pro
onOpenChange(false);
} catch (error: any) {
console.error(error);
toast.error(error.details);
toast.error(error.message);
}
};

View File

@ -1,3 +1,4 @@
import { timestampFromDate } from "@bufbuild/protobuf/wkt";
import React, { useState } from "react";
import { toast } from "react-hot-toast";
import { Button } from "@/components/ui/button";
@ -8,7 +9,7 @@ import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { userServiceClient } from "@/grpcweb";
import useCurrentUser from "@/hooks/useCurrentUser";
import useLoading from "@/hooks/useLoading";
import { UserAccessToken } from "@/types/proto/api/v1/user_service";
import { UserAccessToken } from "@/types/proto/api/v1/user_service_pb";
import { useTranslate } from "@/utils/i18n";
interface Props {
@ -77,7 +78,7 @@ function CreateAccessTokenDialog({ open, onOpenChange, onSuccess }: Props) {
parent: currentUser.name,
accessToken: {
description: state.description,
expiresAt: state.expiration ? new Date(Date.now() + state.expiration * 1000) : undefined,
expiresAt: state.expiration ? timestampFromDate(new Date(Date.now() + state.expiration * 1000)) : undefined,
},
});
@ -85,7 +86,7 @@ function CreateAccessTokenDialog({ open, onOpenChange, onSuccess }: Props) {
onSuccess(created);
onOpenChange(false);
} catch (error: any) {
toast.error(error.details);
toast.error(error.message);
console.error(error);
requestState.setError();
}

View File

@ -1,3 +1,5 @@
import { create } from "@bufbuild/protobuf";
import { FieldMaskSchema } from "@bufbuild/protobuf/wkt";
import { useEffect, useState } from "react";
import { toast } from "react-hot-toast";
import { Button } from "@/components/ui/button";
@ -7,94 +9,115 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@
import { Separator } from "@/components/ui/separator";
import { identityProviderServiceClient } from "@/grpcweb";
import { absolutifyLink } from "@/helpers/utils";
import { FieldMapping, IdentityProvider, IdentityProvider_Type, OAuth2Config } from "@/types/proto/api/v1/idp_service";
import {
FieldMapping,
FieldMappingSchema,
IdentityProvider,
IdentityProvider_Type,
IdentityProviderConfigSchema,
IdentityProviderSchema,
OAuth2Config,
OAuth2ConfigSchema,
} from "@/types/proto/api/v1/idp_service_pb";
import { useTranslate } from "@/utils/i18n";
const templateList: IdentityProvider[] = [
{
create(IdentityProviderSchema, {
name: "",
title: "GitHub",
type: IdentityProvider_Type.OAUTH2,
identifierFilter: "",
config: {
oauth2Config: {
clientId: "",
clientSecret: "",
authUrl: "https://github.com/login/oauth/authorize",
tokenUrl: "https://github.com/login/oauth/access_token",
userInfoUrl: "https://api.github.com/user",
scopes: ["read:user"],
fieldMapping: FieldMapping.fromPartial({
identifier: "login",
displayName: "name",
email: "email",
config: create(IdentityProviderConfigSchema, {
config: {
case: "oauth2Config",
value: create(OAuth2ConfigSchema, {
clientId: "",
clientSecret: "",
authUrl: "https://github.com/login/oauth/authorize",
tokenUrl: "https://github.com/login/oauth/access_token",
userInfoUrl: "https://api.github.com/user",
scopes: ["read:user"],
fieldMapping: create(FieldMappingSchema, {
identifier: "login",
displayName: "name",
email: "email",
}),
}),
},
},
},
{
}),
}),
create(IdentityProviderSchema, {
name: "",
title: "GitLab",
type: IdentityProvider_Type.OAUTH2,
identifierFilter: "",
config: {
oauth2Config: {
clientId: "",
clientSecret: "",
authUrl: "https://gitlab.com/oauth/authorize",
tokenUrl: "https://gitlab.com/oauth/token",
userInfoUrl: "https://gitlab.com/oauth/userinfo",
scopes: ["openid"],
fieldMapping: FieldMapping.fromPartial({
identifier: "name",
displayName: "name",
email: "email",
config: create(IdentityProviderConfigSchema, {
config: {
case: "oauth2Config",
value: create(OAuth2ConfigSchema, {
clientId: "",
clientSecret: "",
authUrl: "https://gitlab.com/oauth/authorize",
tokenUrl: "https://gitlab.com/oauth/token",
userInfoUrl: "https://gitlab.com/oauth/userinfo",
scopes: ["openid"],
fieldMapping: create(FieldMappingSchema, {
identifier: "name",
displayName: "name",
email: "email",
}),
}),
},
},
},
{
}),
}),
create(IdentityProviderSchema, {
name: "",
title: "Google",
type: IdentityProvider_Type.OAUTH2,
identifierFilter: "",
config: {
oauth2Config: {
clientId: "",
clientSecret: "",
authUrl: "https://accounts.google.com/o/oauth2/v2/auth",
tokenUrl: "https://oauth2.googleapis.com/token",
userInfoUrl: "https://www.googleapis.com/oauth2/v2/userinfo",
scopes: ["https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile"],
fieldMapping: FieldMapping.fromPartial({
identifier: "email",
displayName: "name",
email: "email",
config: create(IdentityProviderConfigSchema, {
config: {
case: "oauth2Config",
value: create(OAuth2ConfigSchema, {
clientId: "",
clientSecret: "",
authUrl: "https://accounts.google.com/o/oauth2/v2/auth",
tokenUrl: "https://oauth2.googleapis.com/token",
userInfoUrl: "https://www.googleapis.com/oauth2/v2/userinfo",
scopes: ["https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile"],
fieldMapping: create(FieldMappingSchema, {
identifier: "email",
displayName: "name",
email: "email",
}),
}),
},
},
},
{
}),
}),
create(IdentityProviderSchema, {
name: "",
title: "Custom",
type: IdentityProvider_Type.OAUTH2,
identifierFilter: "",
config: {
oauth2Config: {
clientId: "",
clientSecret: "",
authUrl: "",
tokenUrl: "",
userInfoUrl: "",
scopes: [],
fieldMapping: FieldMapping.fromPartial({
identifier: "",
displayName: "",
email: "",
config: create(IdentityProviderConfigSchema, {
config: {
case: "oauth2Config",
value: create(OAuth2ConfigSchema, {
clientId: "",
clientSecret: "",
authUrl: "",
tokenUrl: "",
userInfoUrl: "",
scopes: [],
fieldMapping: create(FieldMappingSchema, {
identifier: "",
displayName: "",
email: "",
}),
}),
},
},
},
}),
}),
];
interface Props {
@ -112,19 +135,21 @@ function CreateIdentityProviderDialog({ open, onOpenChange, identityProvider, on
identifierFilter: "",
});
const [type, setType] = useState<IdentityProvider_Type>(IdentityProvider_Type.OAUTH2);
const [oauth2Config, setOAuth2Config] = useState<OAuth2Config>({
clientId: "",
clientSecret: "",
authUrl: "",
tokenUrl: "",
userInfoUrl: "",
scopes: [],
fieldMapping: FieldMapping.fromPartial({
identifier: "",
displayName: "",
email: "",
const [oauth2Config, setOAuth2Config] = useState<OAuth2Config>(
create(OAuth2ConfigSchema, {
clientId: "",
clientSecret: "",
authUrl: "",
tokenUrl: "",
userInfoUrl: "",
scopes: [],
fieldMapping: create(FieldMappingSchema, {
identifier: "",
displayName: "",
email: "",
}),
}),
});
);
const [oauth2Scopes, setOAuth2Scopes] = useState<string>("");
const [selectedTemplate, setSelectedTemplate] = useState<string>("GitHub");
const isCreating = identityProvider === undefined;
@ -138,19 +163,21 @@ function CreateIdentityProviderDialog({ open, onOpenChange, identityProvider, on
identifierFilter: "",
});
setType(IdentityProvider_Type.OAUTH2);
setOAuth2Config({
clientId: "",
clientSecret: "",
authUrl: "",
tokenUrl: "",
userInfoUrl: "",
scopes: [],
fieldMapping: FieldMapping.fromPartial({
identifier: "",
displayName: "",
email: "",
setOAuth2Config(
create(OAuth2ConfigSchema, {
clientId: "",
clientSecret: "",
authUrl: "",
tokenUrl: "",
userInfoUrl: "",
scopes: [],
fieldMapping: create(FieldMappingSchema, {
identifier: "",
displayName: "",
email: "",
}),
}),
});
);
setOAuth2Scopes("");
setSelectedTemplate("GitHub");
}
@ -164,8 +191,8 @@ function CreateIdentityProviderDialog({ open, onOpenChange, identityProvider, on
identifierFilter: identityProvider.identifierFilter,
});
setType(identityProvider.type);
if (identityProvider.type === IdentityProvider_Type.OAUTH2) {
const oauth2Config = OAuth2Config.fromPartial(identityProvider.config?.oauth2Config || {});
if (identityProvider.type === IdentityProvider_Type.OAUTH2 && identityProvider.config?.config?.case === "oauth2Config") {
const oauth2Config = create(OAuth2ConfigSchema, identityProvider.config.config.value || {});
setOAuth2Config(oauth2Config);
setOAuth2Scopes(oauth2Config.scopes.join(" "));
}
@ -185,8 +212,8 @@ function CreateIdentityProviderDialog({ open, onOpenChange, identityProvider, on
identifierFilter: template.identifierFilter,
});
setType(template.type);
if (template.type === IdentityProvider_Type.OAUTH2) {
const oauth2Config = OAuth2Config.fromPartial(template.config?.oauth2Config || {});
if (template.type === IdentityProvider_Type.OAUTH2 && template.config?.config?.case === "oauth2Config") {
const oauth2Config = create(OAuth2ConfigSchema, template.config.config.value || {});
setOAuth2Config(oauth2Config);
setOAuth2Scopes(oauth2Config.scopes.join(" "));
}
@ -201,7 +228,7 @@ function CreateIdentityProviderDialog({ open, onOpenChange, identityProvider, on
if (basicInfo.title === "") {
return false;
}
if (type === "OAUTH2") {
if (type === IdentityProvider_Type.OAUTH2) {
if (
oauth2Config.clientId === "" ||
oauth2Config.authUrl === "" ||
@ -226,37 +253,43 @@ function CreateIdentityProviderDialog({ open, onOpenChange, identityProvider, on
try {
if (isCreating) {
await identityProviderServiceClient.createIdentityProvider({
identityProvider: {
identityProvider: create(IdentityProviderSchema, {
...basicInfo,
type: type,
config: {
oauth2Config: {
...oauth2Config,
scopes: oauth2Scopes.split(" "),
config: create(IdentityProviderConfigSchema, {
config: {
case: "oauth2Config",
value: {
...oauth2Config,
scopes: oauth2Scopes.split(" "),
},
},
},
},
}),
}),
});
toast.success(t("setting.sso-section.sso-created", { name: basicInfo.title }));
} else {
await identityProviderServiceClient.updateIdentityProvider({
identityProvider: {
identityProvider: create(IdentityProviderSchema, {
...basicInfo,
name: identityProvider!.name,
type: type,
config: {
oauth2Config: {
...oauth2Config,
scopes: oauth2Scopes.split(" "),
config: create(IdentityProviderConfigSchema, {
config: {
case: "oauth2Config",
value: {
...oauth2Config,
scopes: oauth2Scopes.split(" "),
},
},
},
},
updateMask: ["title", "identifier_filter", "config"],
}),
}),
updateMask: create(FieldMaskSchema, { paths: ["title", "identifier_filter", "config"] }),
});
toast.success(t("setting.sso-section.sso-updated", { name: basicInfo.title }));
}
} catch (error: any) {
toast.error(error.details);
toast.error(error.message);
console.error(error);
}
onSuccess?.();
@ -336,7 +369,7 @@ function CreateIdentityProviderDialog({ open, onOpenChange, identityProvider, on
}
/>
<Separator className="my-2" />
{type === "OAUTH2" && (
{type === IdentityProvider_Type.OAUTH2 && (
<>
{isCreating && (
<p className="border border-border rounded-md p-2 text-sm w-full mb-2 break-all">

View File

@ -1,3 +1,5 @@
import { create } from "@bufbuild/protobuf";
import { FieldMaskSchema } from "@bufbuild/protobuf/wkt";
import React, { useEffect, useState } from "react";
import { toast } from "react-hot-toast";
import { Button } from "@/components/ui/button";
@ -9,7 +11,7 @@ import { shortcutServiceClient } from "@/grpcweb";
import useCurrentUser from "@/hooks/useCurrentUser";
import useLoading from "@/hooks/useLoading";
import { userStore } from "@/store";
import { Shortcut } from "@/types/proto/api/v1/shortcut_service";
import { Shortcut, ShortcutSchema } from "@/types/proto/api/v1/shortcut_service_pb";
import { useTranslate } from "@/utils/i18n";
interface Props {
@ -22,23 +24,27 @@ interface Props {
function CreateShortcutDialog({ open, onOpenChange, shortcut: initialShortcut, onSuccess }: Props) {
const t = useTranslate();
const user = useCurrentUser();
const [shortcut, setShortcut] = useState<Shortcut>({
name: initialShortcut?.name || "",
title: initialShortcut?.title || "",
filter: initialShortcut?.filter || "",
});
const [shortcut, setShortcut] = useState<Shortcut>(
create(ShortcutSchema, {
name: initialShortcut?.name || "",
title: initialShortcut?.title || "",
filter: initialShortcut?.filter || "",
}),
);
const requestState = useLoading(false);
const isCreating = !initialShortcut;
useEffect(() => {
if (initialShortcut) {
setShortcut({
name: initialShortcut.name,
title: initialShortcut.title,
filter: initialShortcut.filter,
});
setShortcut(
create(ShortcutSchema, {
name: initialShortcut.name,
title: initialShortcut.title,
filter: initialShortcut.filter,
}),
);
} else {
setShortcut({ name: "", title: "", filter: "" });
setShortcut(create(ShortcutSchema, { name: "", title: "", filter: "" }));
}
}, [initialShortcut]);
@ -74,7 +80,7 @@ function CreateShortcutDialog({ open, onOpenChange, shortcut: initialShortcut, o
...shortcut,
name: initialShortcut!.name, // Keep the original resource name
},
updateMask: ["title", "filter"],
updateMask: create(FieldMaskSchema, { paths: ["title", "filter"] }),
});
toast.success("Update shortcut successfully");
}
@ -85,7 +91,7 @@ function CreateShortcutDialog({ open, onOpenChange, shortcut: initialShortcut, o
onOpenChange(false);
} catch (error: any) {
console.error(error);
toast.error(error.details);
toast.error(error.message);
requestState.setError();
}
};

View File

@ -1,3 +1,5 @@
import { create } from "@bufbuild/protobuf";
import { FieldMaskSchema } from "@bufbuild/protobuf/wkt";
import { useEffect, useState } from "react";
import { toast } from "react-hot-toast";
import { Button } from "@/components/ui/button";
@ -7,7 +9,7 @@ import { Label } from "@/components/ui/label";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { userServiceClient } from "@/grpcweb";
import useLoading from "@/hooks/useLoading";
import { User, User_Role } from "@/types/proto/api/v1/user_service";
import { User, User_Role, UserSchema } from "@/types/proto/api/v1/user_service_pb";
import { useTranslate } from "@/utils/i18n";
interface Props {
@ -19,15 +21,15 @@ interface Props {
function CreateUserDialog({ open, onOpenChange, user: initialUser, onSuccess }: Props) {
const t = useTranslate();
const [user, setUser] = useState(User.fromPartial({ ...initialUser }));
const [user, setUser] = useState(create(UserSchema, initialUser ? { username: initialUser.username, role: initialUser.role } : {}));
const requestState = useLoading(false);
const isCreating = !initialUser;
useEffect(() => {
if (initialUser) {
setUser(User.fromPartial(initialUser));
setUser(create(UserSchema, { username: initialUser.username, role: initialUser.role }));
} else {
setUser(User.fromPartial({}));
setUser(create(UserSchema, {}));
}
}, [initialUser]);
@ -60,7 +62,7 @@ function CreateUserDialog({ open, onOpenChange, user: initialUser, onSuccess }:
if (user.role !== initialUser?.role) {
updateMask.push("role");
}
await userServiceClient.updateUser({ user, updateMask });
await userServiceClient.updateUser({ user, updateMask: create(FieldMaskSchema, { paths: updateMask }) });
toast.success("Update user successfully");
}
requestState.setFinish();
@ -68,7 +70,7 @@ function CreateUserDialog({ open, onOpenChange, user: initialUser, onSuccess }:
onOpenChange(false);
} catch (error: any) {
console.error(error);
toast.error(error.details);
toast.error(error.message);
requestState.setError();
}
};
@ -112,16 +114,16 @@ function CreateUserDialog({ open, onOpenChange, user: initialUser, onSuccess }:
<div className="grid gap-2">
<Label>{t("common.role")}</Label>
<RadioGroup
value={user.role}
onValueChange={(value) => setPartialUser({ role: value as User_Role })}
value={String(user.role)}
onValueChange={(value) => setPartialUser({ role: Number(value) as User_Role })}
className="flex flex-row gap-4"
>
<div className="flex items-center space-x-2">
<RadioGroupItem value={User_Role.USER} id="user" />
<RadioGroupItem value={String(User_Role.USER)} id="user" />
<Label htmlFor="user">{t("setting.member-section.user")}</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value={User_Role.ADMIN} id="admin" />
<RadioGroupItem value={String(User_Role.ADMIN)} id="admin" />
<Label htmlFor="admin">{t("setting.member-section.admin")}</Label>
</div>
</RadioGroup>

View File

@ -1,3 +1,5 @@
import { create } from "@bufbuild/protobuf";
import { FieldMaskSchema } from "@bufbuild/protobuf/wkt";
import React, { useEffect, useState } from "react";
import { toast } from "react-hot-toast";
import { Button } from "@/components/ui/button";
@ -98,7 +100,7 @@ function CreateWebhookDialog({ open, onOpenChange, webhookName, onSuccess }: Pro
displayName: state.displayName,
url: state.url,
},
updateMask: ["display_name", "url"],
updateMask: create(FieldMaskSchema, { paths: ["display_name", "url"] }),
});
}
@ -107,7 +109,7 @@ function CreateWebhookDialog({ open, onOpenChange, webhookName, onSuccess }: Pro
requestState.setFinish();
} catch (error: any) {
console.error(error);
toast.error(error.details);
toast.error(error.message);
requestState.setError();
}
};

View File

@ -1,3 +1,4 @@
import { timestampDate } from "@bufbuild/protobuf/wkt";
import { CheckIcon, MessageCircleIcon, TrashIcon, XIcon } from "lucide-react";
import { observer } from "mobx-react-lite";
import { useState } from "react";
@ -9,8 +10,8 @@ import useNavigateTo from "@/hooks/useNavigateTo";
import { cn } from "@/lib/utils";
import { memoStore, userStore } from "@/store";
import { activityNamePrefix } from "@/store/common";
import { Memo } from "@/types/proto/api/v1/memo_service";
import { User, UserNotification, UserNotification_Status } from "@/types/proto/api/v1/user_service";
import { Memo } from "@/types/proto/api/v1/memo_service_pb";
import { User, UserNotification, UserNotification_Status } from "@/types/proto/api/v1/user_service_pb";
import { useTranslate } from "@/utils/i18n";
interface Props {
@ -36,8 +37,8 @@ const MemoCommentMessage = observer(({ notification }: Props) => {
name: `${activityNamePrefix}${notification.activityId}`,
});
if (activity.payload?.memoComment) {
const memoCommentPayload = activity.payload.memoComment;
if (activity.payload?.payload?.case === "memoComment") {
const memoCommentPayload = activity.payload.payload.value;
const memo = await memoStore.getOrFetchMemoByName(memoCommentPayload.relatedMemo, {
skipStore: true,
});
@ -160,8 +161,11 @@ const MemoCommentMessage = observer(({ notification }: Props) => {
<span className="font-semibold text-sm text-foreground/95">{sender?.displayName || sender?.username}</span>
<span className="text-sm text-muted-foreground/80">commented on your memo</span>
<span className="text-xs text-muted-foreground/60">
{notification.createTime?.toLocaleDateString([], { month: "short", day: "numeric" })} at{" "}
{notification.createTime?.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}
{notification.createTime &&
timestampDate(notification.createTime)?.toLocaleDateString([], { month: "short", day: "numeric" })}{" "}
at{" "}
{notification.createTime &&
timestampDate(notification.createTime)?.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}
</span>
</div>
<div className="flex items-center gap-1 shrink-0">

View File

@ -1,4 +1,4 @@
import { Memo } from "@/types/proto/api/v1/memo_service";
import { Memo } from "@/types/proto/api/v1/memo_service_pb";
import { DistributionResult } from "./types";
export function distributeItemsToColumns(

View File

@ -1,4 +1,4 @@
import { Memo } from "@/types/proto/api/v1/memo_service";
import { Memo } from "@/types/proto/api/v1/memo_service_pb";
export interface MemoRenderContext {
compact: boolean;

View File

@ -1,5 +1,5 @@
import { useCallback, useEffect, useRef, useState } from "react";
import { Memo } from "@/types/proto/api/v1/memo_service";
import { Memo } from "@/types/proto/api/v1/memo_service_pb";
import { MINIMUM_MEMO_VIEWPORT_WIDTH, REDISTRIBUTION_DEBOUNCE_MS } from "./constants";
import { distributeItemsToColumns } from "./distributeItems";

View File

@ -24,7 +24,7 @@ import {
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { State } from "@/types/proto/api/v1/common";
import { State } from "@/types/proto/api/v1/common_pb";
import { useTranslate } from "@/utils/i18n";
import { hasCompletedTasks } from "@/utils/markdown-manipulation";
import { useMemoActionHandlers } from "./hooks";

View File

@ -4,8 +4,8 @@ import toast from "react-hot-toast";
import { useLocation } from "react-router-dom";
import useNavigateTo from "@/hooks/useNavigateTo";
import { instanceStore, memoStore, userStore } from "@/store";
import { State } from "@/types/proto/api/v1/common";
import type { Memo } from "@/types/proto/api/v1/memo_service";
import { State } from "@/types/proto/api/v1/common_pb";
import type { Memo } from "@/types/proto/api/v1/memo_service_pb";
import { useTranslate } from "@/utils/i18n";
import { removeCompletedTasks } from "@/utils/markdown-manipulation";

View File

@ -1,4 +1,4 @@
import type { Memo } from "@/types/proto/api/v1/memo_service";
import type { Memo } from "@/types/proto/api/v1/memo_service_pb";
export interface MemoActionMenuProps {
memo: Memo;

View File

@ -1,4 +1,4 @@
import { Attachment } from "@/types/proto/api/v1/attachment_service";
import { Attachment } from "@/types/proto/api/v1/attachment_service_pb";
import { getAttachmentUrl, isMidiFile } from "@/utils/attachment";
import AttachmentIcon from "./AttachmentIcon";

View File

@ -1,7 +1,8 @@
import { create } from "@bufbuild/protobuf";
import { isEqual } from "lodash-es";
import { CheckCircleIcon, Code2Icon, HashIcon, LinkIcon } from "lucide-react";
import { cn } from "@/lib/utils";
import { Memo, Memo_Property, MemoRelation_Type } from "@/types/proto/api/v1/memo_service";
import { Memo, Memo_Property, Memo_PropertySchema, MemoRelation_Type } from "@/types/proto/api/v1/memo_service_pb";
import { useTranslate } from "@/utils/i18n";
import MemoRelationForceGraph from "../MemoRelationForceGraph";
@ -13,7 +14,7 @@ interface Props {
const MemoDetailSidebar = ({ memo, className, parentPage }: Props) => {
const t = useTranslate();
const property = Memo_Property.fromPartial(memo.property || {});
const property = create(Memo_PropertySchema, memo.property || {});
const hasSpecialProperty = property.hasLink || property.hasTaskList || property.hasCode || property.hasIncompleteTasks;
const shouldShowRelationGraph = memo.relations.filter((r) => r.type === MemoRelation_Type.REFERENCE).length > 0;

View File

@ -3,7 +3,7 @@ import { useEffect, useState } from "react";
import { useLocation } from "react-router-dom";
import { Button } from "@/components/ui/button";
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
import { Memo } from "@/types/proto/api/v1/memo_service";
import { Memo } from "@/types/proto/api/v1/memo_service_pb";
import MemoDetailSidebar from "./MemoDetailSidebar";
interface Props {

View File

@ -14,7 +14,7 @@ import {
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import type { Location, MemoRelation } from "@/types/proto/api/v1/memo_service";
import type { Location, MemoRelation } from "@/types/proto/api/v1/memo_service_pb";
import { useTranslate } from "@/utils/i18n";
import { LinkMemoDialog, LocationDialog } from "../components";
import { GEOCODING } from "../constants";

View File

@ -1,7 +1,7 @@
import { CheckIcon, ChevronDownIcon } from "lucide-react";
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
import VisibilityIcon from "@/components/VisibilityIcon";
import { Visibility } from "@/types/proto/api/v1/memo_service";
import { Visibility } from "@/types/proto/api/v1/memo_service_pb";
import { useTranslate } from "@/utils/i18n";
interface Props {

View File

@ -1,6 +1,6 @@
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Memo } from "@/types/proto/api/v1/memo_service";
import { Memo } from "@/types/proto/api/v1/memo_service_pb";
import { useTranslate } from "@/utils/i18n";
function highlightSearchText(content: string, searchText: string): React.ReactNode {

View File

@ -1,10 +1,18 @@
import { create } from "@bufbuild/protobuf";
import { useState } from "react";
import useDebounce from "react-use/lib/useDebounce";
import { memoServiceClient } from "@/grpcweb";
import { DEFAULT_LIST_MEMOS_PAGE_SIZE } from "@/helpers/consts";
import useCurrentUser from "@/hooks/useCurrentUser";
import { extractUserIdFromName } from "@/store/common";
import { Memo, MemoRelation, MemoRelation_Memo, MemoRelation_Type } from "@/types/proto/api/v1/memo_service";
import {
Memo,
MemoRelation,
MemoRelation_Memo,
MemoRelation_MemoSchema,
MemoRelation_Type,
MemoRelationSchema,
} from "@/types/proto/api/v1/memo_service_pb";
interface UseLinkMemoParams {
isOpen: boolean;
@ -49,9 +57,9 @@ export const useLinkMemo = ({ isOpen, currentMemoName, existingRelations, onAddR
);
const addMemoRelation = (memo: Memo) => {
const relation = MemoRelation.fromPartial({
const relation = create(MemoRelationSchema, {
type: MemoRelation_Type.REFERENCE,
relatedMemo: MemoRelation_Memo.fromPartial({
relatedMemo: create(MemoRelation_MemoSchema, {
name: memo.name,
snippet: memo.snippet,
}),

View File

@ -1,6 +1,7 @@
import { create } from "@bufbuild/protobuf";
import { LatLng } from "leaflet";
import { useState } from "react";
import { Location } from "@/types/proto/api/v1/memo_service";
import { Location, LocationSchema } from "@/types/proto/api/v1/memo_service_pb";
import { LocationState } from "../types/insert-menu";
export const useLocation = (initialLocation?: Location) => {
@ -62,7 +63,7 @@ export const useLocation = (initialLocation?: Location) => {
if (!state.position || !state.placeholder.trim()) {
return undefined;
}
return Location.fromPartial({
return create(LocationSchema, {
latitude: state.position.lat,
longitude: state.position.lng,
placeholder: state.placeholder,

View File

@ -1,9 +1,10 @@
import { timestampDate } from "@bufbuild/protobuf/wkt";
import { useEffect, useState } from "react";
import useAsyncEffect from "@/hooks/useAsyncEffect";
import { instanceStore, memoStore, userStore } from "@/store";
import type { Attachment } from "@/types/proto/api/v1/attachment_service";
import type { Location, MemoRelation } from "@/types/proto/api/v1/memo_service";
import { Visibility } from "@/types/proto/api/v1/memo_service";
import type { Attachment } from "@/types/proto/api/v1/attachment_service_pb";
import type { Location, MemoRelation } from "@/types/proto/api/v1/memo_service_pb";
import { Visibility } from "@/types/proto/api/v1/memo_service_pb";
import { convertVisibilityFromString } from "@/utils/memo";
import type { EditorRefActions } from "../Editor";
@ -68,7 +69,7 @@ export const useMemoEditorInit = (options: UseMemoEditorInitOptions): UseMemoEdi
const parentMemo = await memoStore.getOrFetchMemoByName(parentMemoName);
visibility = parentMemo.visibility;
}
onVisibilityChange(convertVisibilityFromString(visibility));
onVisibilityChange(visibility);
}, [parentMemoName, userGeneralSetting?.memoVisibility, instanceMemoRelatedSetting.disallowPublicVisibility]);
// Load existing memo if editing
@ -80,8 +81,8 @@ export const useMemoEditorInit = (options: UseMemoEditorInitOptions): UseMemoEdi
const memo = await memoStore.getOrFetchMemoByName(memoName);
if (memo) {
onEditorFocus();
setCreateTime(memo.createTime);
setUpdateTime(memo.updateTime);
setCreateTime(memo.createTime ? timestampDate(memo.createTime) : undefined);
setUpdateTime(memo.updateTime ? timestampDate(memo.updateTime) : undefined);
onVisibilityChange(memo.visibility);
onAttachmentsChange(memo.attachments);
onRelationsChange(memo.relations);

View File

@ -1,7 +1,7 @@
import { useCallback, useState } from "react";
import type { Attachment } from "@/types/proto/api/v1/attachment_service";
import type { Location, MemoRelation } from "@/types/proto/api/v1/memo_service";
import { Visibility } from "@/types/proto/api/v1/memo_service";
import type { Attachment } from "@/types/proto/api/v1/attachment_service_pb";
import type { Location, MemoRelation } from "@/types/proto/api/v1/memo_service_pb";
import { Visibility } from "@/types/proto/api/v1/memo_service_pb";
interface MemoEditorState {
memoVisibility: Visibility;

View File

@ -1,11 +1,14 @@
import { create } from "@bufbuild/protobuf";
import { timestampDate, timestampFromDate } from "@bufbuild/protobuf/wkt";
import { isEqual } from "lodash-es";
import { useCallback } from "react";
import { toast } from "react-hot-toast";
import type { LocalFile } from "@/components/memo-metadata";
import { memoServiceClient } from "@/grpcweb";
import { attachmentStore, memoStore } from "@/store";
import { Attachment } from "@/types/proto/api/v1/attachment_service";
import type { Location, Memo, MemoRelation, Visibility } from "@/types/proto/api/v1/memo_service";
import { Attachment, AttachmentSchema } from "@/types/proto/api/v1/attachment_service_pb";
import type { Location, Memo, MemoRelation, Visibility } from "@/types/proto/api/v1/memo_service_pb";
import { MemoSchema } from "@/types/proto/api/v1/memo_service_pb";
import type { Translations } from "@/utils/i18n";
interface MemoSaveContext {
@ -37,15 +40,14 @@ async function uploadLocalFiles(localFiles: LocalFile[], onUploadingChange: (upl
const attachments: Attachment[] = [];
for (const { file } of localFiles) {
const buffer = new Uint8Array(await file.arrayBuffer());
const attachment = await attachmentStore.createAttachment({
attachment: Attachment.fromPartial({
const attachment = await attachmentStore.createAttachment(
create(AttachmentSchema, {
filename: file.name,
size: file.size,
size: BigInt(file.size),
type: file.type,
content: buffer,
}),
attachmentId: "",
});
);
attachments.push(attachment);
}
return attachments;
@ -93,13 +95,13 @@ function buildUpdateMask(
}
// Handle custom timestamps
if (context.createTime && !isEqual(context.createTime, prevMemo.createTime)) {
if (context.createTime && !isEqual(context.createTime, prevMemo.createTime ? timestampDate(prevMemo.createTime) : undefined)) {
mask.add("create_time");
patch.createTime = context.createTime;
patch.createTime = timestampFromDate(context.createTime);
}
if (context.updateTime && !isEqual(context.updateTime, prevMemo.updateTime)) {
if (context.updateTime && !isEqual(context.updateTime, prevMemo.updateTime ? timestampDate(prevMemo.updateTime) : undefined)) {
mask.add("update_time");
patch.updateTime = context.updateTime;
patch.updateTime = timestampFromDate(context.updateTime);
}
return { mask, patch };
@ -137,24 +139,23 @@ export function useMemoSave(callbacks: MemoSaveCallbacks) {
const memo = context.parentMemoName
? await memoServiceClient.createMemoComment({
name: context.parentMemoName,
comment: {
comment: create(MemoSchema, {
content,
visibility: context.visibility,
attachments: context.attachmentList,
relations: context.relationList,
location: context.location,
},
}),
})
: await memoStore.createMemo({
memo: {
: await memoStore.createMemo(
create(MemoSchema, {
content,
visibility: context.visibility,
attachments: allAttachments,
relations: context.relationList,
location: context.location,
} as Memo,
memoId: "",
});
}),
);
onSuccess(memo.name);
}

View File

@ -10,7 +10,7 @@ import { Button } from "@/components/ui/button";
import useCurrentUser from "@/hooks/useCurrentUser";
import { cn } from "@/lib/utils";
import { extractMemoIdFromName } from "@/store/common";
import { MemoRelation_Type } from "@/types/proto/api/v1/memo_service";
import { MemoRelation_Type } from "@/types/proto/api/v1/memo_service_pb";
import { useTranslate } from "@/utils/i18n";
import DateTimeInput from "../DateTimeInput";
import { AttachmentList, LocationDisplay, RelationList } from "../memo-metadata";

View File

@ -1,6 +1,6 @@
import { createContext } from "react";
import type { Attachment } from "@/types/proto/api/v1/attachment_service";
import type { MemoRelation } from "@/types/proto/api/v1/memo_service";
import type { Attachment } from "@/types/proto/api/v1/attachment_service_pb";
import type { MemoRelation } from "@/types/proto/api/v1/memo_service_pb";
import type { LocalFile } from "../../memo-metadata";
export interface MemoEditorContextValue {

View File

@ -9,7 +9,7 @@ import useAsyncEffect from "@/hooks/useAsyncEffect";
import { cn } from "@/lib/utils";
import { userStore } from "@/store";
import memoFilterStore from "@/store/memoFilter";
import { Shortcut } from "@/types/proto/api/v1/shortcut_service";
import { Shortcut } from "@/types/proto/api/v1/shortcut_service_pb";
import { useTranslate } from "@/utils/i18n";
import CreateShortcutDialog from "../CreateShortcutDialog";
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "../ui/dropdown-menu";

View File

@ -1,8 +1,8 @@
import { observer } from "mobx-react-lite";
import { memo } from "react";
import useCurrentUser from "@/hooks/useCurrentUser";
import { State } from "@/types/proto/api/v1/common";
import type { Memo, Reaction } from "@/types/proto/api/v1/memo_service";
import { State } from "@/types/proto/api/v1/common_pb";
import type { Memo, Reaction } from "@/types/proto/api/v1/memo_service_pb";
import { useReactionGroups } from "./hooks";
import ReactionSelector from "./ReactionSelector";
import ReactionView from "./ReactionView";

View File

@ -4,7 +4,7 @@ import { useState } from "react";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { cn } from "@/lib/utils";
import { instanceStore } from "@/store";
import type { Memo } from "@/types/proto/api/v1/memo_service";
import type { Memo } from "@/types/proto/api/v1/memo_service_pb";
import { useReactionActions } from "./hooks";
interface Props {

View File

@ -2,9 +2,9 @@ import { observer } from "mobx-react-lite";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import useCurrentUser from "@/hooks/useCurrentUser";
import { cn } from "@/lib/utils";
import { State } from "@/types/proto/api/v1/common";
import type { Memo } from "@/types/proto/api/v1/memo_service";
import type { User } from "@/types/proto/api/v1/user_service";
import { State } from "@/types/proto/api/v1/common_pb";
import type { Memo } from "@/types/proto/api/v1/memo_service_pb";
import type { User } from "@/types/proto/api/v1/user_service_pb";
import { formatReactionTooltip, useReactionActions } from "./hooks";
interface Props {

View File

@ -3,8 +3,8 @@ import { useEffect, useState } from "react";
import { memoServiceClient } from "@/grpcweb";
import useCurrentUser from "@/hooks/useCurrentUser";
import { memoStore, userStore } from "@/store";
import type { Memo, Reaction } from "@/types/proto/api/v1/memo_service";
import type { User } from "@/types/proto/api/v1/user_service";
import type { Memo, Reaction } from "@/types/proto/api/v1/memo_service_pb";
import type { User } from "@/types/proto/api/v1/user_service_pb";
export type ReactionGroup = Map<string, User[]>;

View File

@ -3,7 +3,7 @@ import ForceGraph2D, { ForceGraphMethods, LinkObject, NodeObject } from "react-f
import useNavigateTo from "@/hooks/useNavigateTo";
import { cn } from "@/lib/utils";
import { extractMemoIdFromName } from "@/store/common";
import { Memo, MemoRelation_Type } from "@/types/proto/api/v1/memo_service";
import { Memo, MemoRelation_Type } from "@/types/proto/api/v1/memo_service_pb";
import { LinkType, NodeType } from "./types";
import { convertMemoRelationsToGraphData } from "./utils";

View File

@ -1,4 +1,4 @@
import { MemoRelation_Memo } from "@/types/proto/api/v1/memo_service";
import { MemoRelation_Memo } from "@/types/proto/api/v1/memo_service_pb";
export interface NodeType {
memo: MemoRelation_Memo;

View File

@ -1,5 +1,5 @@
import { GraphData, LinkObject, NodeObject } from "react-force-graph-2d";
import { MemoRelation, MemoRelation_Memo } from "@/types/proto/api/v1/memo_service";
import { MemoRelation, MemoRelation_Memo } from "@/types/proto/api/v1/memo_service_pb";
import { LinkType, NodeType } from "./types";
export const convertMemoRelationsToGraphData = (memoRelations: MemoRelation[]): GraphData<NodeType, LinkType> => {

View File

@ -1,7 +1,7 @@
import { observer } from "mobx-react-lite";
import { memo, useMemo, useRef, useState } from "react";
import { cn } from "@/lib/utils";
import type { Memo } from "@/types/proto/api/v1/memo_service";
import type { Memo } from "@/types/proto/api/v1/memo_service_pb";
import MemoEditor from "../MemoEditor";
import PreviewImageDialog from "../PreviewImageDialog";
import { MemoBody, MemoHeader } from "./components";

View File

@ -1,6 +1,6 @@
import { createContext, useContext } from "react";
import type { Memo } from "@/types/proto/api/v1/memo_service";
import type { User } from "@/types/proto/api/v1/user_service";
import type { Memo } from "@/types/proto/api/v1/memo_service_pb";
import type { User } from "@/types/proto/api/v1/user_service_pb";
export interface MemoViewContextValue {
memo: Memo;

View File

@ -1,5 +1,5 @@
import { cn } from "@/lib/utils";
import { MemoRelation_Type } from "@/types/proto/api/v1/memo_service";
import { MemoRelation_Type } from "@/types/proto/api/v1/memo_service_pb";
import { useTranslate } from "@/utils/i18n";
import MemoContent from "../../MemoContent";
import { MemoReactionListView } from "../../MemoReactionListView";

View File

@ -1,10 +1,11 @@
import { timestampDate } from "@bufbuild/protobuf/wkt";
import { BookmarkIcon, EyeOffIcon, MessageCircleMoreIcon } from "lucide-react";
import { Link } from "react-router-dom";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import i18n from "@/i18n";
import { cn } from "@/lib/utils";
import { Visibility } from "@/types/proto/api/v1/memo_service";
import type { User } from "@/types/proto/api/v1/user_service";
import { Visibility } from "@/types/proto/api/v1/memo_service_pb";
import type { User } from "@/types/proto/api/v1/user_service_pb";
import { useTranslate } from "@/utils/i18n";
import { convertVisibilityToString } from "@/utils/memo";
import MemoActionMenu from "../../MemoActionMenu";
@ -43,9 +44,13 @@ const MemoHeader: React.FC<Props> = ({
useMemoViewContext();
const displayTime = isArchived ? (
memo.displayTime?.toLocaleString(i18n.language)
(memo.displayTime ? timestampDate(memo.displayTime) : undefined)?.toLocaleString(i18n.language)
) : (
<relative-time datetime={memo.displayTime?.toISOString()} lang={i18n.language} format={relativeTimeFormat}></relative-time>
<relative-time
datetime={(memo.displayTime ? timestampDate(memo.displayTime) : undefined)?.toISOString()}
lang={i18n.language}
format={relativeTimeFormat}
></relative-time>
);
return (

View File

@ -1,8 +1,9 @@
import { timestampDate } from "@bufbuild/protobuf/wkt";
import { useLocation } from "react-router-dom";
import useCurrentUser from "@/hooks/useCurrentUser";
import { State } from "@/types/proto/api/v1/common";
import type { Memo } from "@/types/proto/api/v1/memo_service";
import { MemoRelation_Type } from "@/types/proto/api/v1/memo_service";
import { State } from "@/types/proto/api/v1/common_pb";
import type { Memo } from "@/types/proto/api/v1/memo_service_pb";
import { MemoRelation_Type } from "@/types/proto/api/v1/memo_service_pb";
import { isSuperUser } from "@/utils/user";
import { RELATIVE_TIME_THRESHOLD_MS } from "../constants";
@ -14,8 +15,9 @@ export const useMemoViewDerivedState = (memo: Memo, parentPageProp?: string) =>
(relation) => relation.type === MemoRelation_Type.COMMENT && relation.relatedMemo?.name === memo.name,
).length;
const displayTime = memo.displayTime ? timestampDate(memo.displayTime) : undefined;
const relativeTimeFormat: "datetime" | "auto" =
memo.displayTime && Date.now() - memo.displayTime.getTime() > RELATIVE_TIME_THRESHOLD_MS ? "datetime" : "auto";
displayTime && Date.now() - displayTime.getTime() > RELATIVE_TIME_THRESHOLD_MS ? "datetime" : "auto";
const isArchived = memo.state === State.ARCHIVED;
const readonly = memo.creator !== user?.name && !isSuperUser(user);

View File

@ -1,8 +1,8 @@
import { useEffect, useRef, useState } from "react";
import toast from "react-hot-toast";
import { instanceStore, memoStore, userStore } from "@/store";
import { State } from "@/types/proto/api/v1/common";
import type { Memo } from "@/types/proto/api/v1/memo_service";
import { State } from "@/types/proto/api/v1/common_pb";
import type { Memo } from "@/types/proto/api/v1/memo_service_pb";
import { useTranslate } from "@/utils/i18n";
import { KEYBOARD_SHORTCUTS, TEXT_INPUT_TYPES } from "../constants";

View File

@ -1,5 +1,5 @@
import type { Memo } from "@/types/proto/api/v1/memo_service";
import type { User } from "@/types/proto/api/v1/user_service";
import type { Memo } from "@/types/proto/api/v1/memo_service_pb";
import type { User } from "@/types/proto/api/v1/user_service_pb";
export interface MemoViewProps {
memo: Memo;

View File

@ -7,6 +7,7 @@ import useCurrentUser from "@/hooks/useCurrentUser";
import { cn } from "@/lib/utils";
import { Routes } from "@/router";
import { userStore } from "@/store";
import { UserNotification_Status } from "@/types/proto/api/v1/user_service_pb";
import { useTranslate } from "@/utils/i18n";
import MemosLogo from "./MemosLogo";
import UserMenu from "./UserMenu";
@ -54,7 +55,7 @@ const Navigation = observer((props: Props) => {
title: t("common.attachments"),
icon: <PaperclipIcon className="w-6 h-auto shrink-0" />,
};
const unreadCount = userStore.state.notifications.filter((n) => n.status === "UNREAD").length;
const unreadCount = userStore.state.notifications.filter((n) => n.status === UserNotification_Status.UNREAD).length;
const inboxNavLink: NavLinkItem = {
id: "header-inbox",
path: Routes.INBOX,

View File

@ -8,8 +8,8 @@ import { DEFAULT_LIST_MEMOS_PAGE_SIZE } from "@/helpers/consts";
import useResponsiveWidth from "@/hooks/useResponsiveWidth";
import { Routes } from "@/router";
import { memoStore, userStore, viewStore } from "@/store";
import { State } from "@/types/proto/api/v1/common";
import type { Memo } from "@/types/proto/api/v1/memo_service";
import { State } from "@/types/proto/api/v1/common_pb";
import type { Memo } from "@/types/proto/api/v1/memo_service_pb";
import { useTranslate } from "@/utils/i18n";
import Empty from "../Empty";
import type { MemoRenderContext } from "../MasonryView";

View File

@ -1,6 +1,6 @@
import { ConnectError } from "@connectrpc/connect";
import { LoaderIcon } from "lucide-react";
import { observer } from "mobx-react-lite";
import { ClientError } from "nice-grpc-web";
import { useState } from "react";
import { toast } from "react-hot-toast";
import { Button } from "@/components/ui/button";
@ -46,13 +46,16 @@ const PasswordSignInForm = observer(() => {
try {
actionBtnLoadingState.setLoading();
await authServiceClient.createSession({
passwordCredentials: { username, password },
credentials: {
case: "passwordCredentials",
value: { username, password },
},
});
await initialUserStore();
navigateTo("/");
} catch (error: any) {
console.error(error);
toast.error((error as ClientError).details || "Failed to sign in.");
toast.error((error as ConnectError).message || "Failed to sign in.");
}
actionBtnLoadingState.setFinish();
};

View File

@ -1,3 +1,4 @@
import { timestampDate } from "@bufbuild/protobuf/wkt";
import copy from "copy-to-clipboard";
import { ClipboardIcon, PlusIcon, TrashIcon } from "lucide-react";
import { useEffect, useState } from "react";
@ -7,14 +8,18 @@ import { Button } from "@/components/ui/button";
import { userServiceClient } from "@/grpcweb";
import useCurrentUser from "@/hooks/useCurrentUser";
import { useDialog } from "@/hooks/useDialog";
import { UserAccessToken } from "@/types/proto/api/v1/user_service";
import { UserAccessToken } from "@/types/proto/api/v1/user_service_pb";
import { useTranslate } from "@/utils/i18n";
import CreateAccessTokenDialog from "../CreateAccessTokenDialog";
import SettingTable from "./SettingTable";
const listAccessTokens = async (parent: string) => {
const { accessTokens } = await userServiceClient.listUserAccessTokens({ parent });
return accessTokens.sort((a, b) => (b.issuedAt?.getTime() ?? 0) - (a.issuedAt?.getTime() ?? 0));
return accessTokens.sort(
(a, b) =>
((b.issuedAt ? timestampDate(b.issuedAt) : undefined)?.getTime() ?? 0) -
((a.issuedAt ? timestampDate(a.issuedAt) : undefined)?.getTime() ?? 0),
);
};
const AccessTokenSection = () => {
@ -98,13 +103,14 @@ const AccessTokenSection = () => {
{
key: "issuedAt",
header: t("setting.access-token-section.create-dialog.created-at"),
render: (_, token: UserAccessToken) => token.issuedAt?.toLocaleString(),
render: (_, token: UserAccessToken) => (token.issuedAt ? timestampDate(token.issuedAt) : undefined)?.toLocaleString(),
},
{
key: "expiresAt",
header: t("setting.access-token-section.create-dialog.expires-at"),
render: (_, token: UserAccessToken) =>
token.expiresAt?.toLocaleString() ?? t("setting.access-token-section.create-dialog.duration-never"),
(token.expiresAt ? timestampDate(token.expiresAt) : undefined)?.toLocaleString() ??
t("setting.access-token-section.create-dialog.duration-never"),
},
{
key: "actions",

View File

@ -1,3 +1,4 @@
import { create } from "@bufbuild/protobuf";
import { isEqual } from "lodash-es";
import { observer } from "mobx-react-lite";
import { useEffect, useState } from "react";
@ -10,19 +11,33 @@ import { identityProviderServiceClient } from "@/grpcweb";
import useDialog from "@/hooks/useDialog";
import { instanceStore } from "@/store";
import { instanceSettingNamePrefix } from "@/store/common";
import { IdentityProvider } from "@/types/proto/api/v1/idp_service";
import { InstanceSetting_GeneralSetting, InstanceSetting_Key } from "@/types/proto/api/v1/instance_service";
import { IdentityProvider } from "@/types/proto/api/v1/idp_service_pb";
import {
InstanceSetting_GeneralSetting,
InstanceSetting_GeneralSettingSchema,
InstanceSetting_Key,
InstanceSettingSchema,
} from "@/types/proto/api/v1/instance_service_pb";
import { useTranslate } from "@/utils/i18n";
import UpdateCustomizedProfileDialog from "../UpdateCustomizedProfileDialog";
import SettingGroup from "./SettingGroup";
import SettingRow from "./SettingRow";
import SettingSection from "./SettingSection";
// Helper to extract general setting value from InstanceSetting oneof
function getGeneralSetting(setting: any): InstanceSetting_GeneralSetting | undefined {
if (setting?.value?.case === "generalSetting") {
return setting.value.value;
}
return undefined;
}
const InstanceSection = observer(() => {
const t = useTranslate();
const customizeDialog = useDialog();
const originalSetting = InstanceSetting_GeneralSetting.fromPartial(
instanceStore.getInstanceSettingByKey(InstanceSetting_Key.GENERAL)?.generalSetting || {},
const originalSetting = create(
InstanceSetting_GeneralSettingSchema,
getGeneralSetting(instanceStore.getInstanceSettingByKey(InstanceSetting_Key.GENERAL)) || {},
);
const [instanceGeneralSetting, setInstanceGeneralSetting] = useState<InstanceSetting_GeneralSetting>(originalSetting);
const [identityProviderList, setIdentityProviderList] = useState<IdentityProvider[]>([]);
@ -37,7 +52,7 @@ const InstanceSection = observer(() => {
const updatePartialSetting = (partial: Partial<InstanceSetting_GeneralSetting>) => {
setInstanceGeneralSetting(
InstanceSetting_GeneralSetting.fromPartial({
create(InstanceSetting_GeneralSettingSchema, {
...instanceGeneralSetting,
...partial,
}),
@ -46,12 +61,17 @@ const InstanceSection = observer(() => {
const handleSaveGeneralSetting = async () => {
try {
await instanceStore.upsertInstanceSetting({
name: `${instanceSettingNamePrefix}${InstanceSetting_Key.GENERAL}`,
generalSetting: instanceGeneralSetting,
});
await instanceStore.upsertInstanceSetting(
create(InstanceSettingSchema, {
name: `${instanceSettingNamePrefix}${InstanceSetting_Key.GENERAL}`,
value: {
case: "generalSetting",
value: instanceGeneralSetting,
},
}),
);
} catch (error: any) {
toast.error(error.details);
toast.error(error.message);
console.error(error);
return;
}

View File

@ -1,3 +1,5 @@
import { create } from "@bufbuild/protobuf";
import { FieldMaskSchema } from "@bufbuild/protobuf/wkt";
import { sortBy } from "lodash-es";
import { MoreVerticalIcon, PlusIcon } from "lucide-react";
import { observer } from "mobx-react-lite";
@ -9,8 +11,8 @@ import { userServiceClient } from "@/grpcweb";
import useCurrentUser from "@/hooks/useCurrentUser";
import { useDialog } from "@/hooks/useDialog";
import { userStore } from "@/store";
import { State } from "@/types/proto/api/v1/common";
import { User, User_Role } from "@/types/proto/api/v1/user_service";
import { State } from "@/types/proto/api/v1/common_pb";
import { User, User_Role } from "@/types/proto/api/v1/user_service_pb";
import { useTranslate } from "@/utils/i18n";
import CreateUserDialog from "../CreateUserDialog";
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "../ui/dropdown-menu";
@ -69,7 +71,7 @@ const MemberSection = observer(() => {
name: archiveTarget.name,
state: State.ARCHIVED,
},
updateMask: ["state"],
updateMask: create(FieldMaskSchema, { paths: ["state"] }),
});
setArchiveTarget(undefined);
toast.success(t("setting.member-section.archive-success", { username }));
@ -83,7 +85,7 @@ const MemberSection = observer(() => {
name: user.name,
state: State.NORMAL,
},
updateMask: ["state"],
updateMask: create(FieldMaskSchema, { paths: ["state"] }),
});
toast.success(t("setting.member-section.restore-success", { username }));
await fetchUsers();

View File

@ -1,3 +1,4 @@
import { create } from "@bufbuild/protobuf";
import { isEqual, uniq } from "lodash-es";
import { CheckIcon, X } from "lucide-react";
import { observer } from "mobx-react-lite";
@ -9,7 +10,12 @@ import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import { instanceStore } from "@/store";
import { instanceSettingNamePrefix } from "@/store/common";
import { InstanceSetting_Key, InstanceSetting_MemoRelatedSetting } from "@/types/proto/api/v1/instance_service";
import {
InstanceSetting_Key,
InstanceSetting_MemoRelatedSetting,
InstanceSetting_MemoRelatedSettingSchema,
InstanceSettingSchema,
} from "@/types/proto/api/v1/instance_service_pb";
import { useTranslate } from "@/utils/i18n";
import SettingGroup from "./SettingGroup";
import SettingRow from "./SettingRow";
@ -23,7 +29,7 @@ const MemoRelatedSettings = observer(() => {
const [editingNsfwTag, setEditingNsfwTag] = useState<string>("");
const updatePartialSetting = (partial: Partial<InstanceSetting_MemoRelatedSetting>) => {
const newInstanceMemoRelatedSetting = InstanceSetting_MemoRelatedSetting.fromPartial({
const newInstanceMemoRelatedSetting = create(InstanceSetting_MemoRelatedSettingSchema, {
...memoRelatedSetting,
...partial,
});
@ -55,14 +61,19 @@ const MemoRelatedSettings = observer(() => {
}
try {
await instanceStore.upsertInstanceSetting({
name: `${instanceSettingNamePrefix}${InstanceSetting_Key.MEMO_RELATED}`,
memoRelatedSetting,
});
await instanceStore.upsertInstanceSetting(
create(InstanceSettingSchema, {
name: `${instanceSettingNamePrefix}${InstanceSetting_Key.MEMO_RELATED}`,
value: {
case: "memoRelatedSetting",
value: memoRelatedSetting,
},
}),
);
setOriginalSetting(memoRelatedSetting);
toast.success(t("message.update-succeed"));
} catch (error: any) {
toast.error(error.details);
toast.error(error.message);
console.error(error);
}
};

View File

@ -1,8 +1,9 @@
import { create } from "@bufbuild/protobuf";
import { observer } from "mobx-react-lite";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { userStore } from "@/store";
import { Visibility } from "@/types/proto/api/v1/memo_service";
import { UserSetting_GeneralSetting } from "@/types/proto/api/v1/user_service";
import { Visibility } from "@/types/proto/api/v1/memo_service_pb";
import { UserSetting_GeneralSetting, UserSetting_GeneralSettingSchema } from "@/types/proto/api/v1/user_service_pb";
import { loadLocale, useTranslate } from "@/utils/i18n";
import { convertVisibilityFromString, convertVisibilityToString } from "@/utils/memo";
import { loadTheme } from "@/utils/theme";
@ -37,11 +38,13 @@ const PreferencesSection = observer(() => {
};
// Provide default values if setting is not loaded yet
const setting: UserSetting_GeneralSetting = generalSetting || {
locale: "en",
memoVisibility: "PRIVATE",
theme: "system",
};
const setting: UserSetting_GeneralSetting =
generalSetting ||
create(UserSetting_GeneralSettingSchema, {
locale: "en",
memoVisibility: "PRIVATE",
theme: "system",
});
return (
<SettingSection>

View File

@ -5,7 +5,7 @@ import ConfirmDialog from "@/components/ConfirmDialog";
import { Button } from "@/components/ui/button";
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
import { identityProviderServiceClient } from "@/grpcweb";
import { IdentityProvider } from "@/types/proto/api/v1/idp_service";
import { IdentityProvider } from "@/types/proto/api/v1/idp_service_pb";
import { useTranslate } from "@/utils/i18n";
import CreateIdentityProviderDialog from "../CreateIdentityProviderDialog";
import LearnMore from "../LearnMore";
@ -38,7 +38,7 @@ const SSOSection = () => {
await identityProviderServiceClient.deleteIdentityProvider({ name: deleteTarget.name });
} catch (error: any) {
console.error(error);
toast.error(error.details);
toast.error(error.message);
}
await fetchIdentityProviderList();
setDeleteTarget(undefined);

View File

@ -1,3 +1,4 @@
import { create } from "@bufbuild/protobuf";
import { isEqual } from "lodash-es";
import { observer } from "mobx-react-lite";
import React, { useEffect, useMemo, useState } from "react";
@ -13,22 +14,39 @@ import {
InstanceSetting_Key,
InstanceSetting_StorageSetting,
InstanceSetting_StorageSetting_S3Config,
InstanceSetting_StorageSetting_S3ConfigSchema,
InstanceSetting_StorageSetting_StorageType,
} from "@/types/proto/api/v1/instance_service";
InstanceSetting_StorageSettingSchema,
InstanceSettingSchema,
} from "@/types/proto/api/v1/instance_service_pb";
import { useTranslate } from "@/utils/i18n";
import SettingGroup from "./SettingGroup";
import SettingRow from "./SettingRow";
import SettingSection from "./SettingSection";
// Helper to extract storage setting value from InstanceSetting oneof
function getStorageSetting(setting: any): InstanceSetting_StorageSetting | undefined {
if (setting?.value?.case === "storageSetting") {
return setting.value.value;
}
return undefined;
}
const StorageSection = observer(() => {
const t = useTranslate();
const [instanceStorageSetting, setInstanceStorageSetting] = useState<InstanceSetting_StorageSetting>(
InstanceSetting_StorageSetting.fromPartial(instanceStore.getInstanceSettingByKey(InstanceSetting_Key.STORAGE)?.storageSetting || {}),
create(
InstanceSetting_StorageSettingSchema,
getStorageSetting(instanceStore.getInstanceSettingByKey(InstanceSetting_Key.STORAGE)) || {},
),
);
useEffect(() => {
setInstanceStorageSetting(
InstanceSetting_StorageSetting.fromPartial(instanceStore.getInstanceSettingByKey(InstanceSetting_Key.STORAGE)?.storageSetting || {}),
create(
InstanceSetting_StorageSettingSchema,
getStorageSetting(instanceStore.getInstanceSettingByKey(InstanceSetting_Key.STORAGE)) || {},
),
);
}, [instanceStore.getInstanceSettingByKey(InstanceSetting_Key.STORAGE)]);
@ -37,8 +55,9 @@ const StorageSection = observer(() => {
return false;
}
const origin = InstanceSetting_StorageSetting.fromPartial(
instanceStore.getInstanceSettingByKey(InstanceSetting_Key.STORAGE)?.storageSetting || {},
const origin = create(
InstanceSetting_StorageSettingSchema,
getStorageSetting(instanceStore.getInstanceSettingByKey(InstanceSetting_Key.STORAGE)) || {},
);
if (instanceStorageSetting.storageType === InstanceSetting_StorageSetting_StorageType.LOCAL) {
if (instanceStorageSetting.filepathTemplate.length === 0) {
@ -63,29 +82,38 @@ const StorageSection = observer(() => {
if (Number.isNaN(num)) {
num = 0;
}
const update: InstanceSetting_StorageSetting = {
const update = create(InstanceSetting_StorageSettingSchema, {
...instanceStorageSetting,
uploadSizeLimitMb: num,
};
uploadSizeLimitMb: BigInt(num),
});
setInstanceStorageSetting(update);
};
const handleFilepathTemplateChanged = async (event: React.FocusEvent<HTMLInputElement>) => {
const update: InstanceSetting_StorageSetting = {
const update = create(InstanceSetting_StorageSettingSchema, {
...instanceStorageSetting,
filepathTemplate: event.target.value,
};
});
setInstanceStorageSetting(update);
};
const handlePartialS3ConfigChanged = async (s3Config: Partial<InstanceSetting_StorageSetting_S3Config>) => {
const update: InstanceSetting_StorageSetting = {
...instanceStorageSetting,
s3Config: InstanceSetting_StorageSetting_S3Config.fromPartial({
...instanceStorageSetting.s3Config,
...s3Config,
}),
const existingS3Config = instanceStorageSetting.s3Config;
const s3ConfigInit = {
accessKeyId: existingS3Config?.accessKeyId ?? "",
accessKeySecret: existingS3Config?.accessKeySecret ?? "",
endpoint: existingS3Config?.endpoint ?? "",
region: existingS3Config?.region ?? "",
bucket: existingS3Config?.bucket ?? "",
usePathStyle: existingS3Config?.usePathStyle ?? false,
...s3Config,
};
const update = create(InstanceSetting_StorageSettingSchema, {
storageType: instanceStorageSetting.storageType,
filepathTemplate: instanceStorageSetting.filepathTemplate,
uploadSizeLimitMb: instanceStorageSetting.uploadSizeLimitMb,
s3Config: create(InstanceSetting_StorageSetting_S3ConfigSchema, s3ConfigInit),
});
setInstanceStorageSetting(update);
};
@ -116,18 +144,23 @@ const StorageSection = observer(() => {
};
const handleStorageTypeChanged = async (storageType: InstanceSetting_StorageSetting_StorageType) => {
const update: InstanceSetting_StorageSetting = {
const update = create(InstanceSetting_StorageSettingSchema, {
...instanceStorageSetting,
storageType: storageType,
};
});
setInstanceStorageSetting(update);
};
const saveInstanceStorageSetting = async () => {
await instanceStore.upsertInstanceSetting({
name: `${instanceSettingNamePrefix}${InstanceSetting_Key.STORAGE}`,
storageSetting: instanceStorageSetting,
});
await instanceStore.upsertInstanceSetting(
create(InstanceSettingSchema, {
name: `${instanceSettingNamePrefix}${InstanceSetting_Key.STORAGE}`,
value: {
case: "storageSetting",
value: instanceStorageSetting,
},
}),
);
toast.success("Updated");
};
@ -136,29 +169,33 @@ const StorageSection = observer(() => {
<SettingGroup title={t("setting.storage-section.current-storage")}>
<div className="w-full">
<RadioGroup
value={instanceStorageSetting.storageType}
value={String(instanceStorageSetting.storageType)}
onValueChange={(value) => {
handleStorageTypeChanged(value as InstanceSetting_StorageSetting_StorageType);
handleStorageTypeChanged(Number(value) as InstanceSetting_StorageSetting_StorageType);
}}
className="flex flex-row gap-4"
>
<div className="flex items-center space-x-2">
<RadioGroupItem value={InstanceSetting_StorageSetting_StorageType.DATABASE} id="database" />
<RadioGroupItem value={String(InstanceSetting_StorageSetting_StorageType.DATABASE)} id="database" />
<Label htmlFor="database">{t("setting.storage-section.type-database")}</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value={InstanceSetting_StorageSetting_StorageType.LOCAL} id="local" />
<RadioGroupItem value={String(InstanceSetting_StorageSetting_StorageType.LOCAL)} id="local" />
<Label htmlFor="local">{t("setting.storage-section.type-local")}</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value={InstanceSetting_StorageSetting_StorageType.S3} id="s3" />
<RadioGroupItem value={String(InstanceSetting_StorageSetting_StorageType.S3)} id="s3" />
<Label htmlFor="s3">S3</Label>
</div>
</RadioGroup>
</div>
<SettingRow label={t("setting.system-section.max-upload-size")} tooltip={t("setting.system-section.max-upload-size-hint")}>
<Input className="w-24 font-mono" value={instanceStorageSetting.uploadSizeLimitMb} onChange={handleMaxUploadSizeChanged} />
<Input
className="w-24 font-mono"
value={String(instanceStorageSetting.uploadSizeLimitMb)}
onChange={handleMaxUploadSizeChanged}
/>
</SettingRow>
{instanceStorageSetting.storageType !== InstanceSetting_StorageSetting_StorageType.DATABASE && (

View File

@ -1,3 +1,4 @@
import { timestampDate } from "@bufbuild/protobuf/wkt";
import { ClockIcon, MonitorIcon, SmartphoneIcon, TabletIcon, TrashIcon, WifiIcon } from "lucide-react";
import { useEffect, useState } from "react";
import { toast } from "react-hot-toast";
@ -5,13 +6,17 @@ import ConfirmDialog from "@/components/ConfirmDialog";
import { Button } from "@/components/ui/button";
import { userServiceClient } from "@/grpcweb";
import useCurrentUser from "@/hooks/useCurrentUser";
import { UserSession } from "@/types/proto/api/v1/user_service";
import { UserSession } from "@/types/proto/api/v1/user_service_pb";
import { useTranslate } from "@/utils/i18n";
import SettingTable from "./SettingTable";
const listUserSessions = async (parent: string) => {
const { sessions } = await userServiceClient.listUserSessions({ parent });
return sessions.sort((a, b) => (b.lastAccessedTime?.getTime() ?? 0) - (a.lastAccessedTime?.getTime() ?? 0));
return sessions.sort(
(a, b) =>
((b.lastAccessedTime ? timestampDate(b.lastAccessedTime) : undefined)?.getTime() ?? 0) -
((a.lastAccessedTime ? timestampDate(a.lastAccessedTime) : undefined)?.getTime() ?? 0),
);
};
const UserSessionsSection = () => {
@ -107,7 +112,7 @@ const UserSessionsSection = () => {
render: (_, session: UserSession) => (
<div className="flex items-center space-x-1">
<ClockIcon className="w-4 h-4" />
<span>{session.lastAccessedTime?.toLocaleString()}</span>
<span>{(session.lastAccessedTime ? timestampDate(session.lastAccessedTime) : undefined)?.toLocaleString()}</span>
</div>
),
},

View File

@ -6,7 +6,7 @@ import ConfirmDialog from "@/components/ConfirmDialog";
import { Button } from "@/components/ui/button";
import { userServiceClient } from "@/grpcweb";
import useCurrentUser from "@/hooks/useCurrentUser";
import { UserWebhook } from "@/types/proto/api/v1/user_service";
import { UserWebhook } from "@/types/proto/api/v1/user_service_pb";
import { useTranslate } from "@/utils/i18n";
import CreateWebhookDialog from "../CreateWebhookDialog";
import SettingTable from "./SettingTable";

View File

@ -1,3 +1,4 @@
import { create } from "@bufbuild/protobuf";
import { isEqual } from "lodash-es";
import { XIcon } from "lucide-react";
import { useState } from "react";
@ -10,7 +11,7 @@ import { Textarea } from "@/components/ui/textarea";
import { convertFileToBase64 } from "@/helpers/utils";
import useCurrentUser from "@/hooks/useCurrentUser";
import { instanceStore, userStore } from "@/store";
import { User as UserPb } from "@/types/proto/api/v1/user_service";
import { User as UserPb, UserSchema } from "@/types/proto/api/v1/user_service_pb";
import { useTranslate } from "@/utils/i18n";
import UserAvatar from "./UserAvatar";
@ -127,7 +128,7 @@ function UpdateAccountDialog({ open, onOpenChange, onSuccess }: Props) {
updateMask.push("description");
}
await userStore.updateUser(
UserPb.fromPartial({
create(UserSchema, {
name: currentUser.name,
username: state.username,
displayName: state.displayName,
@ -142,7 +143,7 @@ function UpdateAccountDialog({ open, onOpenChange, onSuccess }: Props) {
onOpenChange(false);
} catch (error: any) {
console.error(error);
toast.error(error.details);
toast.error(error.message);
}
};

View File

@ -1,3 +1,4 @@
import { create } from "@bufbuild/protobuf";
import { useState } from "react";
import { toast } from "react-hot-toast";
import { Button } from "@/components/ui/button";
@ -7,7 +8,12 @@ import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { instanceStore } from "@/store";
import { instanceSettingNamePrefix } from "@/store/common";
import { InstanceSetting_GeneralSetting_CustomProfile, InstanceSetting_Key } from "@/types/proto/api/v1/instance_service";
import {
InstanceSetting_GeneralSetting_CustomProfile,
InstanceSetting_GeneralSetting_CustomProfileSchema,
InstanceSetting_Key,
InstanceSettingSchema,
} from "@/types/proto/api/v1/instance_service_pb";
import { useTranslate } from "@/utils/i18n";
interface Props {
@ -20,7 +26,7 @@ function UpdateCustomizedProfileDialog({ open, onOpenChange, onSuccess }: Props)
const t = useTranslate();
const instanceGeneralSetting = instanceStore.state.generalSetting;
const [customProfile, setCustomProfile] = useState<InstanceSetting_GeneralSetting_CustomProfile>(
InstanceSetting_GeneralSetting_CustomProfile.fromPartial(instanceGeneralSetting.customProfile || {}),
create(InstanceSetting_GeneralSetting_CustomProfileSchema, instanceGeneralSetting.customProfile || {}),
);
const [isLoading, setIsLoading] = useState(false);
@ -70,13 +76,18 @@ function UpdateCustomizedProfileDialog({ open, onOpenChange, onSuccess }: Props)
setIsLoading(true);
try {
await instanceStore.upsertInstanceSetting({
name: `${instanceSettingNamePrefix}${InstanceSetting_Key.GENERAL}`,
generalSetting: {
...instanceGeneralSetting,
customProfile: customProfile,
},
});
await instanceStore.upsertInstanceSetting(
create(InstanceSettingSchema, {
name: `${instanceSettingNamePrefix}${InstanceSetting_Key.GENERAL}`,
value: {
case: "generalSetting",
value: {
...instanceGeneralSetting,
customProfile: customProfile,
},
},
}),
);
toast.success(t("message.update-succeed"));
onSuccess?.();
onOpenChange(false);

View File

@ -1,6 +1,6 @@
import { Globe2Icon, LockIcon, UsersIcon } from "lucide-react";
import { cn } from "@/lib/utils";
import { Visibility } from "@/types/proto/api/v1/memo_service";
import { Visibility } from "@/types/proto/api/v1/memo_service_pb";
interface Props {
visibility: Visibility;

View File

@ -1,7 +1,7 @@
import { closestCenter, DndContext, type DragEndEvent, MouseSensor, TouchSensor, useSensor, useSensors } from "@dnd-kit/core";
import { arrayMove, SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable";
import { useState } from "react";
import type { Attachment } from "@/types/proto/api/v1/attachment_service";
import type { Attachment } from "@/types/proto/api/v1/attachment_service_pb";
import { getAttachmentType, getAttachmentUrl } from "@/utils/attachment";
import MemoAttachment from "../MemoAttachment";
import PreviewImageDialog from "../PreviewImageDialog";

View File

@ -2,7 +2,7 @@ import { LatLng } from "leaflet";
import { MapPinIcon, XIcon } from "lucide-react";
import { useState } from "react";
import { cn } from "@/lib/utils";
import { Location } from "@/types/proto/api/v1/memo_service";
import { Location } from "@/types/proto/api/v1/memo_service_pb";
import LeafletMap from "../LeafletMap";
import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
import { BaseMetadataProps } from "./types";

View File

@ -2,7 +2,7 @@ import { LinkIcon, XIcon } from "lucide-react";
import { Link } from "react-router-dom";
import { cn } from "@/lib/utils";
import { extractMemoIdFromName } from "@/store/common";
import { MemoRelation_Memo } from "@/types/proto/api/v1/memo_service";
import { MemoRelation_Memo } from "@/types/proto/api/v1/memo_service_pb";
import { DisplayMode } from "./types";
interface RelationCardProps {

View File

@ -1,9 +1,10 @@
import { create } from "@bufbuild/protobuf";
import { LinkIcon, MilestoneIcon } from "lucide-react";
import { observer } from "mobx-react-lite";
import { useEffect, useState } from "react";
import { cn } from "@/lib/utils";
import { memoStore } from "@/store";
import { Memo, MemoRelation, MemoRelation_Type } from "@/types/proto/api/v1/memo_service";
import { Memo, MemoRelation, MemoRelation_MemoSchema, MemoRelation_Type } from "@/types/proto/api/v1/memo_service_pb";
import { useTranslate } from "@/utils/i18n";
import MetadataCard from "./MetadataCard";
import RelationCard from "./RelationCard";
@ -70,7 +71,7 @@ const RelationList = observer(({ relations, currentMemoName, mode, onRelationsCh
{referencingMemos.map((memo) => (
<RelationCard
key={memo.name}
memo={{ name: memo.name, snippet: memo.snippet }}
memo={create(MemoRelation_MemoSchema, { name: memo.name, snippet: memo.snippet })}
mode="edit"
onRemove={() => handleDeleteRelation(memo.name)}
/>

View File

@ -1,4 +1,4 @@
import type { Attachment } from "@/types/proto/api/v1/attachment_service";
import type { Attachment } from "@/types/proto/api/v1/attachment_service_pb";
import { getAttachmentThumbnailUrl, getAttachmentType, getAttachmentUrl } from "@/utils/attachment";
export type DisplayMode = "edit" | "view";
@ -39,7 +39,7 @@ export function attachmentToItem(attachment: Attachment): AttachmentItem {
mimeType: attachment.type,
thumbnailUrl: attachmentType === "image/*" ? getAttachmentThumbnailUrl(attachment) : sourceUrl,
sourceUrl,
size: attachment.size,
size: Number(attachment.size),
isLocal: false,
};
}

View File

@ -1,34 +1,32 @@
import { createChannel, createClientFactory, FetchTransport } from "nice-grpc-web";
import { ActivityServiceDefinition } from "./types/proto/api/v1/activity_service";
import { AttachmentServiceDefinition } from "./types/proto/api/v1/attachment_service";
import { AuthServiceDefinition } from "./types/proto/api/v1/auth_service";
import { IdentityProviderServiceDefinition } from "./types/proto/api/v1/idp_service";
import { InstanceServiceDefinition } from "./types/proto/api/v1/instance_service";
import { MemoServiceDefinition } from "./types/proto/api/v1/memo_service";
import { ShortcutServiceDefinition } from "./types/proto/api/v1/shortcut_service";
import { UserServiceDefinition } from "./types/proto/api/v1/user_service";
import { createClient } from "@connectrpc/connect";
import { createConnectTransport } from "@connectrpc/connect-web";
import { ActivityService } from "./types/proto/api/v1/activity_service_pb";
import { AttachmentService } from "./types/proto/api/v1/attachment_service_pb";
import { AuthService } from "./types/proto/api/v1/auth_service_pb";
import { IdentityProviderService } from "./types/proto/api/v1/idp_service_pb";
import { InstanceService } from "./types/proto/api/v1/instance_service_pb";
import { MemoService } from "./types/proto/api/v1/memo_service_pb";
import { ShortcutService } from "./types/proto/api/v1/shortcut_service_pb";
import { UserService } from "./types/proto/api/v1/user_service_pb";
const channel = createChannel(
window.location.origin,
FetchTransport({
credentials: "include",
}),
);
const transport = createConnectTransport({
baseUrl: window.location.origin,
// Include cookies in requests for session auth
fetch: (input, init) => fetch(input, { ...init, credentials: "include" }),
});
const clientFactory = createClientFactory();
export const instanceServiceClient = createClient(InstanceService, transport);
export const instanceServiceClient = clientFactory.create(InstanceServiceDefinition, channel);
export const authServiceClient = createClient(AuthService, transport);
export const authServiceClient = clientFactory.create(AuthServiceDefinition, channel);
export const userServiceClient = createClient(UserService, transport);
export const userServiceClient = clientFactory.create(UserServiceDefinition, channel);
export const memoServiceClient = createClient(MemoService, transport);
export const memoServiceClient = clientFactory.create(MemoServiceDefinition, channel);
export const attachmentServiceClient = createClient(AttachmentService, transport);
export const attachmentServiceClient = clientFactory.create(AttachmentServiceDefinition, channel);
export const shortcutServiceClient = createClient(ShortcutService, transport);
export const shortcutServiceClient = clientFactory.create(ShortcutServiceDefinition, channel);
export const activityServiceClient = createClient(ActivityService, transport);
export const activityServiceClient = clientFactory.create(ActivityServiceDefinition, channel);
export const identityProviderServiceClient = clientFactory.create(IdentityProviderServiceDefinition, channel);
export const identityProviderServiceClient = createClient(IdentityProviderService, transport);

View File

@ -1,3 +1,4 @@
import { timestampDate } from "@bufbuild/protobuf/wkt";
import dayjs from "dayjs";
import { countBy } from "lodash-es";
import { useEffect, useState } from "react";
@ -57,7 +58,12 @@ export const useFilteredMemoStats = (options: UseFilteredMemoStatsOptions = {}):
if (userStats) {
// Use activity timestamps from user stats
if (userStats.memoDisplayTimestamps && userStats.memoDisplayTimestamps.length > 0) {
activityStats = countBy(userStats.memoDisplayTimestamps.map((date) => dayjs(date).format("YYYY-MM-DD")));
activityStats = countBy(
userStats.memoDisplayTimestamps
.map((ts) => (ts ? timestampDate(ts) : undefined))
.filter((date): date is Date => date !== undefined)
.map((date) => dayjs(date).format("YYYY-MM-DD")),
);
}
// Use tag counts from user stats
if (userStats.tagCount) {
@ -75,8 +81,9 @@ export const useFilteredMemoStats = (options: UseFilteredMemoStatsOptions = {}):
for (const memo of memos) {
// Collect display timestamps for activity calendar
if (memo.displayTime) {
displayTimeList.push(memo.displayTime);
const displayTime = memo.displayTime ? timestampDate(memo.displayTime) : undefined;
if (displayTime) {
displayTimeList.push(displayTime);
}
// Count tags
if (memo.tags && memo.tags.length > 0) {

View File

@ -2,8 +2,8 @@ import { useMemo } from "react";
import { instanceStore, userStore } from "@/store";
import { extractUserIdFromName } from "@/store/common";
import memoFilterStore from "@/store/memoFilter";
import { InstanceSetting_Key } from "@/types/proto/api/v1/instance_service";
import { Visibility } from "@/types/proto/api/v1/memo_service";
import { InstanceSetting_Key } from "@/types/proto/api/v1/instance_service_pb";
import { Visibility } from "@/types/proto/api/v1/memo_service_pb";
const getShortcutId = (name: string): string => {
const parts = name.split("/");
@ -64,8 +64,8 @@ export const useMemoFilters = (options: UseMemoFiltersOptions = {}): string | un
conditions.push(`has_code`);
} else if (filter.factor === "displayTime") {
// Check instance setting for display time factor
const displayWithUpdateTime = instanceStore.getInstanceSettingByKey(InstanceSetting_Key.MEMO_RELATED).memoRelatedSetting
?.displayWithUpdateTime;
const setting = instanceStore.getInstanceSettingByKey(InstanceSetting_Key.MEMO_RELATED);
const displayWithUpdateTime = setting?.value.case === "memoRelatedSetting" ? setting.value.value.displayWithUpdateTime : false;
const factor = displayWithUpdateTime ? "updated_ts" : "created_ts";
// Convert date to UTC timestamp range

View File

@ -1,8 +1,9 @@
import { timestampDate } from "@bufbuild/protobuf/wkt";
import dayjs from "dayjs";
import { useMemo } from "react";
import { viewStore } from "@/store";
import { State } from "@/types/proto/api/v1/common";
import { Memo } from "@/types/proto/api/v1/memo_service";
import { State } from "@/types/proto/api/v1/common_pb";
import { Memo } from "@/types/proto/api/v1/memo_service_pb";
export interface UseMemoSortingOptions {
pinnedFirst?: boolean;
@ -38,9 +39,9 @@ export const useMemoSorting = (options: UseMemoSortingOptions = {}): UseMemoSort
}
// Then sort by display time
return orderByTimeAsc
? dayjs(a.displayTime).unix() - dayjs(b.displayTime).unix()
: dayjs(b.displayTime).unix() - dayjs(a.displayTime).unix();
const aTime = a.displayTime ? timestampDate(a.displayTime) : undefined;
const bTime = b.displayTime ? timestampDate(b.displayTime) : undefined;
return orderByTimeAsc ? dayjs(aTime).unix() - dayjs(bTime).unix() : dayjs(bTime).unix() - dayjs(aTime).unix();
});
};
}, [pinnedFirst, state, orderByTimeAsc]);

View File

@ -4,8 +4,8 @@ import MemoView from "@/components/MemoView";
import PagedMemoList from "@/components/PagedMemoList";
import { useMemoFilters, useMemoSorting } from "@/hooks";
import useCurrentUser from "@/hooks/useCurrentUser";
import { State } from "@/types/proto/api/v1/common";
import { Memo } from "@/types/proto/api/v1/memo_service";
import { State } from "@/types/proto/api/v1/common_pb";
import { Memo } from "@/types/proto/api/v1/memo_service_pb";
const Archived = observer(() => {
const user = useCurrentUser();

Some files were not shown because too many files have changed in this diff Show More