Skip to content

Commit e3699bd

Browse files
authored
feat(repository): add SQL repository support with multiple backends (#88)
* feat(repository): add SQL repository support with multiple backends (sqlite, postgres, mysql) * feat(docs): add repository options for SQL backends in configuration * fix(repository): remove deprecated SQL backend support in Run function * docs: update repository options section for clarity and remove deprecated value
1 parent 48b7a5d commit e3699bd

File tree

7 files changed

+558
-3
lines changed

7 files changed

+558
-3
lines changed

docs/docs/configuration.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,26 @@ You can use both exact matching and glob patterns for OIDC user authorization:
9393
| `--tls-listen` | `TLS_LISTEN` | `:443` | Address to listen on for TLS |
9494
| `--data-path` | `DATA_PATH` | `./data` | Path to the data directory |
9595

96+
### Repository Options
97+
98+
| Option | Environment Variable | Default | Description |
99+
| ---------------------- | -------------------- | ------- | --------------------------------------------------------------------------------------------------------------------- |
100+
| `--repository-backend` | `REPOSITORY_BACKEND` | `local` | Storage backend for OAuth state. Supported values: `local` (embedded BoltDB), `sqlite`, `postgres`, or `mysql`. |
101+
| `--repository-dsn` | `REPOSITORY_DSN` | - | Connection string passed directly to the SQL driver. Required when `--repository-backend` is `sqlite/postgres/mysql`. |
102+
103+
`local` uses an embedded BoltDB file under `--data-path`. SQL backends run migrations automatically via GORM; the DSN must be valid for the chosen driver (examples below). The deprecated value `sql` is no longer accepted—select the concrete driver instead.
104+
105+
```bash title="DSN examples"
106+
# sqlite
107+
--repository-backend sqlite --repository-dsn "file:data/mcp-auth.db?cache=shared&mode=rwc"
108+
109+
# postgres
110+
--repository-backend postgres --repository-dsn "postgres://user:pass@hostname:5432/database?sslmode=disable"
111+
112+
# mysql
113+
--repository-backend mysql --repository-dsn "user:pass@tcp(hostname:3306)/database?parseTime=true"
114+
```
115+
96116
### Proxy Options
97117

98118
| Option | Environment Variable | Default | Description |

go.mod

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,16 @@ require (
1919
go.uber.org/zap v1.27.0
2020
golang.org/x/crypto v0.40.0
2121
golang.org/x/oauth2 v0.14.0
22+
gorm.io/driver/mysql v1.6.0
23+
gorm.io/driver/postgres v1.6.0
24+
gorm.io/driver/sqlite v1.6.0
25+
gorm.io/gorm v1.31.0
2226
)
2327

2428
require (
2529
cloud.google.com/go/compute v1.23.3 // indirect
2630
cloud.google.com/go/compute/metadata v0.2.3 // indirect
31+
filippo.io/edwards25519 v1.1.0 // indirect
2732
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
2833
github.com/bahlo/generic-list-go v0.2.0 // indirect
2934
github.com/buger/jsonparser v1.1.1 // indirect
@@ -46,6 +51,7 @@ require (
4651
github.com/go-playground/locales v0.14.1 // indirect
4752
github.com/go-playground/universal-translator v0.18.1 // indirect
4853
github.com/go-playground/validator/v10 v10.26.0 // indirect
54+
github.com/go-sql-driver/mysql v1.8.1 // indirect
4955
github.com/gobuffalo/pop/v6 v6.1.1 // indirect
5056
github.com/goccy/go-json v0.10.5 // indirect
5157
github.com/gogo/protobuf v1.3.2 // indirect
@@ -62,12 +68,19 @@ require (
6268
github.com/hashicorp/hcl v1.0.0 // indirect
6369
github.com/inconshreveable/mousetrap v1.1.0 // indirect
6470
github.com/invopop/jsonschema v0.13.0 // indirect
71+
github.com/jackc/pgpassfile v1.0.0 // indirect
72+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
73+
github.com/jackc/pgx/v5 v5.6.0 // indirect
74+
github.com/jackc/puddle/v2 v2.2.2 // indirect
75+
github.com/jinzhu/inflection v1.0.0 // indirect
76+
github.com/jinzhu/now v1.1.5 // indirect
6577
github.com/json-iterator/go v1.1.12 // indirect
6678
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
6779
github.com/leodido/go-urn v1.4.0 // indirect
6880
github.com/magiconair/properties v1.8.7 // indirect
6981
github.com/mailru/easyjson v0.7.7 // indirect
7082
github.com/mattn/go-isatty v0.0.20 // indirect
83+
github.com/mattn/go-sqlite3 v1.14.22 // indirect
7184
github.com/mattn/goveralls v0.0.12 // indirect
7285
github.com/mitchellh/mapstructure v1.5.0 // indirect
7386
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect

go.sum

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
4040
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
4141
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
4242
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
43+
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
44+
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
4345
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
4446
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
4547
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
@@ -137,6 +139,8 @@ github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc
137139
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
138140
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
139141
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
142+
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
143+
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
140144
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
141145
github.com/gobuffalo/attrs v1.0.3/go.mod h1:KvDJCE0avbufqS0Bw3UV7RQynESY0jjod+572ctX4t8=
142146
github.com/gobuffalo/envy v1.10.2/go.mod h1:qGAGwdvDsaEtPhfBzb3o0SfDea8ByGn9j8bKmVft9z8=
@@ -277,6 +281,7 @@ github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bY
277281
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
278282
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
279283
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
284+
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
280285
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
281286
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
282287
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
@@ -287,6 +292,8 @@ github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwX
287292
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
288293
github.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
289294
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
295+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
296+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
290297
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
291298
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
292299
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
@@ -297,12 +304,20 @@ github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9
297304
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
298305
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
299306
github.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw=
307+
github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
308+
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
300309
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
301310
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
302311
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
303312
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
313+
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
314+
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
304315
github.com/jandelgado/gcov2lcov v1.0.5 h1:rkBt40h0CVK4oCb8Dps950gvfd1rYvQ8+cWa346lVU0=
305316
github.com/jandelgado/gcov2lcov v1.0.5/go.mod h1:NnSxK6TMlg1oGDBfGelGbjgorT5/L3cchlbtgFYZSss=
317+
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
318+
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
319+
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
320+
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
306321
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
307322
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
308323
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
@@ -366,6 +381,8 @@ github.com/mattn/go-jsonpointer v0.0.1/go.mod h1:1s8vx7JSjlgVRF+LW16MPpWSRZAxyrc
366381
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
367382
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
368383
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
384+
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
385+
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
369386
github.com/mattn/goveralls v0.0.12 h1:PEEeF0k1SsTjOBQ8FOmrOAoCu4ytuMaWCnWe94zxbCg=
370387
github.com/mattn/goveralls v0.0.12/go.mod h1:44ImGEUfmqH8bBtaMrYKsM65LXfNLWmwaxFGjZwgMSQ=
371388
github.com/microcosm-cc/bluemonday v1.0.20/go.mod h1:yfBmMi8mxvaZut3Yytv+jTXRY8mxyjJ0/kQBTElld50=
@@ -938,6 +955,14 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
938955
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
939956
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
940957
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
958+
gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg=
959+
gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo=
960+
gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
961+
gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
962+
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
963+
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
964+
gorm.io/gorm v1.31.0 h1:0VlycGreVhK7RF/Bwt51Fk8v0xLiiiFdbGDPIZQ7mJY=
965+
gorm.io/gorm v1.31.0/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
941966
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
942967
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
943968
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

main.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ func main() {
7272
var tlsDirectoryURL string
7373
var tlsAcceptTOS bool
7474
var dataPath string
75+
var repositoryBackend string
76+
var repositoryDSN string
7577
var externalURL string
7678
var googleClientID string
7779
var googleClientSecret string
@@ -179,6 +181,8 @@ func main() {
179181
tlsDirectoryURL,
180182
tlsAcceptTOS,
181183
dataPath,
184+
repositoryBackend,
185+
repositoryDSN,
182186
externalURL,
183187
googleClientID,
184188
googleClientSecret,
@@ -216,6 +220,8 @@ func main() {
216220
rootCmd.Flags().StringVar(&tlsDirectoryURL, "tls-directory-url", getEnvWithDefault("TLS_DIRECTORY_URL", "https://acme-v02.api.letsencrypt.org/directory"), "ACME directory URL for TLS certificates")
217221
rootCmd.Flags().BoolVar(&tlsAcceptTOS, "tls-accept-tos", getEnvBoolWithDefault("TLS_ACCEPT_TOS", false), "Accept TLS terms of service")
218222
rootCmd.Flags().StringVarP(&dataPath, "data-path", "d", getEnvWithDefault("DATA_PATH", "./data"), "Path to the data directory")
223+
rootCmd.Flags().StringVar(&repositoryBackend, "repository-backend", getEnvWithDefault("REPOSITORY_BACKEND", "local"), "Repository backend to use: local, sqlite, postgres, or mysql")
224+
rootCmd.Flags().StringVar(&repositoryDSN, "repository-dsn", getEnvWithDefault("REPOSITORY_DSN", ""), "DSN passed directly to the SQL driver (required when repository-backend is sqlite/postgres/mysql)")
219225
rootCmd.Flags().StringVarP(&externalURL, "external-url", "e", getEnvWithDefault("EXTERNAL_URL", "http://localhost"), "External URL for the proxy")
220226

221227
// Google OAuth configuration

pkg/mcp-proxy/main.go

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ func Run(
4040
tlsDirectoryURL string,
4141
tlsAcceptTOS bool,
4242
dataPath string,
43+
repositoryBackend string,
44+
repositoryDSN string,
4345
externalURL string,
4446
googleClientID string,
4547
googleClientSecret string,
@@ -138,10 +140,45 @@ func Run(
138140
proxyHeadersMap.Set("Authorization", "Bearer "+proxyBearerToken)
139141
}
140142

141-
repo, err := repository.NewKVSRepository(path.Join(dataPath, "db"), "mcp-oauth-proxy")
142-
if err != nil {
143-
return fmt.Errorf("failed to create repository: %w", err)
143+
var repo repository.Repository
144+
switch backend := strings.ToLower(repositoryBackend); backend {
145+
case "", "local":
146+
repo, err = repository.NewKVSRepository(path.Join(dataPath, "db"), "mcp-oauth-proxy")
147+
if err != nil {
148+
return fmt.Errorf("failed to create repository: %w", err)
149+
}
150+
case "sqlite":
151+
if repositoryDSN == "" {
152+
return fmt.Errorf("repository DSN must be provided for sqlite backend")
153+
}
154+
repo, err = repository.NewSQLRepository("sqlite", repositoryDSN)
155+
if err != nil {
156+
return fmt.Errorf("failed to create repository: %w", err)
157+
}
158+
case "postgres", "postgresql":
159+
if repositoryDSN == "" {
160+
return fmt.Errorf("repository DSN must be provided for postgres backend")
161+
}
162+
repo, err = repository.NewSQLRepository("postgres", repositoryDSN)
163+
if err != nil {
164+
return fmt.Errorf("failed to create repository: %w", err)
165+
}
166+
case "mysql":
167+
if repositoryDSN == "" {
168+
return fmt.Errorf("repository DSN must be provided for mysql backend")
169+
}
170+
repo, err = repository.NewSQLRepository("mysql", repositoryDSN)
171+
if err != nil {
172+
return fmt.Errorf("failed to create repository: %w", err)
173+
}
174+
default:
175+
return fmt.Errorf("unsupported repository backend: %s", repositoryBackend)
144176
}
177+
defer func() {
178+
if err := repo.Close(); err != nil {
179+
logger.Warn("failed to close repository", zap.Error(err))
180+
}
181+
}()
145182

146183
privKey, err := utils.LoadOrGeneratePrivateKey(path.Join(dataPath, "private_key.pem"))
147184
if err != nil {

0 commit comments

Comments
 (0)