mirror of
https://github.com/photoprism/photoprism.git
synced 2025-09-27 05:08:13 +08:00
CLI: Add photoprism connect command
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
@@ -36,7 +36,7 @@ var version = "development"
|
|||||||
var log = event.Log
|
var log = event.Log
|
||||||
|
|
||||||
const appName = "PhotoPrism"
|
const appName = "PhotoPrism"
|
||||||
const appAbout = "PhotoPrism® CE"
|
const appAbout = "PhotoPrism®"
|
||||||
const appEdition = "ce"
|
const appEdition = "ce"
|
||||||
const appDescription = "PhotoPrism® is an AI-Powered Photos App for the Decentralized Web." +
|
const appDescription = "PhotoPrism® is an AI-Powered Photos App for the Decentralized Web." +
|
||||||
" It makes use of the latest technologies to tag and find pictures automatically without getting in your way." +
|
" It makes use of the latest technologies to tag and find pictures automatically without getting in your way." +
|
||||||
|
@@ -65,6 +65,7 @@ var PhotoPrism = []cli.Command{
|
|||||||
ShowCommand,
|
ShowCommand,
|
||||||
VersionCommand,
|
VersionCommand,
|
||||||
ShowConfigCommand,
|
ShowConfigCommand,
|
||||||
|
ConnectCommand,
|
||||||
}
|
}
|
||||||
|
|
||||||
// childAlreadyRunning tests if a .pid file at filePath is a running process.
|
// childAlreadyRunning tests if a .pid file at filePath is a running process.
|
||||||
|
36
internal/commands/connect.go
Normal file
36
internal/commands/connect.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
|
||||||
|
"github.com/photoprism/photoprism/internal/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConnectCommand configures the command name, flags, and action.
|
||||||
|
var ConnectCommand = cli.Command{
|
||||||
|
Name: "connect",
|
||||||
|
Usage: "Connects your membership account",
|
||||||
|
ArgsUsage: "[activation code]",
|
||||||
|
Action: connectAction,
|
||||||
|
}
|
||||||
|
|
||||||
|
// connectAction connects your membership account.
|
||||||
|
func connectAction(ctx *cli.Context) error {
|
||||||
|
return CallWithDependencies(ctx, func(conf *config.Config) error {
|
||||||
|
token := ctx.Args().First()
|
||||||
|
|
||||||
|
// Fail if no code was provided.
|
||||||
|
if token == "" {
|
||||||
|
return cli.ShowSubcommandHelp(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to hub.
|
||||||
|
if err := conf.ResyncHub(token); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("successfully connected your account")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
@@ -73,7 +73,7 @@ type ClientConfig struct {
|
|||||||
Countries entity.Countries `json:"countries"`
|
Countries entity.Countries `json:"countries"`
|
||||||
People entity.People `json:"people"`
|
People entity.People `json:"people"`
|
||||||
Thumbs ThumbSizes `json:"thumbs"`
|
Thumbs ThumbSizes `json:"thumbs"`
|
||||||
License string `json:"license"`
|
Membership string `json:"membership"`
|
||||||
Customer string `json:"customer"`
|
Customer string `json:"customer"`
|
||||||
MapKey string `json:"mapKey"`
|
MapKey string `json:"mapKey"`
|
||||||
DownloadToken string `json:"downloadToken,omitempty"`
|
DownloadToken string `json:"downloadToken,omitempty"`
|
||||||
@@ -289,7 +289,7 @@ func (c *Config) ClientPublic() ClientConfig {
|
|||||||
Lenses: entity.Lenses{},
|
Lenses: entity.Lenses{},
|
||||||
Countries: entity.Countries{},
|
Countries: entity.Countries{},
|
||||||
People: entity.People{},
|
People: entity.People{},
|
||||||
License: c.Hub().Status,
|
Membership: c.Hub().Membership(),
|
||||||
Customer: "",
|
Customer: "",
|
||||||
MapKey: "",
|
MapKey: "",
|
||||||
Thumbs: Thumbs,
|
Thumbs: Thumbs,
|
||||||
@@ -378,7 +378,7 @@ func (c *Config) ClientShare() ClientConfig {
|
|||||||
People: entity.People{},
|
People: entity.People{},
|
||||||
Colors: colors.All.List(),
|
Colors: colors.All.List(),
|
||||||
Thumbs: Thumbs,
|
Thumbs: Thumbs,
|
||||||
License: c.Hub().Status,
|
Membership: c.Hub().Membership(),
|
||||||
Customer: c.Hub().Customer(),
|
Customer: c.Hub().Customer(),
|
||||||
MapKey: c.Hub().MapKey(),
|
MapKey: c.Hub().MapKey(),
|
||||||
DownloadToken: c.DownloadToken(),
|
DownloadToken: c.DownloadToken(),
|
||||||
@@ -471,7 +471,7 @@ func (c *Config) ClientUser(withSettings bool) ClientConfig {
|
|||||||
People: entity.People{},
|
People: entity.People{},
|
||||||
Colors: colors.All.List(),
|
Colors: colors.All.List(),
|
||||||
Thumbs: Thumbs,
|
Thumbs: Thumbs,
|
||||||
License: c.Hub().Status,
|
Membership: c.Hub().Membership(),
|
||||||
Customer: c.Hub().Customer(),
|
Customer: c.Hub().Customer(),
|
||||||
MapKey: c.Hub().MapKey(),
|
MapKey: c.Hub().MapKey(),
|
||||||
DownloadToken: c.DownloadToken(),
|
DownloadToken: c.DownloadToken(),
|
||||||
|
@@ -350,9 +350,7 @@ func (c *Config) Name() string {
|
|||||||
// About returns the app about string.
|
// About returns the app about string.
|
||||||
func (c *Config) About() string {
|
func (c *Config) About() string {
|
||||||
if c.options.About == "" {
|
if c.options.About == "" {
|
||||||
return "PhotoPrism® Dev"
|
return "PhotoPrism®"
|
||||||
} else if strings.HasSuffix(c.options.About, "CE") && c.Sponsor() {
|
|
||||||
return strings.Replace(c.options.About, "CE", "Plus", 1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.options.About
|
return c.options.About
|
||||||
@@ -563,7 +561,7 @@ func (c *Config) Sponsor() bool {
|
|||||||
if Sponsor || c.options.Sponsor {
|
if Sponsor || c.options.Sponsor {
|
||||||
return true
|
return true
|
||||||
} else if c.hub != nil {
|
} else if c.hub != nil {
|
||||||
Sponsor = c.Hub().Plus()
|
Sponsor = c.Hub().Sponsor()
|
||||||
}
|
}
|
||||||
|
|
||||||
return Sponsor
|
return Sponsor
|
||||||
@@ -757,13 +755,25 @@ func (c *Config) ResolutionLimit() int {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateHub renews backend api credentials for maps and places without a token.
|
// UpdateHub renews backend api credentials with an optional activation code.
|
||||||
func (c *Config) UpdateHub() {
|
func (c *Config) UpdateHub() {
|
||||||
_ = c.ResyncHub("")
|
if c.hub == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if token := os.Getenv(EnvVar("CONNECT")); token != "" && !c.Hub().Sponsor() {
|
||||||
|
_ = c.ResyncHub(token)
|
||||||
|
} else {
|
||||||
|
_ = c.ResyncHub("")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResyncHub renews backend api credentials for maps and places with an optional token.
|
// ResyncHub renews backend api credentials for maps and places with an optional token.
|
||||||
func (c *Config) ResyncHub(token string) error {
|
func (c *Config) ResyncHub(token string) error {
|
||||||
|
if c.hub == nil {
|
||||||
|
return fmt.Errorf("hub is not initialized")
|
||||||
|
}
|
||||||
|
|
||||||
if err := c.hub.ReSync(token); err != nil {
|
if err := c.hub.ReSync(token); err != nil {
|
||||||
log.Debugf("config: %s, see https://docs.photoprism.app/getting-started/troubleshooting/firewall/", err)
|
log.Debugf("config: %s, see https://docs.photoprism.app/getting-started/troubleshooting/firewall/", err)
|
||||||
if token != "" {
|
if token != "" {
|
||||||
|
@@ -73,7 +73,7 @@ func TestConfig_About(t *testing.T) {
|
|||||||
c := NewConfig(CliTestContext())
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
name := c.About()
|
name := c.About()
|
||||||
assert.Equal(t, "PhotoPrism® Dev", name)
|
assert.Equal(t, "PhotoPrism®", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfig_Edition(t *testing.T) {
|
func TestConfig_Edition(t *testing.T) {
|
||||||
|
@@ -24,10 +24,12 @@ import (
|
|||||||
"github.com/photoprism/photoprism/pkg/fs"
|
"github.com/photoprism/photoprism/pkg/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Status string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
StatusUnknown = ""
|
StatusUnknown Status = ""
|
||||||
StatusNew = "unregistered"
|
StatusNew Status = "unregistered"
|
||||||
StatusCommunity = "ce"
|
StatusCommunity Status = "ce"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config represents backend api credentials for maps & geodata.
|
// Config represents backend api credentials for maps & geodata.
|
||||||
@@ -39,7 +41,7 @@ type Config struct {
|
|||||||
Session string `json:"session" yaml:"Session"`
|
Session string `json:"session" yaml:"Session"`
|
||||||
session *Session `json:"-" yaml:"-"`
|
session *Session `json:"-" yaml:"-"`
|
||||||
sessionMu sync.Mutex `json:"-" yaml:"-"`
|
sessionMu sync.Mutex `json:"-" yaml:"-"`
|
||||||
Status string `json:"status" yaml:"Status"`
|
Status Status `json:"status" yaml:"Status"`
|
||||||
Serial string `json:"serial" yaml:"Serial"`
|
Serial string `json:"serial" yaml:"Serial"`
|
||||||
Env string `json:"-" yaml:"-"`
|
Env string `json:"-" yaml:"-"`
|
||||||
UserAgent string `json:"-" yaml:"-"`
|
UserAgent string `json:"-" yaml:"-"`
|
||||||
@@ -71,6 +73,15 @@ func (c *Config) MapKey() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Membership returns the membership level as string.
|
||||||
|
func (c *Config) Membership() string {
|
||||||
|
if !c.Sponsor() {
|
||||||
|
return string(StatusCommunity)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(c.Status)
|
||||||
|
}
|
||||||
|
|
||||||
// Customer returns the customer name.
|
// Customer returns the customer name.
|
||||||
func (c *Config) Customer() string {
|
func (c *Config) Customer() string {
|
||||||
if sess, err := c.DecodeSession(true); err != nil {
|
if sess, err := c.DecodeSession(true); err != nil {
|
||||||
@@ -86,8 +97,8 @@ func (c *Config) Propagate() {
|
|||||||
places.Secret = c.Secret
|
places.Secret = c.Secret
|
||||||
}
|
}
|
||||||
|
|
||||||
// Plus reports if you have a community membership.
|
// Sponsor reports if you support the project.
|
||||||
func (c *Config) Plus() bool {
|
func (c *Config) Sponsor() bool {
|
||||||
switch c.Status {
|
switch c.Status {
|
||||||
case StatusUnknown, StatusNew, StatusCommunity:
|
case StatusUnknown, StatusNew, StatusCommunity:
|
||||||
return false
|
return false
|
||||||
|
@@ -13,18 +13,18 @@ func TestConfig_MapKey(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfig_Plus(t *testing.T) {
|
func TestConfig_Sponsor(t *testing.T) {
|
||||||
t.Run("Status", func(t *testing.T) {
|
t.Run("Status", func(t *testing.T) {
|
||||||
c := NewConfig("0.0.0", "testdata/new.yml", "zr58wrg19i8jfjam", "test", "PhotoPrism/Test", "test")
|
c := NewConfig("0.0.0", "testdata/new.yml", "zr58wrg19i8jfjam", "test", "PhotoPrism/Test", "test")
|
||||||
c.Key = "0e159b773c6fb779c3bf6c8ba6e322abf559dbaf"
|
c.Key = "0e159b773c6fb779c3bf6c8ba6e322abf559dbaf"
|
||||||
c.Secret = "23f0024975bd65ade06edcc8191f7fcc"
|
c.Secret = "23f0024975bd65ade06edcc8191f7fcc"
|
||||||
assert.False(t, c.Plus())
|
assert.False(t, c.Sponsor())
|
||||||
c.Status = "plus"
|
c.Status = "sponsor"
|
||||||
assert.False(t, c.Plus())
|
assert.False(t, c.Sponsor())
|
||||||
c.Session = "bde6d0cf514e5456591de5ae09d981056eb88dccf71ba268974bf2cc7b028545e7641c1dfbaa716e4d13f8b0e0d1863e64c16e1f0ac551fc85e1171a87cbda6608cbe330de9e0d5f5b0e14ff61f2ff08fee369"
|
c.Session = "bde6d0cf514e5456591de5ae09d981056eb88dccf71ba268974bf2cc7b028545e7641c1dfbaa716e4d13f8b0e0d1863e64c16e1f0ac551fc85e1171a87cbda6608cbe330de9e0d5f5b0e14ff61f2ff08fee369"
|
||||||
assert.True(t, c.Plus())
|
assert.True(t, c.Sponsor())
|
||||||
c.Status = ""
|
c.Status = ""
|
||||||
assert.False(t, c.Plus())
|
assert.False(t, c.Sponsor())
|
||||||
c.Session = ""
|
c.Session = ""
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@@ -133,7 +133,7 @@ func TestConfig_DecodeSession(t *testing.T) {
|
|||||||
assert.Equal(t, "8dd8b115d052f91ac74b1c2475e305009366c487", c.Key)
|
assert.Equal(t, "8dd8b115d052f91ac74b1c2475e305009366c487", c.Key)
|
||||||
assert.Equal(t, "ddf4ce46afbf6c16a6bd8555ab1e4efb", c.Secret)
|
assert.Equal(t, "ddf4ce46afbf6c16a6bd8555ab1e4efb", c.Secret)
|
||||||
assert.Equal(t, "7607796238c26b2d95007957b05c72d63f504346576bc2aa064a6dc54344de47d2ab38422bd1d061c067a16ef517e6054d8b7f5336c120431935518277fed45e49472aaf740cac1bc33ab2e362c767007a59e953e9973709", c.Session)
|
assert.Equal(t, "7607796238c26b2d95007957b05c72d63f504346576bc2aa064a6dc54344de47d2ab38422bd1d061c067a16ef517e6054d8b7f5336c120431935518277fed45e49472aaf740cac1bc33ab2e362c767007a59e953e9973709", c.Session)
|
||||||
assert.Equal(t, "unregistered", c.Status)
|
assert.Equal(t, Status("unregistered"), c.Status)
|
||||||
assert.Equal(t, "0.0.0", c.Version)
|
assert.Equal(t, "0.0.0", c.Version)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -149,7 +149,7 @@ func TestConfig_Load(t *testing.T) {
|
|||||||
assert.Equal(t, "b32e9ccdc90eb7c0f6f1b9fbc82b8a2b0e993304", c.Key)
|
assert.Equal(t, "b32e9ccdc90eb7c0f6f1b9fbc82b8a2b0e993304", c.Key)
|
||||||
assert.Equal(t, "5991ea36a9611e9e00a8360c10b91567", c.Secret)
|
assert.Equal(t, "5991ea36a9611e9e00a8360c10b91567", c.Secret)
|
||||||
assert.Equal(t, "3ef5685c6391a568731c8fc94ccad82d92dea60476c8b672990047c822248f45366fc0e8e812ad15e0b5ae1eb20e866235c56b", c.Session)
|
assert.Equal(t, "3ef5685c6391a568731c8fc94ccad82d92dea60476c8b672990047c822248f45366fc0e8e812ad15e0b5ae1eb20e866235c56b", c.Session)
|
||||||
assert.Equal(t, "unregistered", c.Status)
|
assert.Equal(t, Status("unregistered"), c.Status)
|
||||||
assert.Equal(t, "0.0.0", c.Version)
|
assert.Equal(t, "0.0.0", c.Version)
|
||||||
})
|
})
|
||||||
t.Run("hub2.yml", func(t *testing.T) {
|
t.Run("hub2.yml", func(t *testing.T) {
|
||||||
@@ -162,7 +162,7 @@ func TestConfig_Load(t *testing.T) {
|
|||||||
assert.Equal(t, "ab66cb5cfb3658dbea0a1433df048d900934ac68", c.Key)
|
assert.Equal(t, "ab66cb5cfb3658dbea0a1433df048d900934ac68", c.Key)
|
||||||
assert.Equal(t, "6b0f8440fe307d3120b3a4366350094b", c.Secret)
|
assert.Equal(t, "6b0f8440fe307d3120b3a4366350094b", c.Secret)
|
||||||
assert.Equal(t, "c0ca88fc3094b70a1947b5b10f980a420cd6b1542a20f6f26ecc6a16f340473b9fb16b80be1078e86d886b3a8d46bf8184d147", c.Session)
|
assert.Equal(t, "c0ca88fc3094b70a1947b5b10f980a420cd6b1542a20f6f26ecc6a16f340473b9fb16b80be1078e86d886b3a8d46bf8184d147", c.Session)
|
||||||
assert.Equal(t, "unregistered", c.Status)
|
assert.Equal(t, Status("unregistered"), c.Status)
|
||||||
assert.Equal(t, "200925-f8e2b580-Darwin-i386-DEBUG", c.Version)
|
assert.Equal(t, "200925-f8e2b580-Darwin-i386-DEBUG", c.Version)
|
||||||
})
|
})
|
||||||
t.Run("not existing filename", func(t *testing.T) {
|
t.Run("not existing filename", func(t *testing.T) {
|
||||||
@@ -191,7 +191,7 @@ func TestConfig_Save(t *testing.T) {
|
|||||||
assert.Equal(t, "b32e9ccdc90eb7c0f6f1b9fbc82b8a2b0e993304", c.Key)
|
assert.Equal(t, "b32e9ccdc90eb7c0f6f1b9fbc82b8a2b0e993304", c.Key)
|
||||||
assert.Equal(t, "5991ea36a9611e9e00a8360c10b91567", c.Secret)
|
assert.Equal(t, "5991ea36a9611e9e00a8360c10b91567", c.Secret)
|
||||||
assert.Equal(t, "3ef5685c6391a568731c8fc94ccad82d92dea60476c8b672990047c822248f45366fc0e8e812ad15e0b5ae1eb20e866235c56b", c.Session)
|
assert.Equal(t, "3ef5685c6391a568731c8fc94ccad82d92dea60476c8b672990047c822248f45366fc0e8e812ad15e0b5ae1eb20e866235c56b", c.Session)
|
||||||
assert.Equal(t, "unregistered", c.Status)
|
assert.Equal(t, Status("unregistered"), c.Status)
|
||||||
assert.Equal(t, "0.0.0", c.Version)
|
assert.Equal(t, "0.0.0", c.Version)
|
||||||
|
|
||||||
c.FileName = "testdata/hub-save.yml"
|
c.FileName = "testdata/hub-save.yml"
|
||||||
@@ -205,7 +205,7 @@ func TestConfig_Save(t *testing.T) {
|
|||||||
assert.Equal(t, "b32e9ccdc90eb7c0f6f1b9fbc82b8a2b0e993304", c.Key)
|
assert.Equal(t, "b32e9ccdc90eb7c0f6f1b9fbc82b8a2b0e993304", c.Key)
|
||||||
assert.Equal(t, "5991ea36a9611e9e00a8360c10b91567", c.Secret)
|
assert.Equal(t, "5991ea36a9611e9e00a8360c10b91567", c.Secret)
|
||||||
assert.Equal(t, "3ef5685c6391a568731c8fc94ccad82d92dea60476c8b672990047c822248f45366fc0e8e812ad15e0b5ae1eb20e866235c56b", c.Session)
|
assert.Equal(t, "3ef5685c6391a568731c8fc94ccad82d92dea60476c8b672990047c822248f45366fc0e8e812ad15e0b5ae1eb20e866235c56b", c.Session)
|
||||||
assert.Equal(t, "unregistered", c.Status)
|
assert.Equal(t, Status("unregistered"), c.Status)
|
||||||
assert.Equal(t, "0.0.0", c.Version)
|
assert.Equal(t, "0.0.0", c.Version)
|
||||||
|
|
||||||
assert.FileExists(t, "testdata/hub-save.yml")
|
assert.FileExists(t, "testdata/hub-save.yml")
|
||||||
@@ -217,7 +217,7 @@ func TestConfig_Save(t *testing.T) {
|
|||||||
assert.Equal(t, "b32e9ccdc90eb7c0f6f1b9fbc82b8a2b0e993304", c.Key)
|
assert.Equal(t, "b32e9ccdc90eb7c0f6f1b9fbc82b8a2b0e993304", c.Key)
|
||||||
assert.Equal(t, "5991ea36a9611e9e00a8360c10b91567", c.Secret)
|
assert.Equal(t, "5991ea36a9611e9e00a8360c10b91567", c.Secret)
|
||||||
assert.Equal(t, "3ef5685c6391a568731c8fc94ccad82d92dea60476c8b672990047c822248f45366fc0e8e812ad15e0b5ae1eb20e866235c56b", c.Session)
|
assert.Equal(t, "3ef5685c6391a568731c8fc94ccad82d92dea60476c8b672990047c822248f45366fc0e8e812ad15e0b5ae1eb20e866235c56b", c.Session)
|
||||||
assert.Equal(t, "unregistered", c.Status)
|
assert.Equal(t, Status("unregistered"), c.Status)
|
||||||
assert.Equal(t, "0.0.0", c.Version)
|
assert.Equal(t, "0.0.0", c.Version)
|
||||||
})
|
})
|
||||||
t.Run("not existing filename", func(t *testing.T) {
|
t.Run("not existing filename", func(t *testing.T) {
|
||||||
|
Reference in New Issue
Block a user