mirror of
https://github.com/rclone/rclone.git
synced 2024-11-22 16:34:30 +01:00
fs: add names to each config parameter so we can override them #3455
This commit is contained in:
parent
94dbfa4ea6
commit
f122808d86
@ -208,9 +208,9 @@ func init() {
|
|||||||
return fs.ConfigGoto("teamdrive")
|
return fs.ConfigGoto("teamdrive")
|
||||||
case "teamdrive":
|
case "teamdrive":
|
||||||
if opt.TeamDriveID == "" {
|
if opt.TeamDriveID == "" {
|
||||||
return fs.ConfigConfirm("teamdrive_ok", false, "Configure this as a Shared Drive (Team Drive)?\n")
|
return fs.ConfigConfirm("teamdrive_ok", false, "config_change_team_drive", "Configure this as a Shared Drive (Team Drive)?\n")
|
||||||
}
|
}
|
||||||
return fs.ConfigConfirm("teamdrive_ok", false, fmt.Sprintf("Change current Shared Drive (Team Drive) ID %q?\n", opt.TeamDriveID))
|
return fs.ConfigConfirm("teamdrive_ok", false, "config_change_team_drive", fmt.Sprintf("Change current Shared Drive (Team Drive) ID %q?\n", opt.TeamDriveID))
|
||||||
case "teamdrive_ok":
|
case "teamdrive_ok":
|
||||||
if config.Result == "false" {
|
if config.Result == "false" {
|
||||||
m.Set("team_drive", "")
|
m.Set("team_drive", "")
|
||||||
@ -227,7 +227,7 @@ func init() {
|
|||||||
if len(teamDrives) == 0 {
|
if len(teamDrives) == 0 {
|
||||||
return fs.ConfigError("", "No Shared Drives found in your account")
|
return fs.ConfigError("", "No Shared Drives found in your account")
|
||||||
}
|
}
|
||||||
return fs.ConfigChoose("teamdrive_final", "Shared Drive", len(teamDrives), func(i int) (string, string) {
|
return fs.ConfigChoose("teamdrive_final", "config_team_drive", "Shared Drive", len(teamDrives), func(i int) (string, string) {
|
||||||
teamDrive := teamDrives[i]
|
teamDrive := teamDrives[i]
|
||||||
return teamDrive.Id, teamDrive.Name
|
return teamDrive.Id, teamDrive.Name
|
||||||
})
|
})
|
||||||
|
@ -98,7 +98,7 @@ func init() {
|
|||||||
})
|
})
|
||||||
case "warning":
|
case "warning":
|
||||||
// Warn the user as required by google photos integration
|
// Warn the user as required by google photos integration
|
||||||
return fs.ConfigConfirm("warning_done", true, `Warning
|
return fs.ConfigConfirm("warning_done", true, "config_warning", `Warning
|
||||||
|
|
||||||
IMPORTANT: All media items uploaded to Google Photos with rclone
|
IMPORTANT: All media items uploaded to Google Photos with rclone
|
||||||
are stored in full resolution at original quality. These uploads
|
are stored in full resolution at original quality. These uploads
|
||||||
|
@ -126,7 +126,7 @@ func init() {
|
|||||||
func Config(ctx context.Context, name string, m configmap.Mapper, config fs.ConfigIn) (*fs.ConfigOut, error) {
|
func Config(ctx context.Context, name string, m configmap.Mapper, config fs.ConfigIn) (*fs.ConfigOut, error) {
|
||||||
switch config.State {
|
switch config.State {
|
||||||
case "":
|
case "":
|
||||||
return fs.ConfigChooseFixed("auth_type_done", `Authentication type`, []fs.OptionExample{{
|
return fs.ConfigChooseFixed("auth_type_done", "config_type", `Authentication type`, []fs.OptionExample{{
|
||||||
Value: "standard",
|
Value: "standard",
|
||||||
Help: "Standard authentication - use this if you're a normal Jottacloud user.",
|
Help: "Standard authentication - use this if you're a normal Jottacloud user.",
|
||||||
}, {
|
}, {
|
||||||
@ -141,7 +141,7 @@ func Config(ctx context.Context, name string, m configmap.Mapper, config fs.Conf
|
|||||||
return fs.ConfigGoto(config.Result)
|
return fs.ConfigGoto(config.Result)
|
||||||
case "standard": // configure a jottacloud backend using the modern JottaCli token based authentication
|
case "standard": // configure a jottacloud backend using the modern JottaCli token based authentication
|
||||||
m.Set("configVersion", fmt.Sprint(configVersion))
|
m.Set("configVersion", fmt.Sprint(configVersion))
|
||||||
return fs.ConfigInput("standard_token", "Personal login token.\n\nGenerate here: https://www.jottacloud.com/web/secure")
|
return fs.ConfigInput("standard_token", "config_login_token", "Personal login token.\n\nGenerate here: https://www.jottacloud.com/web/secure")
|
||||||
case "standard_token":
|
case "standard_token":
|
||||||
loginToken := config.Result
|
loginToken := config.Result
|
||||||
m.Set(configClientID, "jottacli")
|
m.Set(configClientID, "jottacli")
|
||||||
@ -159,7 +159,7 @@ func Config(ctx context.Context, name string, m configmap.Mapper, config fs.Conf
|
|||||||
return fs.ConfigGoto("choose_device")
|
return fs.ConfigGoto("choose_device")
|
||||||
case "legacy": // configure a jottacloud backend using legacy authentication
|
case "legacy": // configure a jottacloud backend using legacy authentication
|
||||||
m.Set("configVersion", fmt.Sprint(v1configVersion))
|
m.Set("configVersion", fmt.Sprint(v1configVersion))
|
||||||
return fs.ConfigConfirm("legacy_api", false, `Do you want to create a machine specific API key?
|
return fs.ConfigConfirm("legacy_api", false, "config_machine_specific", `Do you want to create a machine specific API key?
|
||||||
|
|
||||||
Rclone has it's own Jottacloud API KEY which works fine as long as one
|
Rclone has it's own Jottacloud API KEY which works fine as long as one
|
||||||
only uses rclone on a single machine. When you want to use rclone with
|
only uses rclone on a single machine. When you want to use rclone with
|
||||||
@ -177,10 +177,10 @@ machines.`)
|
|||||||
m.Set(configClientSecret, obscure.MustObscure(deviceRegistration.ClientSecret))
|
m.Set(configClientSecret, obscure.MustObscure(deviceRegistration.ClientSecret))
|
||||||
fs.Debugf(nil, "Got clientID %q and clientSecret %q", deviceRegistration.ClientID, deviceRegistration.ClientSecret)
|
fs.Debugf(nil, "Got clientID %q and clientSecret %q", deviceRegistration.ClientID, deviceRegistration.ClientSecret)
|
||||||
}
|
}
|
||||||
return fs.ConfigInput("legacy_user", "Username")
|
return fs.ConfigInput("legacy_user", "config_user", "Username")
|
||||||
case "legacy_username":
|
case "legacy_username":
|
||||||
m.Set(configUsername, config.Result)
|
m.Set(configUsername, config.Result)
|
||||||
return fs.ConfigPassword("legacy_password", "Jottacloud password\n\n(this is only required during setup and will not be stored).")
|
return fs.ConfigPassword("legacy_password", "config_password", "Jottacloud password\n\n(this is only required during setup and will not be stored).")
|
||||||
case "legacy_password":
|
case "legacy_password":
|
||||||
m.Set("password", config.Result)
|
m.Set("password", config.Result)
|
||||||
m.Set("auth_code", "")
|
m.Set("auth_code", "")
|
||||||
@ -213,7 +213,7 @@ machines.`)
|
|||||||
|
|
||||||
token, err := doAuthV1(ctx, srv, username, password, authCode)
|
token, err := doAuthV1(ctx, srv, username, password, authCode)
|
||||||
if err == errAuthCodeRequired {
|
if err == errAuthCodeRequired {
|
||||||
return fs.ConfigInput("legacy_auth_code", "Verification Code\nThis account uses 2 factor authentication you will receive a verification code via SMS.")
|
return fs.ConfigInput("legacy_auth_code", "config_auth_code", "Verification Code\nThis account uses 2 factor authentication you will receive a verification code via SMS.")
|
||||||
}
|
}
|
||||||
m.Set("password", "")
|
m.Set("password", "")
|
||||||
m.Set("auth_code", "")
|
m.Set("auth_code", "")
|
||||||
@ -241,7 +241,7 @@ machines.`)
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
case "choose_device":
|
case "choose_device":
|
||||||
return fs.ConfigConfirm("choose_device_query", false, "Use a non standard device/mountpoint e.g. for accessing files uploaded using the official Jottacloud client?")
|
return fs.ConfigConfirm("choose_device_query", false, "config_non_standard", "Use a non standard device/mountpoint e.g. for accessing files uploaded using the official Jottacloud client?")
|
||||||
case "choose_device_query":
|
case "choose_device_query":
|
||||||
if config.Result != "true" {
|
if config.Result != "true" {
|
||||||
m.Set(configDevice, "")
|
m.Set(configDevice, "")
|
||||||
@ -265,7 +265,7 @@ machines.`)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return fs.ConfigChoose("choose_device_result", `Please select the device to use. Normally this will be Jotta`, len(acc.Devices), func(i int) (string, string) {
|
return fs.ConfigChoose("choose_device_result", "config_device", `Please select the device to use. Normally this will be Jotta`, len(acc.Devices), func(i int) (string, string) {
|
||||||
return acc.Devices[i].Name, ""
|
return acc.Devices[i].Name, ""
|
||||||
})
|
})
|
||||||
case "choose_device_result":
|
case "choose_device_result":
|
||||||
@ -283,7 +283,7 @@ machines.`)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return fs.ConfigChoose("choose_device_mountpoint", `Please select the mountpoint to use. Normally this will be Archive.`, len(dev.MountPoints), func(i int) (string, string) {
|
return fs.ConfigChoose("choose_device_mountpoint", "config_mountpoint", `Please select the mountpoint to use. Normally this will be Archive.`, len(dev.MountPoints), func(i int) (string, string) {
|
||||||
return dev.MountPoints[i].Name, ""
|
return dev.MountPoints[i].Name, ""
|
||||||
})
|
})
|
||||||
case "choose_device_mountpoint":
|
case "choose_device_mountpoint":
|
||||||
|
@ -363,7 +363,7 @@ func chooseDrive(ctx context.Context, name string, m configmap.Mapper, srv *rest
|
|||||||
if len(drives.Drives) == 0 {
|
if len(drives.Drives) == 0 {
|
||||||
return fs.ConfigError("choose_type", "No drives found")
|
return fs.ConfigError("choose_type", "No drives found")
|
||||||
}
|
}
|
||||||
return fs.ConfigChoose("driveid_final", "Select drive you want to use", len(drives.Drives), func(i int) (string, string) {
|
return fs.ConfigChoose("driveid_final", "config_driveid", "Select drive you want to use", len(drives.Drives), func(i int) (string, string) {
|
||||||
drive := drives.Drives[i]
|
drive := drives.Drives[i]
|
||||||
return drive.DriveID, fmt.Sprintf("%s (%s)", drive.DriveName, drive.DriveType)
|
return drive.DriveID, fmt.Sprintf("%s (%s)", drive.DriveName, drive.DriveType)
|
||||||
})
|
})
|
||||||
@ -388,7 +388,7 @@ func Config(ctx context.Context, name string, m configmap.Mapper, config fs.Conf
|
|||||||
OAuth2Config: oauthConfig,
|
OAuth2Config: oauthConfig,
|
||||||
})
|
})
|
||||||
case "choose_type":
|
case "choose_type":
|
||||||
return fs.ConfigChooseFixed("choose_type_done", "Type of connection", []fs.OptionExample{{
|
return fs.ConfigChooseFixed("choose_type_done", "config_type", "Type of connection", []fs.OptionExample{{
|
||||||
Value: "onedrive",
|
Value: "onedrive",
|
||||||
Help: "OneDrive Personal or Business",
|
Help: "OneDrive Personal or Business",
|
||||||
}, {
|
}, {
|
||||||
@ -430,19 +430,19 @@ func Config(ctx context.Context, name string, m configmap.Mapper, config fs.Conf
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
case "driveid":
|
case "driveid":
|
||||||
return fs.ConfigInput("driveid_end", "Drive ID")
|
return fs.ConfigInput("driveid_end", "config_driveid_fixed", "Drive ID")
|
||||||
case "driveid_end":
|
case "driveid_end":
|
||||||
return chooseDrive(ctx, name, m, srv, chooseDriveOpt{
|
return chooseDrive(ctx, name, m, srv, chooseDriveOpt{
|
||||||
finalDriveID: config.Result,
|
finalDriveID: config.Result,
|
||||||
})
|
})
|
||||||
case "siteid":
|
case "siteid":
|
||||||
return fs.ConfigInput("siteid_end", "Site ID")
|
return fs.ConfigInput("siteid_end", "config_siteid", "Site ID")
|
||||||
case "siteid_end":
|
case "siteid_end":
|
||||||
return chooseDrive(ctx, name, m, srv, chooseDriveOpt{
|
return chooseDrive(ctx, name, m, srv, chooseDriveOpt{
|
||||||
siteID: config.Result,
|
siteID: config.Result,
|
||||||
})
|
})
|
||||||
case "url":
|
case "url":
|
||||||
return fs.ConfigInput("url_end", `Site URL
|
return fs.ConfigInput("url_end", "config_site_url", `Site URL
|
||||||
|
|
||||||
Example: "https://contoso.sharepoint.com/sites/mysite" or "mysite"
|
Example: "https://contoso.sharepoint.com/sites/mysite" or "mysite"
|
||||||
`)
|
`)
|
||||||
@ -459,13 +459,13 @@ Example: "https://contoso.sharepoint.com/sites/mysite" or "mysite"
|
|||||||
relativePath: "/sites/" + siteURL,
|
relativePath: "/sites/" + siteURL,
|
||||||
})
|
})
|
||||||
case "path":
|
case "path":
|
||||||
return fs.ConfigInput("path_end", `Server-relative URL`)
|
return fs.ConfigInput("path_end", "config_sharepoint_url", `Server-relative URL`)
|
||||||
case "path_end":
|
case "path_end":
|
||||||
return chooseDrive(ctx, name, m, srv, chooseDriveOpt{
|
return chooseDrive(ctx, name, m, srv, chooseDriveOpt{
|
||||||
relativePath: config.Result,
|
relativePath: config.Result,
|
||||||
})
|
})
|
||||||
case "search":
|
case "search":
|
||||||
return fs.ConfigInput("search_end", `Search term`)
|
return fs.ConfigInput("search_end", "config_search_term", `Search term`)
|
||||||
case "search_end":
|
case "search_end":
|
||||||
searchTerm := config.Result
|
searchTerm := config.Result
|
||||||
opts := rest.Opts{
|
opts := rest.Opts{
|
||||||
@ -483,7 +483,7 @@ Example: "https://contoso.sharepoint.com/sites/mysite" or "mysite"
|
|||||||
if len(sites.Sites) == 0 {
|
if len(sites.Sites) == 0 {
|
||||||
return fs.ConfigError("choose_type", fmt.Sprintf("search for %q returned no results", searchTerm))
|
return fs.ConfigError("choose_type", fmt.Sprintf("search for %q returned no results", searchTerm))
|
||||||
}
|
}
|
||||||
return fs.ConfigChoose("search_sites", `Select the Site you want to use`, len(sites.Sites), func(i int) (string, string) {
|
return fs.ConfigChoose("search_sites", "config_site", `Select the Site you want to use`, len(sites.Sites), func(i int) (string, string) {
|
||||||
site := sites.Sites[i]
|
site := sites.Sites[i]
|
||||||
return site.SiteID, fmt.Sprintf("%s (%s)", site.SiteName, site.SiteURL)
|
return site.SiteID, fmt.Sprintf("%s (%s)", site.SiteName, site.SiteURL)
|
||||||
})
|
})
|
||||||
@ -508,7 +508,7 @@ Example: "https://contoso.sharepoint.com/sites/mysite" or "mysite"
|
|||||||
m.Set(configDriveID, finalDriveID)
|
m.Set(configDriveID, finalDriveID)
|
||||||
m.Set(configDriveType, rootItem.ParentReference.DriveType)
|
m.Set(configDriveType, rootItem.ParentReference.DriveType)
|
||||||
|
|
||||||
return fs.ConfigConfirm("driveid_final_end", true, fmt.Sprintf("Drive OK?\n\nFound drive %q of type %q\nURL: %s\n", rootItem.Name, rootItem.ParentReference.DriveType, rootItem.WebURL))
|
return fs.ConfigConfirm("driveid_final_end", true, "config_drive_ok", fmt.Sprintf("Drive OK?\n\nFound drive %q of type %q\nURL: %s\n", rootItem.Name, rootItem.ParentReference.DriveType, rootItem.WebURL))
|
||||||
case "driveid_final_end":
|
case "driveid_final_end":
|
||||||
if config.Result == "true" {
|
if config.Result == "true" {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@ -327,7 +327,7 @@ func Config(ctx context.Context, name string, m configmap.Mapper, config fs.Conf
|
|||||||
case "":
|
case "":
|
||||||
// Just make sure we do have a password
|
// Just make sure we do have a password
|
||||||
if password == "" {
|
if password == "" {
|
||||||
return fs.ConfigPassword("", "Two-factor authentication: please enter your password (it won't be saved in the configuration)")
|
return fs.ConfigPassword("", "config_password", "Two-factor authentication: please enter your password (it won't be saved in the configuration)")
|
||||||
}
|
}
|
||||||
return fs.ConfigGoto("password")
|
return fs.ConfigGoto("password")
|
||||||
case "password":
|
case "password":
|
||||||
@ -338,7 +338,7 @@ func Config(ctx context.Context, name string, m configmap.Mapper, config fs.Conf
|
|||||||
m.Set(configPassword, obscure.MustObscure(config.Result))
|
m.Set(configPassword, obscure.MustObscure(config.Result))
|
||||||
return fs.ConfigGoto("2fa")
|
return fs.ConfigGoto("2fa")
|
||||||
case "2fa":
|
case "2fa":
|
||||||
return fs.ConfigInput("2fa_do", "Two-factor authentication: please enter your 2FA code")
|
return fs.ConfigInput("2fa_do", "config_2fa", "Two-factor authentication: please enter your 2FA code")
|
||||||
case "2fa_do":
|
case "2fa_do":
|
||||||
code := config.Result
|
code := config.Result
|
||||||
if code == "" {
|
if code == "" {
|
||||||
@ -358,10 +358,10 @@ func Config(ctx context.Context, name string, m configmap.Mapper, config fs.Conf
|
|||||||
|
|
||||||
token, err := getAuthorizationToken(ctx, srv, username, password, code)
|
token, err := getAuthorizationToken(ctx, srv, username, password, code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fs.ConfigConfirm("2fa_error", true, fmt.Sprintf("Authentication failed: %v\n\nTry Again?", err))
|
return fs.ConfigConfirm("2fa_error", true, "config_retry", fmt.Sprintf("Authentication failed: %v\n\nTry Again?", err))
|
||||||
}
|
}
|
||||||
if token == "" {
|
if token == "" {
|
||||||
return fs.ConfigConfirm("2fa_error", true, "Authentication failed - no token returned.\n\nTry Again?")
|
return fs.ConfigConfirm("2fa_error", true, "config_retry", "Authentication failed - no token returned.\n\nTry Again?")
|
||||||
}
|
}
|
||||||
// Let's save the token into the configuration
|
// Let's save the token into the configuration
|
||||||
m.Set(configAuthToken, token)
|
m.Set(configAuthToken, token)
|
||||||
|
@ -87,17 +87,17 @@ func init() {
|
|||||||
if opt.RefreshToken == "" {
|
if opt.RefreshToken == "" {
|
||||||
return fs.ConfigGoto("username")
|
return fs.ConfigGoto("username")
|
||||||
}
|
}
|
||||||
return fs.ConfigConfirm("refresh", true, "Already have a token - refresh?")
|
return fs.ConfigConfirm("refresh", true, "config_refresh", "Already have a token - refresh?")
|
||||||
case "refresh":
|
case "refresh":
|
||||||
if config.Result == "false" {
|
if config.Result == "false" {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
return fs.ConfigGoto("username")
|
return fs.ConfigGoto("username")
|
||||||
case "username":
|
case "username":
|
||||||
return fs.ConfigInput("password", "username (email address)")
|
return fs.ConfigInput("password", "config_username", "username (email address)")
|
||||||
case "password":
|
case "password":
|
||||||
m.Set("username", config.Result)
|
m.Set("username", config.Result)
|
||||||
return fs.ConfigPassword("auth", "Your Sugarsync password.\n\nOnly required during setup and will not be stored.")
|
return fs.ConfigPassword("auth", "config_password", "Your Sugarsync password.\n\nOnly required during setup and will not be stored.")
|
||||||
case "auth":
|
case "auth":
|
||||||
username, _ := m.Get("username")
|
username, _ := m.Get("username")
|
||||||
m.Set("username", "")
|
m.Set("username", "")
|
||||||
|
@ -131,7 +131,7 @@ func init() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return fs.ConfigChoose("workspace", "Team Drive ID", len(teams), func(i int) (string, string) {
|
return fs.ConfigChoose("workspace", "config_team_drive_id", "Team Drive ID", len(teams), func(i int) (string, string) {
|
||||||
team := teams[i]
|
team := teams[i]
|
||||||
return team.ID, team.Attributes.Name
|
return team.ID, team.Attributes.Name
|
||||||
})
|
})
|
||||||
@ -145,7 +145,7 @@ func init() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return fs.ConfigChoose("workspace_end", "Workspace ID", len(workspaces), func(i int) (string, string) {
|
return fs.ConfigChoose("workspace_end", "config_workspace", "Workspace ID", len(workspaces), func(i int) (string, string) {
|
||||||
workspace := workspaces[i]
|
workspace := workspaces[i]
|
||||||
return workspace.ID, workspace.Attributes.Name
|
return workspace.ID, workspace.Attributes.Name
|
||||||
})
|
})
|
||||||
|
@ -25,7 +25,9 @@ var ConfigOAuth func(ctx context.Context, name string, m configmap.Mapper, ri *R
|
|||||||
|
|
||||||
// ConfigIn is passed to the Config function for an Fs
|
// ConfigIn is passed to the Config function for an Fs
|
||||||
//
|
//
|
||||||
// The interactive config system for backends is state based. This is so that different frontends to the config can be attached, eg over the API or web page.
|
// The interactive config system for backends is state based. This is
|
||||||
|
// so that different frontends to the config can be attached, eg over
|
||||||
|
// the API or web page.
|
||||||
//
|
//
|
||||||
// Each call to the config system supplies ConfigIn which tells the
|
// Each call to the config system supplies ConfigIn which tells the
|
||||||
// system what to do. Each will return a ConfigOut which gives a
|
// system what to do. Each will return a ConfigOut which gives a
|
||||||
@ -47,6 +49,10 @@ var ConfigOAuth func(ctx context.Context, name string, m configmap.Mapper, ri *R
|
|||||||
//
|
//
|
||||||
// The utilities here are convenience methods for different kinds of
|
// The utilities here are convenience methods for different kinds of
|
||||||
// questions and responses.
|
// questions and responses.
|
||||||
|
//
|
||||||
|
// Where the questions ask for a name then this should start with
|
||||||
|
// "config_" to show it is an ephemeral config input rather than the
|
||||||
|
// actual value stored in the config file.
|
||||||
type ConfigIn struct {
|
type ConfigIn struct {
|
||||||
State string // State to run
|
State string // State to run
|
||||||
Result string // Result from previous Option
|
Result string // Result from previous Option
|
||||||
@ -66,33 +72,42 @@ type ConfigOut struct {
|
|||||||
Result string // if Option/OAuth not set then this is passed to the next state
|
Result string // if Option/OAuth not set then this is passed to the next state
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigInput asks the user for a string
|
// ConfigInputOptional asks the user for a string which may be empty
|
||||||
//
|
//
|
||||||
// state should be the next state required
|
// state should be the next state required
|
||||||
|
// name is the config name for this item
|
||||||
// help should be the help shown to the user
|
// help should be the help shown to the user
|
||||||
func ConfigInput(state string, help string) (*ConfigOut, error) {
|
func ConfigInputOptional(state string, name string, help string) (*ConfigOut, error) {
|
||||||
return &ConfigOut{
|
return &ConfigOut{
|
||||||
State: state,
|
State: state,
|
||||||
Option: &Option{
|
Option: &Option{
|
||||||
|
Name: name,
|
||||||
Help: help,
|
Help: help,
|
||||||
Default: "",
|
Default: "",
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ConfigInput asks the user for a non-empty string
|
||||||
|
//
|
||||||
|
// state should be the next state required
|
||||||
|
// name is the config name for this item
|
||||||
|
// help should be the help shown to the user
|
||||||
|
func ConfigInput(state string, name string, help string) (*ConfigOut, error) {
|
||||||
|
out, _ := ConfigInputOptional(state, name, help)
|
||||||
|
out.Option.Required = true
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ConfigPassword asks the user for a password
|
// ConfigPassword asks the user for a password
|
||||||
//
|
//
|
||||||
// state should be the next state required
|
// state should be the next state required
|
||||||
|
// name is the config name for this item
|
||||||
// help should be the help shown to the user
|
// help should be the help shown to the user
|
||||||
func ConfigPassword(state string, help string) (*ConfigOut, error) {
|
func ConfigPassword(state string, name string, help string) (*ConfigOut, error) {
|
||||||
return &ConfigOut{
|
out, _ := ConfigInputOptional(state, name, help)
|
||||||
State: state,
|
out.Option.IsPassword = true
|
||||||
Option: &Option{
|
return out, nil
|
||||||
Help: help,
|
|
||||||
Default: "",
|
|
||||||
IsPassword: true,
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigGoto goes to the next state with empty Result
|
// ConfigGoto goes to the next state with empty Result
|
||||||
@ -130,11 +145,13 @@ func ConfigError(state string, Error string) (*ConfigOut, error) {
|
|||||||
//
|
//
|
||||||
// state should be the next state required
|
// state should be the next state required
|
||||||
// Default should be the default state
|
// Default should be the default state
|
||||||
|
// name is the config name for this item
|
||||||
// help should be the help shown to the user
|
// help should be the help shown to the user
|
||||||
func ConfigConfirm(state string, Default bool, help string) (*ConfigOut, error) {
|
func ConfigConfirm(state string, Default bool, name string, help string) (*ConfigOut, error) {
|
||||||
return &ConfigOut{
|
return &ConfigOut{
|
||||||
State: state,
|
State: state,
|
||||||
Option: &Option{
|
Option: &Option{
|
||||||
|
Name: name,
|
||||||
Help: help,
|
Help: help,
|
||||||
Default: Default,
|
Default: Default,
|
||||||
Examples: []OptionExample{{
|
Examples: []OptionExample{{
|
||||||
@ -151,19 +168,21 @@ func ConfigConfirm(state string, Default bool, help string) (*ConfigOut, error)
|
|||||||
// ConfigChooseFixed returns a ConfigOut structure which has a list of items to choose from.
|
// ConfigChooseFixed returns a ConfigOut structure which has a list of items to choose from.
|
||||||
//
|
//
|
||||||
// state should be the next state required
|
// state should be the next state required
|
||||||
|
// name is the config name for this item
|
||||||
// help should be the help shown to the user
|
// help should be the help shown to the user
|
||||||
// items should be the items in the list
|
// items should be the items in the list
|
||||||
//
|
//
|
||||||
// It chooses the first item to be the default.
|
// It chooses the first item to be the default.
|
||||||
// If there are no items then it will return an error.
|
// If there are no items then it will return an error.
|
||||||
// If there is only one item it will short cut to the next state
|
// If there is only one item it will short cut to the next state
|
||||||
func ConfigChooseFixed(state string, help string, items []OptionExample) (*ConfigOut, error) {
|
func ConfigChooseFixed(state string, name string, help string, items []OptionExample) (*ConfigOut, error) {
|
||||||
if len(items) == 0 {
|
if len(items) == 0 {
|
||||||
return nil, errors.Errorf("no items found in: %s", help)
|
return nil, errors.Errorf("no items found in: %s", help)
|
||||||
}
|
}
|
||||||
choose := &ConfigOut{
|
choose := &ConfigOut{
|
||||||
State: state,
|
State: state,
|
||||||
Option: &Option{
|
Option: &Option{
|
||||||
|
Name: name,
|
||||||
Help: help,
|
Help: help,
|
||||||
Examples: items,
|
Examples: items,
|
||||||
},
|
},
|
||||||
@ -180,6 +199,7 @@ func ConfigChooseFixed(state string, help string, items []OptionExample) (*Confi
|
|||||||
// ConfigChoose returns a ConfigOut structure which has a list of items to choose from.
|
// ConfigChoose returns a ConfigOut structure which has a list of items to choose from.
|
||||||
//
|
//
|
||||||
// state should be the next state required
|
// state should be the next state required
|
||||||
|
// name is the config name for this item
|
||||||
// help should be the help shown to the user
|
// help should be the help shown to the user
|
||||||
// n should be the number of items in the list
|
// n should be the number of items in the list
|
||||||
// getItem should return the items (value, help)
|
// getItem should return the items (value, help)
|
||||||
@ -187,12 +207,12 @@ func ConfigChooseFixed(state string, help string, items []OptionExample) (*Confi
|
|||||||
// It chooses the first item to be the default.
|
// It chooses the first item to be the default.
|
||||||
// If there are no items then it will return an error.
|
// If there are no items then it will return an error.
|
||||||
// If there is only one item it will short cut to the next state
|
// If there is only one item it will short cut to the next state
|
||||||
func ConfigChoose(state string, help string, n int, getItem func(i int) (itemValue string, itemHelp string)) (*ConfigOut, error) {
|
func ConfigChoose(state string, name string, help string, n int, getItem func(i int) (itemValue string, itemHelp string)) (*ConfigOut, error) {
|
||||||
items := make(OptionExamples, n)
|
items := make(OptionExamples, n)
|
||||||
for i := range items {
|
for i := range items {
|
||||||
items[i].Value, items[i].Help = getItem(i)
|
items[i].Value, items[i].Help = getItem(i)
|
||||||
}
|
}
|
||||||
return ConfigChooseFixed(state, help, items)
|
return ConfigChooseFixed(state, name, help, items)
|
||||||
}
|
}
|
||||||
|
|
||||||
// StatePush pushes a new values onto the front of the config string
|
// StatePush pushes a new values onto the front of the config string
|
||||||
@ -237,21 +257,21 @@ func StatePop(state string) (newState string, value string) {
|
|||||||
// BackendConfig calls the config for the backend in ri
|
// BackendConfig calls the config for the backend in ri
|
||||||
//
|
//
|
||||||
// It wraps any OAuth transactions as necessary so only straight forward config questions are emitted
|
// It wraps any OAuth transactions as necessary so only straight forward config questions are emitted
|
||||||
func BackendConfig(ctx context.Context, name string, m configmap.Mapper, ri *RegInfo, in ConfigIn) (*ConfigOut, error) {
|
func BackendConfig(ctx context.Context, name string, m configmap.Mapper, ri *RegInfo, in ConfigIn) (out *ConfigOut, err error) {
|
||||||
ci := GetConfig(ctx)
|
ci := GetConfig(ctx)
|
||||||
if ri.Config == nil {
|
if ri.Config == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
// Do internal states here
|
switch {
|
||||||
if strings.HasPrefix(in.State, "*") {
|
case strings.HasPrefix(in.State, "*oauth"):
|
||||||
switch {
|
// Do internal oauth states
|
||||||
case strings.HasPrefix(in.State, "*oauth"):
|
out, err = ConfigOAuth(ctx, name, m, ri, in)
|
||||||
return ConfigOAuth(ctx, name, m, ri, in)
|
case strings.HasPrefix(in.State, "*"):
|
||||||
default:
|
err = errors.Errorf("unknown internal state %q", in.State)
|
||||||
return nil, errors.Errorf("unknown internal state %q", in.State)
|
default:
|
||||||
}
|
// Otherwise pass to backend
|
||||||
|
out, err = ri.Config(ctx, name, m, in)
|
||||||
}
|
}
|
||||||
out, err := ri.Config(ctx, name, m, in)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -267,11 +287,21 @@ func BackendConfig(ctx context.Context, name string, m configmap.Mapper, ri *Reg
|
|||||||
}
|
}
|
||||||
// Run internal state, saving the input so we can recall the state
|
// Run internal state, saving the input so we can recall the state
|
||||||
return ConfigGoto(StatePush("", "*oauth", returnState, in.State, in.Result))
|
return ConfigGoto(StatePush("", "*oauth", returnState, in.State, in.Result))
|
||||||
case out.Option != nil && ci.AutoConfirm:
|
case out.Option != nil:
|
||||||
|
if out.Option.Name == "" {
|
||||||
|
return nil, errors.New("internal error: no name set in Option")
|
||||||
|
}
|
||||||
|
// If override value is set in the config then use that
|
||||||
|
if result, ok := m.Get(out.Option.Name); ok {
|
||||||
|
Debugf(nil, "Override value found, choosing value %q for state %q", result, out.State)
|
||||||
|
return ConfigResult(out.State, result)
|
||||||
|
}
|
||||||
// If AutoConfirm is set, choose the default value
|
// If AutoConfirm is set, choose the default value
|
||||||
result := fmt.Sprint(out.Option.Default)
|
if ci.AutoConfirm {
|
||||||
Debugf(nil, "Auto confirm is set, choosing default %q for state %q", result, out.State)
|
result := fmt.Sprint(out.Option.Default)
|
||||||
return ConfigResult(out.State, result)
|
Debugf(nil, "Auto confirm is set, choosing default %q for state %q", result, out.State)
|
||||||
|
return ConfigResult(out.State, result)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
@ -245,7 +245,6 @@ func OkRemote(name string) bool {
|
|||||||
//
|
//
|
||||||
// The is the user interface loop that drives the post configuration backend config.
|
// The is the user interface loop that drives the post configuration backend config.
|
||||||
func PostConfig(ctx context.Context, name string, m configmap.Mapper, ri *fs.RegInfo) error {
|
func PostConfig(ctx context.Context, name string, m configmap.Mapper, ri *fs.RegInfo) error {
|
||||||
// FIXME if doing authorize, stop when we've got to the OAuth
|
|
||||||
if ri.Config == nil {
|
if ri.Config == nil {
|
||||||
return errors.New("backend doesn't support reconnect or authorize")
|
return errors.New("backend doesn't support reconnect or authorize")
|
||||||
}
|
}
|
||||||
@ -267,6 +266,7 @@ func PostConfig(ctx context.Context, name string, m configmap.Mapper, ri *fs.Reg
|
|||||||
in.State = out.State
|
in.State = out.State
|
||||||
in.Result = out.Result
|
in.Result = out.Result
|
||||||
if out.Option != nil {
|
if out.Option != nil {
|
||||||
|
fs.Debugf(name, "config: reading config item named %q", out.Option.Name)
|
||||||
if out.Option.Default == nil {
|
if out.Option.Default == nil {
|
||||||
out.Option.Default = ""
|
out.Option.Default = ""
|
||||||
}
|
}
|
||||||
|
@ -455,14 +455,14 @@ func ConfigOAuth(ctx context.Context, name string, m configmap.Mapper, ri *fs.Re
|
|||||||
// See if already have a token
|
// See if already have a token
|
||||||
tokenString, ok := m.Get("token")
|
tokenString, ok := m.Get("token")
|
||||||
if ok && tokenString != "" {
|
if ok && tokenString != "" {
|
||||||
return fs.ConfigConfirm(newState("*oauth-confirm"), true, "Already have a token - refresh?")
|
return fs.ConfigConfirm(newState("*oauth-confirm"), true, "config_refresh_token", "Already have a token - refresh?")
|
||||||
}
|
}
|
||||||
return fs.ConfigGoto(newState("*oauth-confirm"))
|
return fs.ConfigGoto(newState("*oauth-confirm"))
|
||||||
case "*oauth-confirm":
|
case "*oauth-confirm":
|
||||||
if in.Result == "false" {
|
if in.Result == "false" {
|
||||||
return fs.ConfigGoto(newState("*oauth-done"))
|
return fs.ConfigGoto(newState("*oauth-done"))
|
||||||
}
|
}
|
||||||
return fs.ConfigConfirm(newState("*oauth-islocal"), true, "Use auto config?\n * Say Y if not sure\n * Say N if you are working on a remote or headless machine\n")
|
return fs.ConfigConfirm(newState("*oauth-islocal"), true, "config_is_local", "Use auto config?\n * Say Y if not sure\n * Say N if you are working on a remote or headless machine\n")
|
||||||
case "*oauth-islocal":
|
case "*oauth-islocal":
|
||||||
if in.Result == "true" {
|
if in.Result == "true" {
|
||||||
return fs.ConfigGoto(newState("*oauth-do"))
|
return fs.ConfigGoto(newState("*oauth-do"))
|
||||||
@ -478,7 +478,7 @@ func ConfigOAuth(ctx context.Context, name string, m configmap.Mapper, ri *fs.Re
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return fs.ConfigInput(newState("*oauth-do"), fmt.Sprintf("Verification code\n\nGo to this URL, authenticate then paste the code here.\n\n%s\n", authURL))
|
return fs.ConfigInput(newState("*oauth-do"), "config_verification_code", fmt.Sprintf("Verification code\n\nGo to this URL, authenticate then paste the code here.\n\n%s\n", authURL))
|
||||||
}
|
}
|
||||||
var out strings.Builder
|
var out strings.Builder
|
||||||
fmt.Fprintf(&out, `For this to work, you will need rclone available on a machine that has
|
fmt.Fprintf(&out, `For this to work, you will need rclone available on a machine that has
|
||||||
@ -508,7 +508,7 @@ version recommended):
|
|||||||
fmt.Fprintf(&out, "\trclone authorize %q\n", ri.Name)
|
fmt.Fprintf(&out, "\trclone authorize %q\n", ri.Name)
|
||||||
}
|
}
|
||||||
fmt.Fprintln(&out, "\nThen paste the result.")
|
fmt.Fprintln(&out, "\nThen paste the result.")
|
||||||
return fs.ConfigInput(newState("*oauth-authorize"), out.String())
|
return fs.ConfigInput(newState("*oauth-authorize"), "config_token", out.String())
|
||||||
case "*oauth-authorize":
|
case "*oauth-authorize":
|
||||||
// Read the updates to the config
|
// Read the updates to the config
|
||||||
outM := configmap.Simple{}
|
outM := configmap.Simple{}
|
||||||
|
Loading…
Reference in New Issue
Block a user