working frontend. adding database.
This commit is contained in:
parent
1b77e3d01b
commit
e657d73e0e
|
@ -0,0 +1,12 @@
|
||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DatabaseRepository interface {
|
||||||
|
Close() error
|
||||||
|
|
||||||
|
CreateProviderCredentials(ctx context.Context, providerCreds models.ProviderCredential) error
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||||
|
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||||
|
"github.com/glebarez/sqlite"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewRepository(appConfig config.Interface, globalLogger logrus.FieldLogger) (DatabaseRepository, error) {
|
||||||
|
//backgroundContext := context.Background()
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Gorm/SQLite setup
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
globalLogger.Infof("Trying to connect to sqlite db: %s\n", appConfig.GetString("web.database.location"))
|
||||||
|
|
||||||
|
// When a transaction cannot lock the database, because it is already locked by another one,
|
||||||
|
// SQLite by default throws an error: database is locked. This behavior is usually not appropriate when
|
||||||
|
// concurrent access is needed, typically when multiple processes write to the same database.
|
||||||
|
// PRAGMA busy_timeout lets you set a timeout or a handler for these events. When setting a timeout,
|
||||||
|
// SQLite will try the transaction multiple times within this timeout.
|
||||||
|
// fixes #341
|
||||||
|
// https://rsqlite.r-dbi.org/reference/sqlitesetbusyhandler
|
||||||
|
// retrying for 30000 milliseconds, 30seconds - this would be unreasonable for a distributed multi-tenant application,
|
||||||
|
// but should be fine for local usage.
|
||||||
|
pragmaStr := sqlitePragmaString(map[string]string{
|
||||||
|
"busy_timeout": "30000",
|
||||||
|
})
|
||||||
|
database, err := gorm.Open(sqlite.Open(appConfig.GetString("web.database.location")+pragmaStr), &gorm.Config{
|
||||||
|
//TODO: figure out how to log database queries again.
|
||||||
|
//Logger: logger
|
||||||
|
DisableForeignKeyConstraintWhenMigrating: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to connect to database! - %v", err)
|
||||||
|
}
|
||||||
|
globalLogger.Infof("Successfully connected to scrutiny sqlite db: %s\n", appConfig.GetString("web.database.location"))
|
||||||
|
|
||||||
|
deviceRepo := sqliteRepository{
|
||||||
|
appConfig: appConfig,
|
||||||
|
logger: globalLogger,
|
||||||
|
gormClient: database,
|
||||||
|
}
|
||||||
|
return &deviceRepo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type sqliteRepository struct {
|
||||||
|
appConfig config.Interface
|
||||||
|
logger logrus.FieldLogger
|
||||||
|
|
||||||
|
gormClient *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sr *sqliteRepository) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// DeviceSummary
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
func (sr *sqliteRepository) CreateProviderCredentials(ctx context.Context, providerCreds models.ProviderCredential) error {
|
||||||
|
return sr.gormClient.WithContext(ctx).Create(providerCreds).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Utilities
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
func sqlitePragmaString(pragmas map[string]string) string {
|
||||||
|
q := url.Values{}
|
||||||
|
for key, val := range pragmas {
|
||||||
|
q.Add("_pragma", key+"="+val)
|
||||||
|
}
|
||||||
|
|
||||||
|
queryStr := q.Encode()
|
||||||
|
if len(queryStr) > 0 {
|
||||||
|
return "?" + queryStr
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
import "gorm.io/gorm"
|
||||||
|
|
||||||
|
type ProviderCredential struct {
|
||||||
|
gorm.Model
|
||||||
|
|
||||||
|
OauthEndpointBaseUrl string `json:"oauth_endpoint_base_url"`
|
||||||
|
ApiEndpointBaseUrl string `json:"api_endpoint_base_url"`
|
||||||
|
ClientId string `json:"client_id"`
|
||||||
|
RedirectUri string `json:"redirect_uri"`
|
||||||
|
Scopes []string `json:"scopes"`
|
||||||
|
PatientId string `json:"patient"`
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
RefreshToken string `json:"refresh_token"`
|
||||||
|
IdToken string `json:"id_token"`
|
||||||
|
ExpiresAt string `json:"expires_at"`
|
||||||
|
CodeChallenge string `json:"code_challenge"`
|
||||||
|
CodeVerifier string `json:"code_verifier"`
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
serverUrl: connectData.message.api_endpoint_base_url,
|
||||||
|
clientId: connectData.message.client_id,
|
||||||
|
redirectUri: connectData.message.redirect_uri,
|
||||||
|
tokenUri: `${connectData.message.oauth_endpoint_base_url}/token`,
|
||||||
|
scope: connectData.message.scopes.join(' '),
|
||||||
|
tokenResponse: payload,
|
||||||
|
expiresAt: getAccessTokenExpiration(payload, new BrowserAdapter()),
|
||||||
|
codeChallenge: codeChallenge,
|
||||||
|
codeVerifier: codeVerifier
|
||||||
|
|
||||||
|
*/
|
|
@ -0,0 +1,7 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
import "gorm.io/gorm"
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
gorm.Model
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||||
|
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateProviderCredentials(c *gin.Context) {
|
||||||
|
logger := c.MustGet("LOGGER").(*logrus.Entry)
|
||||||
|
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||||
|
|
||||||
|
var providerCred models.ProviderCredential
|
||||||
|
if err := c.ShouldBindJSON(&providerCred); err != nil {
|
||||||
|
logger.Errorln("An error occurred while parsing posted provider credential", err)
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"success": false})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := databaseRepo.CreateProviderCredentials(c, providerCred)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorln("An error occurred while storing provider credential", err)
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, gin.H{"success": true, "data": providerCred})
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||||
|
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RepositoryMiddleware(appConfig config.Interface, globalLogger logrus.FieldLogger) gin.HandlerFunc {
|
||||||
|
|
||||||
|
deviceRepo, err := database.NewRepository(appConfig, globalLogger)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: determine where we can call defer deviceRepo.Close()
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
c.Set("REPOSITORY", deviceRepo)
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,7 +20,7 @@ func (ae *AppEngine) Setup(logger *logrus.Entry) *gin.Engine {
|
||||||
r := gin.New()
|
r := gin.New()
|
||||||
|
|
||||||
r.Use(middleware.LoggerMiddleware(logger))
|
r.Use(middleware.LoggerMiddleware(logger))
|
||||||
//r.Use(middleware.DatabaseMiddleware(ae.Config, logger))
|
r.Use(middleware.RepositoryMiddleware(ae.Config, logger))
|
||||||
r.Use(middleware.ConfigMiddleware(ae.Config))
|
r.Use(middleware.ConfigMiddleware(ae.Config))
|
||||||
r.Use(gin.Recovery())
|
r.Use(gin.Recovery())
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ func (ae *AppEngine) Setup(logger *logrus.Entry) *gin.Engine {
|
||||||
"success": true,
|
"success": true,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
api.POST("/hello-world", handler.GetHelloWorld) //used to save settings
|
api.POST("/provider_credentials", handler.CreateProviderCredentials) //used to save settings
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -21,6 +21,7 @@
|
||||||
"@angular/platform-browser-dynamic": "^14.1.3",
|
"@angular/platform-browser-dynamic": "^14.1.3",
|
||||||
"@angular/router": "^14.1.3",
|
"@angular/router": "^14.1.3",
|
||||||
"@panva/oauth4webapi": "^1.1.3",
|
"@panva/oauth4webapi": "^1.1.3",
|
||||||
|
"fhirclient": "^2.5.1",
|
||||||
"rxjs": "~6.5.4",
|
"rxjs": "~6.5.4",
|
||||||
"tslib": "^2.0.0",
|
"tslib": "^2.0.0",
|
||||||
"zone.js": "~0.11.8"
|
"zone.js": "~0.11.8"
|
||||||
|
|
|
@ -2,6 +2,16 @@ import { Component } from '@angular/core';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
// import 'fhir-js-client';
|
// import 'fhir-js-client';
|
||||||
import * as Oauth from '@panva/oauth4webapi';
|
import * as Oauth from '@panva/oauth4webapi';
|
||||||
|
import { concatMap, delay, retryWhen } from 'rxjs/operators';
|
||||||
|
import { Observable, of, throwError } from 'rxjs';
|
||||||
|
// import {fhirclient} from 'fhirclient/lib/types';
|
||||||
|
import * as FHIR from "fhirclient"
|
||||||
|
import Client from 'fhirclient/lib/Client';
|
||||||
|
import {getAccessTokenExpiration} from 'fhirclient/lib/lib';
|
||||||
|
import BrowserAdapter from 'fhirclient/lib/adapters/BrowserAdapter';
|
||||||
|
|
||||||
|
export const retryCount = 24; //wait 2 minutes (5 * 24 = 120)
|
||||||
|
export const retryWaitMilliSeconds = 5000; //wait 5 seconds
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
|
@ -16,8 +26,8 @@ export class AppComponent {
|
||||||
|
|
||||||
connect(provider: string) {
|
connect(provider: string) {
|
||||||
this.http.get<any>(`https://sandbox-api.fastenhealth.com/v1/connect/${provider}`)
|
this.http.get<any>(`https://sandbox-api.fastenhealth.com/v1/connect/${provider}`)
|
||||||
.subscribe(async (data: any) => {
|
.subscribe(async (connectData: any) => {
|
||||||
console.log(data);
|
console.log(connectData);
|
||||||
|
|
||||||
// https://github.com/panva/oauth4webapi/blob/8eba19eac408bdec5c1fe8abac2710c50bfadcc3/examples/public.ts
|
// https://github.com/panva/oauth4webapi/blob/8eba19eac408bdec5c1fe8abac2710c50bfadcc3/examples/public.ts
|
||||||
const codeVerifier = Oauth.generateRandomCodeVerifier();
|
const codeVerifier = Oauth.generateRandomCodeVerifier();
|
||||||
|
@ -25,22 +35,128 @@ export class AppComponent {
|
||||||
const codeChallengeMethod = 'S256';
|
const codeChallengeMethod = 'S256';
|
||||||
|
|
||||||
// generate the authorization url
|
// generate the authorization url
|
||||||
|
const state = this.uuidV4()
|
||||||
const authorizationUrl = new URL(`${data.message.server_url}/authorize`);
|
const authorizationUrl = new URL(`${connectData.message.oauth_endpoint_base_url}/authorize`);
|
||||||
authorizationUrl.searchParams.set('client_id', data.message.client_id);
|
authorizationUrl.searchParams.set('client_id', connectData.message.client_id);
|
||||||
authorizationUrl.searchParams.set('code_challenge', codeChallenge);
|
authorizationUrl.searchParams.set('code_challenge', codeChallenge);
|
||||||
authorizationUrl.searchParams.set('code_challenge_method', codeChallengeMethod);
|
authorizationUrl.searchParams.set('code_challenge_method', codeChallengeMethod);
|
||||||
authorizationUrl.searchParams.set('redirect_uri', data.message.redirect_uri);
|
authorizationUrl.searchParams.set('redirect_uri', connectData.message.redirect_uri);
|
||||||
authorizationUrl.searchParams.set('response_type', 'code');
|
authorizationUrl.searchParams.set('response_type', 'code');
|
||||||
authorizationUrl.searchParams.set('scope', data.message.scopes.join(' '));
|
authorizationUrl.searchParams.set('scope', connectData.message.scopes.join(' '));
|
||||||
authorizationUrl.searchParams.set('state', 'hello-world-my-friend');
|
authorizationUrl.searchParams.set('state', state);
|
||||||
if (data.message.aud){
|
if (connectData.message.aud){
|
||||||
authorizationUrl.searchParams.set('aud', data.message.aud);
|
authorizationUrl.searchParams.set('aud', connectData.message.aud);
|
||||||
}
|
}
|
||||||
console.log('authorize url:', authorizationUrl.toString());
|
console.log('authorize url:', authorizationUrl.toString());
|
||||||
|
// open new browser window
|
||||||
|
window.open(authorizationUrl.toString(), "_blank");
|
||||||
|
|
||||||
|
//wait for response
|
||||||
|
this.waitForClaimOrTimeout(provider, state).subscribe(async (claimData: any) => {
|
||||||
|
console.log("claim response:", claimData)
|
||||||
|
|
||||||
|
|
||||||
|
//swap code for token
|
||||||
|
let sub: string
|
||||||
|
let access_token: string
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
const client: oauth.Client = {
|
||||||
|
client_id: connectData.message.client_id,
|
||||||
|
token_endpoint_auth_method: 'none',
|
||||||
|
}
|
||||||
|
|
||||||
|
const as = {
|
||||||
|
issuer: `${authorizationUrl.protocol}//${authorizationUrl.host}`,
|
||||||
|
authorization_endpoint: `${connectData.message.oauth_endpoint_base_url}/authorize`,
|
||||||
|
token_endpoint: `${connectData.message.oauth_endpoint_base_url}/token`,
|
||||||
|
introspect_endpoint: `${connectData.message.oauth_endpoint_base_url}/introspect`,
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("STARTING--- Oauth.validateAuthResponse")
|
||||||
|
const params = Oauth.validateAuthResponse(as, client, new URLSearchParams(claimData.message), state)
|
||||||
|
if (Oauth.isOAuth2Error(params)) {
|
||||||
|
console.log('error', params)
|
||||||
|
throw new Error() // Handle OAuth 2.0 redirect error
|
||||||
|
}
|
||||||
|
console.log("ENDING--- Oauth.validateAuthResponse")
|
||||||
|
console.log("STARTING--- Oauth.authorizationCodeGrantRequest")
|
||||||
|
const response = await Oauth.authorizationCodeGrantRequest(
|
||||||
|
as,
|
||||||
|
client,
|
||||||
|
params,
|
||||||
|
connectData.message.redirect_uri,
|
||||||
|
codeVerifier,
|
||||||
|
)
|
||||||
|
const payload = await response.json()
|
||||||
|
console.log("ENDING--- Oauth.authorizationCodeGrantRequest", payload)
|
||||||
|
|
||||||
|
|
||||||
|
//Create FHIR Client
|
||||||
|
const clientState = {
|
||||||
|
serverUrl: connectData.message.api_endpoint_base_url,
|
||||||
|
clientId: connectData.message.client_id,
|
||||||
|
redirectUri: connectData.message.redirect_uri,
|
||||||
|
tokenUri: `${connectData.message.oauth_endpoint_base_url}/token`,
|
||||||
|
scope: connectData.message.scopes.join(' '),
|
||||||
|
tokenResponse: payload,
|
||||||
|
expiresAt: getAccessTokenExpiration(payload, new BrowserAdapter()),
|
||||||
|
codeChallenge: codeChallenge,
|
||||||
|
codeVerifier: codeVerifier
|
||||||
|
}
|
||||||
|
console.log("STARTING--- FHIR.client(clientState)", clientState)
|
||||||
|
const fhirClient = FHIR.client(clientState);
|
||||||
|
|
||||||
|
console.log("STARTING--- client.request(Patient)")
|
||||||
|
const patientResponse = await fhirClient.request("PatientAccess/v1/$userinfo")
|
||||||
|
console.log(patientResponse)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// // fetch userinfo response
|
||||||
|
//
|
||||||
|
// const response = await oauth.userInfoRequest(as, client, access_token)
|
||||||
|
//
|
||||||
|
// let challenges: oauth.WWWAuthenticateChallenge[] | undefined
|
||||||
|
// if ((challenges = oauth.parseWwwAuthenticateChallenges(response))) {
|
||||||
|
// for (const challenge of challenges) {
|
||||||
|
// console.log('challenge', challenge)
|
||||||
|
// }
|
||||||
|
// throw new Error() // Handle www-authenticate challenges as needed
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// const result = await oauth.processUserInfoResponse(as, client, sub, response)
|
||||||
|
// console.log('result', result)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
waitForClaimOrTimeout(provider: string, state: string): Observable<any> {
|
||||||
|
return this.http.get<any>(`https://sandbox-api.fastenhealth.com/v1/claim/${provider}`, {params: {"state": state}}).pipe(
|
||||||
|
retryWhen(error =>
|
||||||
|
error.pipe(
|
||||||
|
concatMap((error, count) => {
|
||||||
|
if (count <= retryCount && error.status == 500) {
|
||||||
|
return of(error);
|
||||||
|
}
|
||||||
|
return throwError(error);
|
||||||
|
}),
|
||||||
|
delay(retryWaitMilliSeconds)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
uuidV4(){
|
||||||
|
// @ts-ignore
|
||||||
|
return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
|
||||||
|
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
11
go.mod
11
go.mod
|
@ -5,9 +5,11 @@ go 1.18
|
||||||
require (
|
require (
|
||||||
github.com/analogj/go-util v0.0.0-20210417161720-39b497cca03b
|
github.com/analogj/go-util v0.0.0-20210417161720-39b497cca03b
|
||||||
github.com/gin-gonic/gin v1.8.1
|
github.com/gin-gonic/gin v1.8.1
|
||||||
|
github.com/glebarez/sqlite v1.4.6
|
||||||
github.com/sirupsen/logrus v1.9.0
|
github.com/sirupsen/logrus v1.9.0
|
||||||
github.com/spf13/viper v1.12.0
|
github.com/spf13/viper v1.12.0
|
||||||
github.com/urfave/cli/v2 v2.11.2
|
github.com/urfave/cli/v2 v2.11.2
|
||||||
|
gorm.io/gorm v1.23.8
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
@ -15,11 +17,15 @@ require (
|
||||||
github.com/fatih/color v1.13.0 // indirect
|
github.com/fatih/color v1.13.0 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
|
github.com/glebarez/go-sqlite v1.17.3 // indirect
|
||||||
github.com/go-playground/locales v0.14.0 // indirect
|
github.com/go-playground/locales v0.14.0 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.10.0 // indirect
|
github.com/go-playground/validator/v10 v10.10.0 // indirect
|
||||||
github.com/goccy/go-json v0.9.7 // indirect
|
github.com/goccy/go-json v0.9.7 // indirect
|
||||||
|
github.com/google/uuid v1.3.0 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
|
github.com/jinzhu/now v1.1.4 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/kvz/logstreamer v0.0.0-20150507115422-a635b98146f0 // indirect
|
github.com/kvz/logstreamer v0.0.0-20150507115422-a635b98146f0 // indirect
|
||||||
github.com/leodido/go-urn v1.2.1 // indirect
|
github.com/leodido/go-urn v1.2.1 // indirect
|
||||||
|
@ -32,6 +38,7 @@ require (
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
|
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/spf13/afero v1.8.2 // indirect
|
github.com/spf13/afero v1.8.2 // indirect
|
||||||
github.com/spf13/cast v1.5.0 // indirect
|
github.com/spf13/cast v1.5.0 // indirect
|
||||||
|
@ -49,4 +56,8 @@ require (
|
||||||
gopkg.in/ini.v1 v1.66.4 // indirect
|
gopkg.in/ini.v1 v1.66.4 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
modernc.org/libc v1.16.8 // indirect
|
||||||
|
modernc.org/mathutil v1.4.1 // indirect
|
||||||
|
modernc.org/memory v1.1.1 // indirect
|
||||||
|
modernc.org/sqlite v1.17.3 // indirect
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue