2018-06-24 16:40:48 +03:00
package commands
import (
2022-01-20 09:39:58 +03:00
"fmt"
"net/url"
2020-02-24 19:14:46 +03:00
"os"
2018-06-24 16:40:48 +03:00
2022-11-07 18:16:55 +03:00
"github.com/hasura/graphql-engine/cli/v2/internal/errors"
2021-06-16 14:44:15 +03:00
"github.com/hasura/graphql-engine/cli/v2/internal/scripts"
"github.com/hasura/graphql-engine/cli/v2/util"
2020-04-07 12:23:20 +03:00
2018-06-24 16:40:48 +03:00
"github.com/gin-gonic/gin"
2021-06-16 14:44:15 +03:00
"github.com/hasura/graphql-engine/cli/v2"
"github.com/hasura/graphql-engine/cli/v2/pkg/console"
2018-06-24 16:40:48 +03:00
"github.com/spf13/cobra"
2018-06-24 16:47:01 +03:00
"github.com/spf13/viper"
2018-06-24 16:40:48 +03:00
)
2019-01-28 16:55:28 +03:00
// NewConsoleCmd returns the console command
2018-06-24 16:40:48 +03:00
func NewConsoleCmd ( ec * cli . ExecutionContext ) * cobra . Command {
2022-01-20 09:39:58 +03:00
var apiHost string
2018-06-24 16:47:01 +03:00
v := viper . New ( )
2020-02-24 19:14:46 +03:00
opts := & ConsoleOptions {
2018-06-24 16:40:48 +03:00
EC : ec ,
}
consoleCmd := & cobra . Command {
Use : "console" ,
2022-12-30 06:50:48 +03:00
Short : "Open the Console to manage the database and try out APIs" ,
Long : "This command will start a web server to serve the Hasura Console for the GraphQL Engine. You can use this to manage the database, build queries, and try out APIs. The Console is served at http://localhost:9695 by default. You can change the port using the --console-port flag and further configure the command by including other available flags." ,
2018-06-24 16:40:48 +03:00
Example : ` # Start console :
hasura console
# Start console on a different address and ports :
2019-12-12 08:16:36 +03:00
hasura console -- address 0.0 .0 .0 -- console - port 8080 -- api - port 8081
# Start console without opening the browser automatically
hasura console -- no - browser
# Use with admin secret :
hasura console -- admin - secret "<admin-secret>"
# Connect to an instance specified by the flag , overrides the one mentioned in config . yaml :
2023-01-11 17:08:43 +03:00
hasura console -- endpoint "<endpoint>"
# Connect to HGE instance running in a container when running CLI inside another container :
hasura console -- endpoint < container network endpoint , like : http : //host.docker.internal:8080> --no-browser --address 0.0.0.0 --console-hge-endpoint http://0.0.0.0:8080`,
2018-06-24 16:40:48 +03:00
SilenceUsage : true ,
2018-06-24 16:47:01 +03:00
PreRunE : func ( cmd * cobra . Command , args [ ] string ) error {
2022-11-07 18:16:55 +03:00
op := genOpName ( cmd , "PreRunE" )
2018-06-24 16:47:01 +03:00
ec . Viper = v
2020-02-24 19:14:46 +03:00
err := ec . Prepare ( )
if err != nil {
2022-11-07 18:16:55 +03:00
return errors . E ( op , err )
2020-02-24 19:14:46 +03:00
}
2021-01-18 20:11:05 +03:00
if err := ec . Validate ( ) ; err != nil {
2022-11-07 18:16:55 +03:00
return errors . E ( op , err )
2021-01-18 20:11:05 +03:00
}
2022-11-07 18:16:55 +03:00
if err := scripts . CheckIfUpdateToConfigV3IsRequired ( ec ) ; err != nil {
return errors . E ( op , err )
}
return nil
2018-06-24 16:47:01 +03:00
} ,
2018-06-24 16:40:48 +03:00
RunE : func ( cmd * cobra . Command , args [ ] string ) error {
2022-11-07 18:16:55 +03:00
op := genOpName ( cmd , "RunE" )
2022-01-20 09:39:58 +03:00
if cmd . Flags ( ) . Changed ( "api-host" ) {
var err error
opts . APIHost , err = url . ParseRequestURI ( apiHost )
if err != nil {
2022-11-07 18:16:55 +03:00
return errors . E ( op , fmt . Errorf ( "expected a valid url for --api-host, parsing error: %w" , err ) )
2022-01-20 09:39:58 +03:00
}
} else {
opts . APIHost = & url . URL {
Scheme : "http" ,
Host : opts . Address ,
}
}
2022-11-07 18:16:55 +03:00
if err := opts . Run ( ) ; err != nil {
return errors . E ( op , err )
}
return nil
2018-06-24 16:40:48 +03:00
} ,
}
f := consoleCmd . Flags ( )
f . StringVar ( & opts . APIPort , "api-port" , "9693" , "port for serving migrate api" )
2022-01-20 09:39:58 +03:00
f . StringVar ( & apiHost , "api-host" , "http://localhost" , "(PREVIEW: usage may change in future) host serving migrate api" )
2018-06-24 16:40:48 +03:00
f . StringVar ( & opts . ConsolePort , "console-port" , "9695" , "port for serving console" )
2019-01-29 19:02:49 +03:00
f . StringVar ( & opts . Address , "address" , "localhost" , "address to serve console and migration API from" )
2018-06-27 15:04:09 +03:00
f . BoolVar ( & opts . DontOpenBrowser , "no-browser" , false , "do not automatically open console in browser" )
2018-07-18 13:52:07 +03:00
f . StringVar ( & opts . StaticDir , "static-dir" , "" , "directory where static assets mentioned in the console html template can be served from" )
2020-01-08 06:06:09 +03:00
f . StringVar ( & opts . Browser , "browser" , "" , "open console in a specific browser" )
2020-06-03 07:06:23 +03:00
f . BoolVar ( & opts . UseServerAssets , "use-server-assets" , false , "when rendering console, use assets provided by HGE server" )
2023-02-18 09:10:15 +03:00
f . StringVar ( & opts . DataApiUrl , "console-hge-endpoint" , "" , "endpoint on which the CLI Console should reach the HGE Server" )
2018-06-24 16:47:01 +03:00
2023-01-11 11:37:47 +03:00
f . String ( "endpoint" , "" , "http(s) endpoint for Hasura GraphQL Engine" )
f . String ( "admin-secret" , "" , "admin secret for Hasura GraphQL Engine" )
f . String ( "access-key" , "" , "access key for Hasura GraphQL Engine" )
2021-10-13 17:38:07 +03:00
if err := f . MarkDeprecated ( "access-key" , "use --admin-secret instead" ) ; err != nil {
ec . Logger . WithError ( err ) . Errorf ( "error while using a dependency library" )
}
2020-04-28 14:59:57 +03:00
f . Bool ( "insecure-skip-tls-verify" , false , "skip TLS verification and disable cert checking (default: false)" )
f . String ( "certificate-authority" , "" , "path to a cert file for the certificate authority" )
2018-06-24 16:47:01 +03:00
// need to create a new viper because https://github.com/spf13/viper/issues/233
2020-04-08 14:55:42 +03:00
util . BindPFlag ( v , "endpoint" , f . Lookup ( "endpoint" ) )
util . BindPFlag ( v , "admin_secret" , f . Lookup ( "admin-secret" ) )
util . BindPFlag ( v , "access_key" , f . Lookup ( "access-key" ) )
2020-04-28 14:59:57 +03:00
util . BindPFlag ( v , "insecure_skip_tls_verify" , f . Lookup ( "insecure-skip-tls-verify" ) )
util . BindPFlag ( v , "certificate_authority" , f . Lookup ( "certificate-authority" ) )
2020-04-08 14:55:42 +03:00
2018-06-24 16:40:48 +03:00
return consoleCmd
}
2020-02-24 19:14:46 +03:00
type ConsoleOptions struct {
2018-06-24 16:40:48 +03:00
EC * cli . ExecutionContext
APIPort string
2022-01-20 09:39:58 +03:00
APIHost * url . URL
2018-06-24 16:40:48 +03:00
ConsolePort string
Address string
2018-06-27 15:04:09 +03:00
DontOpenBrowser bool
2020-06-03 07:06:23 +03:00
StaticDir string
Browser string
UseServerAssets bool
2023-01-11 17:08:43 +03:00
DataApiUrl string
2020-02-24 19:14:46 +03:00
APIServerInterruptSignal chan os . Signal
ConsoleServerInterruptSignal chan os . Signal
2018-06-24 16:40:48 +03:00
}
2020-02-24 19:14:46 +03:00
func ( o * ConsoleOptions ) Run ( ) error {
2022-11-07 18:16:55 +03:00
var op errors . Op = "commands.ConsoleOptions.Run"
2018-07-06 08:06:27 +03:00
if o . EC . Version == nil {
2022-11-07 18:16:55 +03:00
return errors . E ( op , "cannot validate version, object is nil" )
2018-07-06 08:06:27 +03:00
}
2019-02-14 12:37:47 +03:00
2022-01-20 09:39:58 +03:00
apiServer , err := console . NewAPIServer ( o . APIHost . Host , o . APIPort , o . EC )
2019-09-18 08:36:16 +03:00
if err != nil {
2022-11-07 18:16:55 +03:00
return errors . E ( op , err )
2019-09-18 08:36:16 +03:00
}
2023-01-30 12:26:48 +03:00
var templateProvider console . TemplateProvider
2020-04-09 12:30:47 +03:00
adminSecretHeader := cli . GetAdminSecretHeaderName ( o . EC . Version )
2020-06-03 07:06:23 +03:00
if o . EC . Config . ServerConfig . HasuraServerInternalConfig . ConsoleAssetsDir != "" {
o . UseServerAssets = true
}
2023-01-11 17:08:43 +03:00
dataApiUrl := o . EC . Config . ServerConfig . ParsedEndpoint . String ( )
if o . DataApiUrl != "" {
// change to dataApiUrl value user entered in flag --console-hge-endpoint
dataApiUrl = o . DataApiUrl
}
2023-01-30 12:26:48 +03:00
templateVars := gin . H {
2022-01-20 09:39:58 +03:00
"apiHost" : o . APIHost . String ( ) ,
2020-06-02 08:11:47 +03:00
"apiPort" : o . APIPort ,
"cliVersion" : o . EC . Version . GetCLIVersion ( ) ,
"serverVersion" : o . EC . Version . GetServerVersion ( ) ,
2023-01-11 17:08:43 +03:00
"dataApiUrl" : dataApiUrl ,
2020-06-02 08:11:47 +03:00
"dataApiVersion" : "" ,
"hasAccessKey" : adminSecretHeader == cli . XHasuraAccessKey ,
"adminSecret" : o . EC . Config . ServerConfig . AdminSecret ,
"enableTelemetry" : o . EC . GlobalConfig . EnableTelemetry ,
"cliUUID" : o . EC . GlobalConfig . UUID ,
"migrateSkipExecution" : true ,
2020-06-03 07:06:23 +03:00
"cdnAssets" : ! o . UseServerAssets ,
"consolePath" : "/console" ,
"urlPrefix" : "/console" ,
2023-01-30 12:26:48 +03:00
}
const basePath = "templates/gohtml/"
const templateFilename = "console.gohtml"
versionInfo , err := o . EC . APIClient . V1Version . GetVersion ( )
if err != nil {
return errors . E ( op , err )
}
templateProvider = console . NewDefaultTemplateProvider ( basePath , templateFilename , console . ConsoleFS )
// we use the default template provider by default but
// if we are able to find out the server type from the version API
// introduced in: https://github.com/hasura/graphql-engine-mono/pull/7141
// we will use that information to choose the correct server type and set the
// template provider accordingly.
if versionInfo . ServerType != nil {
switch * versionInfo . ServerType {
case "ee" :
templateVars [ "consoleType" ] = "pro-lite"
templateProvider = console . NewEETemplateProvider ( basePath , templateFilename , console . ConsoleFS )
case "ee-classic" :
templateVars [ "consoleType" ] = "pro"
templateProvider = console . NewEETemplateProvider ( basePath , templateFilename , console . ConsoleFS )
case "cloud" :
templateVars [ "consoleType" ] = "cloud"
templateProvider = console . NewCloudTemplateProvider ( basePath , templateFilename , console . ConsoleFS )
}
}
consoleTemplateVersion := templateProvider . GetTemplateVersion ( o . EC . Version )
consoleAssetsVersion := templateProvider . GetAssetsVersion ( o . EC . Version )
templateVars [ "assetsVersion" ] = consoleAssetsVersion
templateVars [ "assetsPath" ] = templateProvider . GetAssetsCDN ( )
// Setup console server
o . EC . Logger . Debugf ( "rendering console template [%s] with assets [%s]" , consoleTemplateVersion , consoleAssetsVersion )
consoleRouter , err := console . BuildConsoleRouter ( templateProvider , consoleTemplateVersion , o . StaticDir , templateVars )
2018-06-24 16:40:48 +03:00
if err != nil {
2022-11-07 18:16:55 +03:00
return errors . E ( op , fmt . Errorf ( "error serving console: %w" , err ) )
2018-06-24 16:40:48 +03:00
}
2020-04-07 12:23:20 +03:00
consoleServer := console . NewConsoleServer ( & console . NewConsoleServerOpts {
Logger : o . EC . Logger ,
APIPort : o . APIPort ,
Address : o . Address ,
Browser : o . Browser ,
DontOpenBrowser : o . DontOpenBrowser ,
EC : o . EC ,
Port : o . ConsolePort ,
Router : consoleRouter ,
StaticDir : o . StaticDir ,
TemplateProvider : templateProvider ,
2018-06-24 16:40:48 +03:00
} )
2020-04-07 12:23:20 +03:00
// start console and API HTTP Servers
serveOpts := & console . ServeOpts {
APIServer : apiServer ,
ConsoleServer : consoleServer ,
EC : o . EC ,
DontOpenBrowser : o . DontOpenBrowser ,
Browser : o . Browser ,
ConsolePort : o . ConsolePort ,
APIPort : o . APIPort ,
Address : o . Address ,
SignalChanConsoleServer : o . ConsoleServerInterruptSignal ,
SignalChanAPIServer : o . APIServerInterruptSignal ,
}
2022-11-07 18:16:55 +03:00
if err := console . Serve ( serveOpts ) ; err != nil {
return errors . E ( op , err )
}
return nil
2018-06-24 16:40:48 +03:00
}