Frontend: Add upgrade page and update about page
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
parent
0014e104fe
commit
53232b53c0
12
frontend/package-lock.json
generated
12
frontend/package-lock.json
generated
|
@ -2467,9 +2467,9 @@
|
|||
"integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg=="
|
||||
},
|
||||
"node_modules/@vvo/tzdb": {
|
||||
"version": "6.76.0",
|
||||
"resolved": "https://registry.npmjs.org/@vvo/tzdb/-/tzdb-6.76.0.tgz",
|
||||
"integrity": "sha512-hDVkAaqDA5ti5izUiPFk7tYWu12D3MyM7BgCkJb++KSUpJbhMiEI+um2PhmWvMhXt5Fwz0OAIGpV5Xtfv63HVw=="
|
||||
"version": "6.77.0",
|
||||
"resolved": "https://registry.npmjs.org/@vvo/tzdb/-/tzdb-6.77.0.tgz",
|
||||
"integrity": "sha512-t7aN3GAznzt8fQ5enJiM3C7HKPEDBoqKExp2W7nYu2AgS0J9FfMk6rwWhL2jjTe0+27REmO9C+TL3XM2evileQ=="
|
||||
},
|
||||
"node_modules/@webassemblyjs/ast": {
|
||||
"version": "1.11.1",
|
||||
|
@ -8355,9 +8355,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/minipass": {
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.4.tgz",
|
||||
"integrity": "sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==",
|
||||
"version": "3.3.6",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
|
||||
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
|
|
|
@ -34,12 +34,13 @@ import People from "page/people.vue";
|
|||
import Library from "page/library.vue";
|
||||
import Settings from "page/settings.vue";
|
||||
import Login from "page/login.vue";
|
||||
import Connect from "page/connect.vue";
|
||||
import Discover from "page/discover.vue";
|
||||
import About from "page/about/about.vue";
|
||||
import Feedback from "page/about/feedback.vue";
|
||||
import License from "page/about/license.vue";
|
||||
import Help from "page/help.vue";
|
||||
import Upgrade from "page/upgrade.vue";
|
||||
import Connect from "page/connect.vue";
|
||||
import { $gettext } from "common/vm";
|
||||
import { config, session } from "./session";
|
||||
|
||||
|
@ -91,6 +92,16 @@ export default [
|
|||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "upgrade",
|
||||
path: "/upgrade",
|
||||
component: Upgrade,
|
||||
meta: {
|
||||
title: siteTitle,
|
||||
auth: true,
|
||||
admin: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "connect",
|
||||
path: "/connect/:name/:token",
|
||||
|
|
|
@ -471,7 +471,7 @@
|
|||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
|
||||
<v-list-tile v-show="!isPublic && isAdmin" :to="{ name: 'feedback' }" :exact="true" class="nav-feedback"
|
||||
<v-list-tile v-show="!isPublic && isAdmin && isSponsor" :to="{ name: 'feedback' }" :exact="true" class="nav-feedback"
|
||||
@click.stop="">
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title :class="`menu-item ${rtl ? '--rtl' : ''}`">
|
||||
|
@ -502,6 +502,17 @@
|
|||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
|
||||
<v-list-tile v-show="isAdmin && !isPublic && !isDemo && !isSponsor" :to="{ name: 'upgrade' }" class="nav-upgrade" @click.stop="">
|
||||
<v-list-tile-action :title="$gettext('Upgrade')">
|
||||
<v-icon>diamond</v-icon>
|
||||
</v-list-tile-action>
|
||||
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
<translate key="Upgrade">Upgrade</translate>
|
||||
</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
</v-list>
|
||||
|
||||
<v-list class="p-user-box">
|
||||
|
@ -622,21 +633,16 @@
|
|||
<translate>Logs</translate>
|
||||
</router-link>
|
||||
</div>
|
||||
<!-- div v-if="auth && $config.feature('account') && !routeName('settings')" class="menu-action nav-account">
|
||||
<router-link :to="{ name: 'settings_account' }">
|
||||
<v-icon>person</v-icon>
|
||||
<translate>Account</translate>
|
||||
<div v-if="!isPublic && !isSponsor && isAdmin" class="menu-action nav-membership">
|
||||
<router-link :to="{ name: 'upgrade' }">
|
||||
<v-icon>diamond</v-icon>
|
||||
<translate>Upgrade</translate>
|
||||
</router-link>
|
||||
</div -->
|
||||
</div>
|
||||
<div class="menu-action nav-manual"><a href="https://link.photoprism.app/docs" target="_blank">
|
||||
<v-icon>auto_stories</v-icon>
|
||||
<translate>User Guide</translate>
|
||||
</a></div>
|
||||
<div v-if="!isSponsor && isAdmin" class="menu-action nav-membership"><a href="https://link.photoprism.app/membership"
|
||||
target="_blank">
|
||||
<v-icon>workspace_premium</v-icon>
|
||||
<translate>Become a sponsor</translate>
|
||||
</a></div>
|
||||
<div v-if="config.legalUrl && isSponsor" class="menu-action nav-legal"><a :href="config.legalUrl"
|
||||
target="_blank">
|
||||
<v-icon>info</v-icon>
|
||||
|
|
|
@ -1,47 +1,49 @@
|
|||
<template>
|
||||
<v-dialog :value="show" lazy persistent max-width="500" class="modal-dialog sponsor-dialog" @keydown.esc="close">
|
||||
<v-dialog :value="show" lazy persistent max-width="575" class="modal-dialog sponsor-dialog" @keydown.esc="close">
|
||||
<v-card raised elevation="24">
|
||||
<v-card-title primary-title class="pb-0">
|
||||
<v-layout row wrap>
|
||||
<v-flex xs9>
|
||||
<v-card-title primary-title class="px-2 pb-0">
|
||||
<v-layout row wrap class="px-2">
|
||||
<v-flex xs10>
|
||||
<h3 class="title mb-0">
|
||||
<translate>Become a sponsor</translate>
|
||||
<translate>Support Our Mission</translate>
|
||||
</h3>
|
||||
</v-flex>
|
||||
<v-flex xs3 text-xs-right>
|
||||
<v-icon color="secondary-dark">$vuetify.icons.sponsor</v-icon>
|
||||
<v-flex xs2 text-xs-right>
|
||||
<v-icon color="secondary-dark">diamond</v-icon>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<p class="body-1">
|
||||
<translate>This currently is a sponsor feature to thank everyone who supports the development of this application.</translate>
|
||||
<translate>We'll let you know how to enable it when you sign up on Patreon or GitHub Sponsors.</translate>
|
||||
</p>
|
||||
<p class="body-1">
|
||||
<translate>Your continued support helps us provide regular updates and services like world maps.</translate>
|
||||
</p>
|
||||
<p class="body-1">
|
||||
<translate>Feel free to contact us at hello@photoprism.app if you have any questions.</translate>
|
||||
</p>
|
||||
</v-card-text>
|
||||
<v-card-actions class="pt-0 px-3">
|
||||
<v-card-text class="px-2">
|
||||
<v-layout row wrap class="px-2">
|
||||
<v-flex xs12 sm4 :text-xs-right="!rtl" :text-sm-left="!rtl" :text-xs-left="rtl" class="py-2">
|
||||
<v-flex xs12 class="py-2">
|
||||
<p class="body-2">
|
||||
<translate>Your continued support helps us provide regular updates and remain independent, so we can fulfill our mission and protect your privacy.</translate>
|
||||
</p>
|
||||
<p class="body-1">
|
||||
<translate>Being 100% self-funded and independent, we can promise you that we will never sell your data and that we will always be transparent about our software and services.</translate>
|
||||
</p>
|
||||
<p class="body-1">
|
||||
<translate>Feel free to contact us at hello@photoprism.app if you have any questions.</translate>
|
||||
</p>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-card-text>
|
||||
<v-card-actions class="pt-0 px-2">
|
||||
<v-layout row wrap class="px-2">
|
||||
<v-flex xs12 text-xs-right class="py-2">
|
||||
<v-btn depressed color="secondary-light"
|
||||
class="action-close compact"
|
||||
@click.stop="close">
|
||||
<translate>No thanks</translate>
|
||||
</v-btn>
|
||||
</v-flex>
|
||||
<v-flex xs12 sm8 text-xs-right class="py-2">
|
||||
<v-btn depressed color="primary-button" class="white--text action-close compact"
|
||||
@click.stop="signIn">
|
||||
<translate>I'm a sponsor</translate>
|
||||
<v-btn v-if="isPublic || !isAdmin" href="https://link.photoprism.app/personal-editions"
|
||||
target="_blank"
|
||||
depressed color="primary-button" class="white--text action-about compact">
|
||||
<translate>Learn more</translate>
|
||||
</v-btn>
|
||||
<v-btn depressed color="primary-button" class="white--text action-close compact"
|
||||
@click.stop="signUp">
|
||||
<translate>Sign Up</translate>
|
||||
<v-btn v-else depressed color="primary-button" class="white--text action-upgrade compact"
|
||||
@click.stop="upgrade">
|
||||
<translate>Upgrade Now</translate>
|
||||
</v-btn>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
|
@ -57,6 +59,10 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
isPublic: this.$config.isPublic(),
|
||||
isAdmin: this.$session.isAdmin(),
|
||||
isDemo: this.$config.isDemo(),
|
||||
isSponsor: this.$config.isSponsor(),
|
||||
host: window.location.host,
|
||||
rtl: this.$rtl,
|
||||
};
|
||||
|
@ -65,12 +71,8 @@ export default {
|
|||
close() {
|
||||
this.$emit('close');
|
||||
},
|
||||
signIn() {
|
||||
window.open("https://photoprism.app/contact", "_blank");
|
||||
this.$emit('close');
|
||||
},
|
||||
signUp() {
|
||||
window.open("https://link.photoprism.app/membership", "_blank");
|
||||
upgrade() {
|
||||
this.$router.push({name: "upgrade"});
|
||||
this.$emit('close');
|
||||
},
|
||||
},
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -7,45 +7,47 @@
|
|||
|
||||
<v-spacer></v-spacer>
|
||||
|
||||
<v-btn icon href="https://photoprism.app/" target="_blank" class="action-info" :title="$gettext('About')">
|
||||
<v-icon size="26">chat</v-icon>
|
||||
<v-btn icon href="https://photoprism.app/" target="_blank" class="action-info" :title="$gettext('Learn more')">
|
||||
<v-icon size="26">info</v-icon>
|
||||
</v-btn>
|
||||
</v-toolbar>
|
||||
<v-container fluid class="px-4 pt-4 pb-1">
|
||||
<p class="body-2 text-selectable">
|
||||
<p class="subheading font-weight-bold text-selectable">
|
||||
<translate>PhotoPrism® is an AI-Powered Photos App for the Decentralized Web.</translate>
|
||||
<translate>It makes use of the latest technologies to tag and find pictures automatically without getting in your way.</translate>
|
||||
<translate>You can run it at home, on a private server, or in the cloud.</translate>
|
||||
</p>
|
||||
|
||||
<p class="body-1 text-selectable pb-1">
|
||||
<span v-if="sponsor">
|
||||
<translate>Your continued support helps us provide regular updates and remain independent, so we can fulfill our mission and protect your privacy.</translate>
|
||||
</span>
|
||||
<span v-else>
|
||||
<translate>Sponsors get access to additional features, receive direct technical support via email, and can join our private chat room on matrix.org.</translate>
|
||||
</span>
|
||||
<p class="subheading text-selectable">
|
||||
<translate>Your continued support helps us provide regular updates and remain independent, so we can fulfill our mission and protect your privacy.</translate>
|
||||
<translate>Being 100% self-funded and independent, we can promise you that we will never sell your data and that we will always be transparent about our software and services.</translate>
|
||||
</p>
|
||||
|
||||
<div v-if="!sponsor">
|
||||
<div v-if="isPublic">
|
||||
<p class="text-xs-center my-4">
|
||||
<v-btn
|
||||
href="https://link.photoprism.app/membership"
|
||||
href="https://link.photoprism.app/personal-editions"
|
||||
target="_blank"
|
||||
color="primary-button"
|
||||
class="white--text px-3 py-2 action-sponsor"
|
||||
round depressed small
|
||||
class="white--text px-3 py-2 action-upgrade"
|
||||
round depressed
|
||||
>
|
||||
<translate>Become a sponsor</translate>
|
||||
<v-icon :left="rtl" :right="!rtl" size="16" class="ml-2" dark>star</v-icon>
|
||||
<translate>Learn more</translate>
|
||||
<v-icon :left="rtl" :right="!rtl" size="18" class="ml-2" dark>diamond</v-icon>
|
||||
</v-btn>
|
||||
</p>
|
||||
|
||||
<p class="body-1 pt-2">
|
||||
<a target="_blank" href="https://link.photoprism.app/github">
|
||||
<translate>Also, please leave a star on GitHub if you like this project. It provides additional motivation to keep going.</translate>
|
||||
</a>
|
||||
</div>
|
||||
<div v-else-if="isAdmin && !isSponsor">
|
||||
<p class="text-xs-center my-4">
|
||||
<v-btn
|
||||
to="/upgrade"
|
||||
color="primary-button"
|
||||
class="white--text px-3 py-2 action-upgrade"
|
||||
round depressed
|
||||
>
|
||||
<translate>Upgrade Now</translate>
|
||||
<v-icon :left="rtl" :right="!rtl" size="18" class="ml-2" dark>diamond</v-icon>
|
||||
</v-btn>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
@ -77,12 +79,12 @@
|
|||
</ul>
|
||||
<p class="body-1 text-selectable pb-2">
|
||||
<a target="_blank" href="https://photoprism.app/contact"><translate>In addition, sponsors receive direct technical support via email.</translate></a>
|
||||
<span v-if="!sponsor">
|
||||
<span v-if="!isSponsor">
|
||||
<translate>We'll do our best to answer all your questions. In return, we ask you to back us on Patreon or GitHub Sponsors.</translate>
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<p v-if="sponsor" class="text-xs-center">
|
||||
<p v-if="isSponsor" class="text-xs-center">
|
||||
<img src="https://cdn.photoprism.app/thank-you/colorful.png" width="100%" alt="THANK YOU">
|
||||
</p>
|
||||
|
||||
|
@ -123,7 +125,10 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
rtl: this.$rtl,
|
||||
sponsor: this.$config.isSponsor(),
|
||||
isPublic: this.$config.isPublic(),
|
||||
isAdmin: this.$session.isAdmin(),
|
||||
isDemo: this.$config.isDemo(),
|
||||
isSponsor: this.$config.isSponsor(),
|
||||
};
|
||||
},
|
||||
methods: {},
|
||||
|
|
|
@ -81,7 +81,11 @@ export default {
|
|||
},
|
||||
created() {
|
||||
this.$config.load().then(() => {
|
||||
this.send();
|
||||
if (this.$config.isPublic() || !this.$session.isAdmin()) {
|
||||
this.$router.push({name: "home"});
|
||||
} else {
|
||||
this.send();
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
|
@ -112,7 +116,7 @@ export default {
|
|||
});
|
||||
} else {
|
||||
this.$notify.error(this.$gettext("Invalid parameters"));
|
||||
this.$router.push({name: "settings"});
|
||||
this.$router.push({name: "upgrade"});
|
||||
}
|
||||
|
||||
},
|
||||
|
|
|
@ -86,14 +86,14 @@
|
|||
</v-flex>
|
||||
<v-flex xs12 sm6 class="pa-0 body-2 text-xs-center text-sm-right white--text">
|
||||
<v-btn
|
||||
href="https://link.photoprism.app/membership"
|
||||
href="https://photoprism.app/"
|
||||
target="_blank"
|
||||
color="transparent"
|
||||
class="white--text px-3 py-2 ma-0 action-sponsor"
|
||||
class="white--text px-3 py-2 ma-0 action-about"
|
||||
round depressed small
|
||||
>
|
||||
<translate>Become a sponsor</translate>
|
||||
<v-icon :left="rtl" :right="!rtl" size="16" class="ml-2" dark>star</v-icon>
|
||||
<translate>Learn more</translate>
|
||||
<v-icon :left="rtl" :right="!rtl" size="16" class="ml-2" dark>diamond</v-icon>
|
||||
</v-btn>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
|
|
134
frontend/src/page/upgrade.vue
Normal file
134
frontend/src/page/upgrade.vue
Normal file
|
@ -0,0 +1,134 @@
|
|||
<template>
|
||||
<div class="p-page p-page-upgrade">
|
||||
<v-toolbar flat color="secondary" :dense="$vuetify.breakpoint.smAndDown">
|
||||
<v-toolbar-title>
|
||||
<translate>Support Our Mission</translate>
|
||||
</v-toolbar-title>
|
||||
|
||||
<v-spacer></v-spacer>
|
||||
|
||||
<v-btn icon href="https://link.photoprism.app/personal-editions" target="_blank" class="action-upgrade" :title="$gettext('Upgrade')">
|
||||
<v-icon size="26">diamond</v-icon>
|
||||
</v-btn>
|
||||
</v-toolbar>
|
||||
<v-form ref="form" v-model="valid" autocomplete="off" class="px-3 pt-3 pb-0" lazy-validation>
|
||||
<v-layout row wrap>
|
||||
<v-flex xs12 class="px-2 pt-2 pb-0">
|
||||
<p class="subheading text-selectable">
|
||||
<strong><translate>Upgrade now and enjoy our member benefits!</translate></strong>
|
||||
</p>
|
||||
<p class="subheading text-selectable">
|
||||
<translate>Your continued support helps us provide regular updates and remain independent, so we can fulfill our mission and protect your privacy.</translate>
|
||||
<translate>To upgrade, you may either enter an activation code or click on "Sign Up" to upgrade on our website:</translate>
|
||||
</p>
|
||||
</v-flex>
|
||||
<v-flex xs12 class="pa-2">
|
||||
<v-text-field v-model="form.token" flat solo hide-details return-masked-value :mask="tokenMask"
|
||||
browser-autocomplete="off"
|
||||
color="secondary-dark"
|
||||
background-color="secondary-light" :label="$gettext('Activation Code')" type="text">
|
||||
</v-text-field>
|
||||
</v-flex>
|
||||
<v-flex xs12 grow class="px-2 py-1">
|
||||
<v-btn color="secondary-light" :block="$vuetify.breakpoint.xsOnly"
|
||||
class="ml-0"
|
||||
depressed
|
||||
:disabled="busy"
|
||||
@click.stop="compare">
|
||||
<translate>Compare Features</translate>
|
||||
</v-btn>
|
||||
<v-btn v-if="form.token.length < 1" color="primary-button"
|
||||
class="white--text ml-0" :block="$vuetify.breakpoint.xsOnly"
|
||||
depressed
|
||||
:disabled="busy"
|
||||
@click.stop="upgrade">
|
||||
<translate>Sign Up</translate>
|
||||
<v-icon :right="!rtl" :left="rtl" dark>navigate_next</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else color="primary-button" :block="$vuetify.breakpoint.xsOnly"
|
||||
class="white--text ml-0"
|
||||
depressed
|
||||
:disabled="busy || form.token.length < 4"
|
||||
@click.stop="activate">
|
||||
<translate>Activate</translate>
|
||||
<v-icon :right="!rtl" :left="rtl" dark>navigate_next</v-icon>
|
||||
</v-btn>
|
||||
</v-flex>
|
||||
<v-flex xs12 class="px-2 pt-3 pb-0">
|
||||
<p class="body-1 text-selectable">
|
||||
<translate>Feel free to contact us at hello@photoprism.app if you have any questions.</translate>
|
||||
</p>
|
||||
</v-flex>
|
||||
<v-flex xs12 class="px-2 pt-3 pb-0">
|
||||
<h3 class="pb-3"><translate>Frequently Asked Questions</translate></h3>
|
||||
<p class="body-2 text-selectable">
|
||||
<translate>Shouldn't free software be free of costs?</translate>
|
||||
</p>
|
||||
<p class="body-1 text-selectable">
|
||||
<translate>Think of “free software” as in “free speech,” not as in “free beer.” The Free Software Foundation sometimes calls it “libre software,” borrowing the French or Spanish word for “free” as in freedom, to show they do not mean the software is gratis.</translate>
|
||||
</p>
|
||||
<p class="body-2 text-selectable">
|
||||
<translate>Why are some features only available to sponsors?</translate>
|
||||
</p>
|
||||
<p class="body-1 text-selectable">
|
||||
<translate>PhotoPrism is 100% self-funded. Voluntary donations do not cover the cost of a team working full time to provide you with updates, documentation, and support. It is your decision whether you want to sign up to enjoy additional benefits.</translate>
|
||||
</p>
|
||||
<p class="body-2 text-selectable">
|
||||
<translate>What functionality is generally available?</translate>
|
||||
</p>
|
||||
<p class="body-1 text-selectable">
|
||||
<translate>Our team evaluates this on an ongoing basis, depending on the support effort features and config options cause or have caused in the past, and whether they are generally needed by everyone or mainly requested by organizations and advanced users. As this allows us to make more features available to the public, we encourage all users to support our mission.</translate>
|
||||
</p>
|
||||
<p><a href="https://link.photoprism.app/membership" class="text-link" target="_blank"><translate>Learn more</translate> ›</a></p>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-form>
|
||||
<p-about-footer></p-about-footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import * as options from "options/options";
|
||||
|
||||
export default {
|
||||
name: 'PPageUpgrade',
|
||||
data() {
|
||||
return {
|
||||
success: false,
|
||||
busy: false,
|
||||
valid: false,
|
||||
error: "",
|
||||
options: options,
|
||||
isPublic: this.$config.isPublic(),
|
||||
isAdmin: this.$session.isAdmin(),
|
||||
isDemo: this.$config.isDemo(),
|
||||
isSponsor: this.$config.isSponsor(),
|
||||
rtl: this.$rtl,
|
||||
tokenMask: 'nnnn-nnnn-nnnn',
|
||||
form: {
|
||||
name: "hub",
|
||||
token: "",
|
||||
},
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.$config.load().then(() => {
|
||||
if (this.$config.isPublic() || !this.$session.isAdmin()) {
|
||||
this.$router.push({name: "home"});
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
compare() {
|
||||
window.open('https://link.photoprism.app/personal-editions', '_blank').focus();
|
||||
},
|
||||
upgrade() {
|
||||
window.location ='https://my.photoprism.app/register?upgrade='+encodeURIComponent(window.location);
|
||||
},
|
||||
activate() {
|
||||
this.$router.push({name: "connect", params: {name:"hub",token:""}});
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
2
go.mod
2
go.mod
|
@ -81,7 +81,7 @@ require (
|
|||
github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect
|
||||
github.com/aws/aws-sdk-go v1.44.115 // indirect
|
||||
github.com/cloudflare/cloudflare-go v0.52.0 // indirect
|
||||
github.com/go-acme/lego/v4 v4.9.0
|
||||
github.com/go-acme/lego/v4 v4.9.1
|
||||
github.com/golang-jwt/jwt/v4 v4.4.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect
|
||||
|
|
4
go.sum
4
go.sum
|
@ -382,8 +382,8 @@ github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjX
|
|||
github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8=
|
||||
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
|
||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-acme/lego/v4 v4.9.0 h1:8Hjj44IqRS7cigshMyFQ+0pIZvwgkG/+9A0UnNh7G8A=
|
||||
github.com/go-acme/lego/v4 v4.9.0/go.mod h1:g3JRUyWS3L/VObpp4bCxzJftKyf/Wba8QrSSnoiqjg4=
|
||||
github.com/go-acme/lego/v4 v4.9.1 h1:n9Z5MQwANeGSQKlVE3bEh9SDvAySK9oVYOKCGCESqQE=
|
||||
github.com/go-acme/lego/v4 v4.9.1/go.mod h1:g3JRUyWS3L/VObpp4bCxzJftKyf/Wba8QrSSnoiqjg4=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs=
|
||||
|
|
|
@ -71,6 +71,7 @@ type ClientConfig struct {
|
|||
Countries entity.Countries `json:"countries"`
|
||||
People entity.People `json:"people"`
|
||||
Thumbs ThumbSizes `json:"thumbs"`
|
||||
Customer string `json:"customer"`
|
||||
MapKey string `json:"mapKey"`
|
||||
DownloadToken string `json:"downloadToken,omitempty"`
|
||||
PreviewToken string `json:"previewToken,omitempty"`
|
||||
|
@ -355,6 +356,7 @@ func (c *Config) ClientShare() ClientConfig {
|
|||
Colors: colors.All.List(),
|
||||
Thumbs: Thumbs,
|
||||
MapKey: c.Hub().MapKey(),
|
||||
Customer: c.Hub().Customer(),
|
||||
DownloadToken: c.DownloadToken(),
|
||||
PreviewToken: c.PreviewToken(),
|
||||
ManifestUri: c.ClientManifestUri(),
|
||||
|
@ -441,6 +443,7 @@ func (c *Config) ClientUser(withSettings bool) ClientConfig {
|
|||
Colors: colors.All.List(),
|
||||
Thumbs: Thumbs,
|
||||
MapKey: c.Hub().MapKey(),
|
||||
Customer: c.Hub().Customer(),
|
||||
DownloadToken: c.DownloadToken(),
|
||||
PreviewToken: c.PreviewToken(),
|
||||
ManifestUri: c.ClientManifestUri(),
|
||||
|
|
|
@ -727,7 +727,7 @@ func (c *Config) UpdateHub() {
|
|||
|
||||
// ResyncHub renews backend api credentials for maps & places with an optional token.
|
||||
func (c *Config) ResyncHub(token string) error {
|
||||
if err := c.hub.Resync(token); err != nil {
|
||||
if err := c.hub.ReSync(token); err != nil {
|
||||
log.Debugf("config: %s (refresh backend api tokens)", err)
|
||||
if token != "" {
|
||||
return i18n.Error(i18n.ErrAccountConnect)
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
|
@ -31,16 +32,18 @@ const (
|
|||
|
||||
// Config represents backend api credentials for maps & geodata.
|
||||
type Config struct {
|
||||
Version string `json:"version" yaml:"Version"`
|
||||
FileName string `json:"-" yaml:"-"`
|
||||
Key string `json:"key" yaml:"Key"`
|
||||
Secret string `json:"secret" yaml:"Secret"`
|
||||
Session string `json:"session" yaml:"Session"`
|
||||
Status string `json:"status" yaml:"Status"`
|
||||
Serial string `json:"serial" yaml:"Serial"`
|
||||
Env string `json:"-" yaml:"-"`
|
||||
UserAgent string `json:"-" yaml:"-"`
|
||||
PartnerID string `json:"-" yaml:"-"`
|
||||
Version string `json:"version" yaml:"Version"`
|
||||
FileName string `json:"-" yaml:"-"`
|
||||
Key string `json:"key" yaml:"Key"`
|
||||
Secret string `json:"secret" yaml:"Secret"`
|
||||
Session string `json:"session" yaml:"Session"`
|
||||
session *Session `json:"-" yaml:"-"`
|
||||
sessionMu sync.Mutex `json:"-" yaml:"-"`
|
||||
Status string `json:"status" yaml:"Status"`
|
||||
Serial string `json:"serial" yaml:"Serial"`
|
||||
Env string `json:"-" yaml:"-"`
|
||||
UserAgent string `json:"-" yaml:"-"`
|
||||
PartnerID string `json:"-" yaml:"-"`
|
||||
}
|
||||
|
||||
// NewConfig creates a new backend api credentials instance.
|
||||
|
@ -61,13 +64,22 @@ func NewConfig(version, fileName, serial, env, userAgent, partnerId string) *Con
|
|||
|
||||
// MapKey returns the maps api key.
|
||||
func (c *Config) MapKey() string {
|
||||
if sess, err := c.DecodeSession(); err != nil {
|
||||
if sess, err := c.DecodeSession(true); err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return sess.MapKey
|
||||
}
|
||||
}
|
||||
|
||||
// Customer returns the customer name.
|
||||
func (c *Config) Customer() string {
|
||||
if sess, err := c.DecodeSession(true); err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return sess.Customer
|
||||
}
|
||||
}
|
||||
|
||||
// Propagate updates backend api credentials in other packages.
|
||||
func (c *Config) Propagate() {
|
||||
places.Key = c.Key
|
||||
|
@ -99,15 +111,28 @@ func (c *Config) Sanitize() {
|
|||
}
|
||||
|
||||
// DecodeSession decodes backend api session data.
|
||||
func (c *Config) DecodeSession() (Session, error) {
|
||||
func (c *Config) DecodeSession(cached bool) (Session, error) {
|
||||
c.sessionMu.Lock()
|
||||
defer c.sessionMu.Unlock()
|
||||
|
||||
c.Sanitize()
|
||||
|
||||
result := Session{}
|
||||
|
||||
// No session?
|
||||
if c.Session == "" {
|
||||
return result, fmt.Errorf("empty session")
|
||||
c.session = nil
|
||||
return Session{}, fmt.Errorf("empty session")
|
||||
}
|
||||
|
||||
if cached && c.session != nil {
|
||||
// Return cached session.
|
||||
return *c.session, nil
|
||||
} else {
|
||||
// Clear session cache.
|
||||
c.session = nil
|
||||
}
|
||||
|
||||
result := Session{}
|
||||
|
||||
s, err := hex.DecodeString(c.Session)
|
||||
|
||||
if err != nil {
|
||||
|
@ -140,20 +165,27 @@ func (c *Config) DecodeSession() (Session, error) {
|
|||
return result, err
|
||||
}
|
||||
|
||||
// Cache session.
|
||||
c.session = &result
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Update renews backend api credentials without a token.
|
||||
func (c *Config) Update() error {
|
||||
return c.Resync("")
|
||||
return c.ReSync("")
|
||||
}
|
||||
|
||||
// Resync renews backend api credentials with an optional token.
|
||||
func (c *Config) Resync(token string) (err error) {
|
||||
// ReSync renews backend api credentials with an optional token.
|
||||
func (c *Config) ReSync(token string) (err error) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(c.FileName), fs.ModeDir); err != nil {
|
||||
// Clear session.
|
||||
c.session = nil
|
||||
|
||||
// Make sure storage folder exists.
|
||||
if err = os.MkdirAll(filepath.Dir(c.FileName), fs.ModeDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -251,7 +283,7 @@ func (c *Config) Load() error {
|
|||
c.Sanitize()
|
||||
c.Propagate()
|
||||
|
||||
if sess, err := c.DecodeSession(); err != nil {
|
||||
if sess, err := c.DecodeSession(false); err != nil {
|
||||
return err
|
||||
} else if sess.Expired() {
|
||||
return errors.New("session expired")
|
||||
|
|
|
@ -82,10 +82,12 @@ func TestConfig_Refresh(t *testing.T) {
|
|||
assert.Len(t, c.Secret, 32)
|
||||
assert.Equal(t, "0.0.0", c.Version)
|
||||
|
||||
if sess, err := c.DecodeSession(); err != nil {
|
||||
if sess, err := c.DecodeSession(false); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if sess.Expired() {
|
||||
t.Fatalf("session expired: %+v", sess)
|
||||
} else {
|
||||
t.Logf("(1) session: %#v", sess)
|
||||
}
|
||||
|
||||
if err := c.Save(); err != nil {
|
||||
|
@ -104,18 +106,18 @@ func TestConfig_Refresh(t *testing.T) {
|
|||
assert.Len(t, c.Secret, 32)
|
||||
assert.Equal(t, "0.0.0", c.Version)
|
||||
|
||||
if sess, err := c.DecodeSession(); err != nil {
|
||||
if sess, err := c.DecodeSession(false); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if sess.Expired() {
|
||||
t.Fatal("session expired")
|
||||
} else {
|
||||
t.Logf("api session: %+v", sess)
|
||||
t.Logf("(2) session: %#v", sess)
|
||||
}
|
||||
|
||||
if err := c.Save(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Logf("filename: %s", fileName)
|
||||
assert.FileExists(t, fileName)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import "time"
|
|||
// Session represents backend api session data.
|
||||
type Session struct {
|
||||
MapKey string
|
||||
Customer string `json:",omitempty"`
|
||||
ExpiresAt string
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue