CLI: Add photoprism connect command

Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
Michael Mayer 2023-03-24 19:35:29 +01:00
parent 653f381f81
commit 84d1e7de1d
9 changed files with 88 additions and 30 deletions

View file

@ -36,7 +36,7 @@ var version = "development"
var log = event.Log
const appName = "PhotoPrism"
const appAbout = "PhotoPrism® CE"
const appAbout = "PhotoPrism®"
const appEdition = "ce"
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." +

View file

@ -65,6 +65,7 @@ var PhotoPrism = []cli.Command{
ShowCommand,
VersionCommand,
ShowConfigCommand,
ConnectCommand,
}
// childAlreadyRunning tests if a .pid file at filePath is a running process.

View 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
})
}

View file

@ -73,7 +73,7 @@ type ClientConfig struct {
Countries entity.Countries `json:"countries"`
People entity.People `json:"people"`
Thumbs ThumbSizes `json:"thumbs"`
License string `json:"license"`
Membership string `json:"membership"`
Customer string `json:"customer"`
MapKey string `json:"mapKey"`
DownloadToken string `json:"downloadToken,omitempty"`
@ -289,7 +289,7 @@ func (c *Config) ClientPublic() ClientConfig {
Lenses: entity.Lenses{},
Countries: entity.Countries{},
People: entity.People{},
License: c.Hub().Status,
Membership: c.Hub().Membership(),
Customer: "",
MapKey: "",
Thumbs: Thumbs,
@ -378,7 +378,7 @@ func (c *Config) ClientShare() ClientConfig {
People: entity.People{},
Colors: colors.All.List(),
Thumbs: Thumbs,
License: c.Hub().Status,
Membership: c.Hub().Membership(),
Customer: c.Hub().Customer(),
MapKey: c.Hub().MapKey(),
DownloadToken: c.DownloadToken(),
@ -471,7 +471,7 @@ func (c *Config) ClientUser(withSettings bool) ClientConfig {
People: entity.People{},
Colors: colors.All.List(),
Thumbs: Thumbs,
License: c.Hub().Status,
Membership: c.Hub().Membership(),
Customer: c.Hub().Customer(),
MapKey: c.Hub().MapKey(),
DownloadToken: c.DownloadToken(),

View file

@ -350,9 +350,7 @@ func (c *Config) Name() string {
// About returns the app about string.
func (c *Config) About() string {
if c.options.About == "" {
return "PhotoPrism® Dev"
} else if strings.HasSuffix(c.options.About, "CE") && c.Sponsor() {
return strings.Replace(c.options.About, "CE", "Plus", 1)
return "PhotoPrism®"
}
return c.options.About
@ -563,7 +561,7 @@ func (c *Config) Sponsor() bool {
if Sponsor || c.options.Sponsor {
return true
} else if c.hub != nil {
Sponsor = c.Hub().Plus()
Sponsor = c.Hub().Sponsor()
}
return Sponsor
@ -757,13 +755,25 @@ func (c *Config) ResolutionLimit() int {
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() {
_ = 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.
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 {
log.Debugf("config: %s, see https://docs.photoprism.app/getting-started/troubleshooting/firewall/", err)
if token != "" {

View file

@ -73,7 +73,7 @@ func TestConfig_About(t *testing.T) {
c := NewConfig(CliTestContext())
name := c.About()
assert.Equal(t, "PhotoPrism® Dev", name)
assert.Equal(t, "PhotoPrism®", name)
}
func TestConfig_Edition(t *testing.T) {

View file

@ -24,10 +24,12 @@ import (
"github.com/photoprism/photoprism/pkg/fs"
)
type Status string
const (
StatusUnknown = ""
StatusNew = "unregistered"
StatusCommunity = "ce"
StatusUnknown Status = ""
StatusNew Status = "unregistered"
StatusCommunity Status = "ce"
)
// Config represents backend api credentials for maps & geodata.
@ -39,7 +41,7 @@ type Config struct {
Session string `json:"session" yaml:"Session"`
session *Session `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"`
Env 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.
func (c *Config) Customer() string {
if sess, err := c.DecodeSession(true); err != nil {
@ -86,8 +97,8 @@ func (c *Config) Propagate() {
places.Secret = c.Secret
}
// Plus reports if you have a community membership.
func (c *Config) Plus() bool {
// Sponsor reports if you support the project.
func (c *Config) Sponsor() bool {
switch c.Status {
case StatusUnknown, StatusNew, StatusCommunity:
return false

View file

@ -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) {
c := NewConfig("0.0.0", "testdata/new.yml", "zr58wrg19i8jfjam", "test", "PhotoPrism/Test", "test")
c.Key = "0e159b773c6fb779c3bf6c8ba6e322abf559dbaf"
c.Secret = "23f0024975bd65ade06edcc8191f7fcc"
assert.False(t, c.Plus())
c.Status = "plus"
assert.False(t, c.Plus())
assert.False(t, c.Sponsor())
c.Status = "sponsor"
assert.False(t, c.Sponsor())
c.Session = "bde6d0cf514e5456591de5ae09d981056eb88dccf71ba268974bf2cc7b028545e7641c1dfbaa716e4d13f8b0e0d1863e64c16e1f0ac551fc85e1171a87cbda6608cbe330de9e0d5f5b0e14ff61f2ff08fee369"
assert.True(t, c.Plus())
assert.True(t, c.Sponsor())
c.Status = ""
assert.False(t, c.Plus())
assert.False(t, c.Sponsor())
c.Session = ""
})
}

View file

@ -133,7 +133,7 @@ func TestConfig_DecodeSession(t *testing.T) {
assert.Equal(t, "8dd8b115d052f91ac74b1c2475e305009366c487", c.Key)
assert.Equal(t, "ddf4ce46afbf6c16a6bd8555ab1e4efb", c.Secret)
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)
})
}
@ -149,7 +149,7 @@ func TestConfig_Load(t *testing.T) {
assert.Equal(t, "b32e9ccdc90eb7c0f6f1b9fbc82b8a2b0e993304", c.Key)
assert.Equal(t, "5991ea36a9611e9e00a8360c10b91567", c.Secret)
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)
})
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, "6b0f8440fe307d3120b3a4366350094b", c.Secret)
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)
})
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, "5991ea36a9611e9e00a8360c10b91567", c.Secret)
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)
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, "5991ea36a9611e9e00a8360c10b91567", c.Secret)
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.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, "5991ea36a9611e9e00a8360c10b91567", c.Secret)
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)
})
t.Run("not existing filename", func(t *testing.T) {