Spiegel von
https://github.com/paviliondev/discourse-custom-wizard.git
synchronisiert 2024-11-25 02:30:28 +01:00
Update authentication and subscription handling
Dieser Commit ist enthalten in:
Ursprung
247f7ca466
Commit
a27c222dc6
24 geänderte Dateien mit 687 neuen und 8 gelöschten Zeilen
|
@ -6,6 +6,7 @@ import I18n from "I18n";
|
|||
const icons = {
|
||||
error: "times-circle",
|
||||
success: "check-circle",
|
||||
warn: "exclamation-circle",
|
||||
info: "info-circle",
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
import Component from "@ember/component";
|
||||
import CustomWizardPro from "../models/custom-wizard-pro";
|
||||
import { notEmpty } from "@ember/object/computed";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
|
||||
export default Component.extend({
|
||||
classNameBindings: [':custom-wizard-pro-subscription', 'subscription.active:active:inactive'],
|
||||
subscribed: notEmpty('subscription'),
|
||||
|
||||
@discourseComputed('subscription.type')
|
||||
title(type) {
|
||||
return type ?
|
||||
I18n.t(`admin.wizard.pro.subscription.title.${type}`) :
|
||||
I18n.t("admin.wizard.pro.not_subscribed");
|
||||
},
|
||||
|
||||
@discourseComputed('subscription.active')
|
||||
stateClass(active) {
|
||||
return active ? 'active' : 'inactive';
|
||||
},
|
||||
|
||||
@discourseComputed('stateClass')
|
||||
stateLabel(stateClass) {
|
||||
return I18n.t(`admin.wizard.pro.subscription.status.${stateClass}`);
|
||||
},
|
||||
|
||||
actions: {
|
||||
update() {
|
||||
this.set('updating', true);
|
||||
CustomWizardPro.update_subscription().then(result => {
|
||||
if (result.success) {
|
||||
this.setProperties({
|
||||
updateIcon: 'check',
|
||||
subscription: result.subscription
|
||||
});
|
||||
} else {
|
||||
this.set('updateIcon', 'times');
|
||||
}
|
||||
}).finally(() => {
|
||||
this.set('updating', false);
|
||||
setTimeout(() => {
|
||||
this.set('updateIcon', null);
|
||||
}, 7000);
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
56
assets/javascripts/discourse/controllers/admin-wizards-pro.js.es6
Normale Datei
56
assets/javascripts/discourse/controllers/admin-wizards-pro.js.es6
Normale Datei
|
@ -0,0 +1,56 @@
|
|||
import Controller from "@ember/controller";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import CustomWizardPro from "../models/custom-wizard-pro";
|
||||
import { alias } from "@ember/object/computed";
|
||||
|
||||
export default Controller.extend({
|
||||
messageUrl: "https://thepavilion.io/t/3652",
|
||||
messageType: 'info',
|
||||
messageKey: null,
|
||||
showSubscription: alias('model.authentication.active'),
|
||||
|
||||
setup() {
|
||||
const authentication = this.get('model.authentication');
|
||||
const subscription = this.get('model.subscription');
|
||||
const subscribed = subscription && subscription.active;
|
||||
const authenticated = authentication && authentication.active;
|
||||
|
||||
if (!subscribed) {
|
||||
this.set('messageKey', authenticated ? 'not_subscribed' : 'authorize');
|
||||
} else {
|
||||
this.set('messageKey', !authenticated ?
|
||||
'subscription_expiring' :
|
||||
subscribed ? 'subscription_active' : 'subscription_inactive'
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
@discourseComputed('model.server')
|
||||
messageOpts(server) {
|
||||
return { server };
|
||||
},
|
||||
|
||||
actions: {
|
||||
unauthorize() {
|
||||
this.set('unauthorizing', true);
|
||||
|
||||
CustomWizardPro.unauthorize().then(result => {
|
||||
if (result.success) {
|
||||
this.setProperties({
|
||||
messageKey: 'unauthorized',
|
||||
messageType: 'warn',
|
||||
"model.authentication": null,
|
||||
"model.subscription": null
|
||||
});
|
||||
} else {
|
||||
this.setProperties({
|
||||
messageKey: 'unauthorize_failed',
|
||||
messageType: 'error'
|
||||
});
|
||||
}
|
||||
}).finally(() => {
|
||||
this.set('unauthorizing', false);
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
|
@ -43,12 +43,20 @@ export default {
|
|||
}
|
||||
);
|
||||
|
||||
this.route("adminWizardsLogs", { path: "/logs", resetNamespace: true });
|
||||
this.route("adminWizardsLogs", {
|
||||
path: "/logs",
|
||||
resetNamespace: true
|
||||
});
|
||||
|
||||
this.route("adminWizardsManager", {
|
||||
path: "/manager",
|
||||
resetNamespace: true,
|
||||
});
|
||||
|
||||
this.route("adminWizardsPro", {
|
||||
path: "/pro",
|
||||
resetNamespace: true,
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
|
|
34
assets/javascripts/discourse/models/custom-wizard-pro.js.es6
Normale Datei
34
assets/javascripts/discourse/models/custom-wizard-pro.js.es6
Normale Datei
|
@ -0,0 +1,34 @@
|
|||
import { ajax } from "discourse/lib/ajax";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import EmberObject from "@ember/object";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
|
||||
const CustomWizardPro = EmberObject.extend();
|
||||
|
||||
const basePath = "/admin/wizards/pro";
|
||||
|
||||
CustomWizardPro.reopenClass({
|
||||
status() {
|
||||
return ajax(basePath, {
|
||||
type: "GET",
|
||||
}).catch(popupAjaxError);
|
||||
},
|
||||
|
||||
authorize() {
|
||||
window.location.href = `${basePath}/authorize`;
|
||||
},
|
||||
|
||||
unauthorize() {
|
||||
return ajax(`${basePath}/authorize`, {
|
||||
type: "DELETE",
|
||||
}).catch(popupAjaxError);
|
||||
},
|
||||
|
||||
update_subscription() {
|
||||
return ajax(`${basePath}/subscription`, {
|
||||
type: "POST",
|
||||
}).catch(popupAjaxError);
|
||||
}
|
||||
});
|
||||
|
||||
export default CustomWizardPro;
|
|
@ -221,7 +221,7 @@ CustomWizard.reopenClass({
|
|||
const wizard = this._super.apply(this);
|
||||
wizard.setProperties(buildProperties(wizardJson));
|
||||
return wizard;
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
export default CustomWizard;
|
||||
|
|
20
assets/javascripts/discourse/routes/admin-wizards-pro.js.es6
Normale Datei
20
assets/javascripts/discourse/routes/admin-wizards-pro.js.es6
Normale Datei
|
@ -0,0 +1,20 @@
|
|||
import CustomWizardPro from "../models/custom-wizard-pro";
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
|
||||
export default DiscourseRoute.extend({
|
||||
model() {
|
||||
return CustomWizardPro.status();
|
||||
},
|
||||
|
||||
setupController(controller, model) {
|
||||
console.log(model)
|
||||
controller.set('model', model);
|
||||
controller.setup();
|
||||
},
|
||||
|
||||
actions: {
|
||||
authorize() {
|
||||
CustomWizardPro.authorize();
|
||||
}
|
||||
}
|
||||
});
|
31
assets/javascripts/discourse/templates/admin-wizards-pro.hbs
Normale Datei
31
assets/javascripts/discourse/templates/admin-wizards-pro.hbs
Normale Datei
|
@ -0,0 +1,31 @@
|
|||
<div class="admin-wizard-controls">
|
||||
<h3>{{i18n "admin.wizard.pro.title"}}</h3>
|
||||
|
||||
<div class="buttons">
|
||||
{{#if model.authentication.active}}
|
||||
{{conditional-loading-spinner size="small" condition=unauthorizing}}
|
||||
<a {{action "unauthorize"}} title={{i18n "admin.wizard.pro.unauthorize"}}>
|
||||
{{i18n "admin.wizard.pro.unauthorize"}}
|
||||
</a>
|
||||
<label>{{i18n "admin.wizard.pro.authorized"}}</label>
|
||||
{{else}}
|
||||
{{d-button
|
||||
icon="id-card"
|
||||
label="admin.wizard.pro.authorize"
|
||||
action=(route-action "authorize")}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{wizard-message
|
||||
key=messageKey
|
||||
url=messageUrl
|
||||
type=messageType
|
||||
opts=messageOpts
|
||||
component="pro"}}
|
||||
|
||||
<div class="admin-wizard-container">
|
||||
{{#if showSubscription}}
|
||||
{{wizard-pro-subscription subscription=model.subscription}}
|
||||
{{/if}}
|
||||
</div>
|
|
@ -7,6 +7,7 @@
|
|||
{{/if}}
|
||||
{{nav-item route="adminWizardsLogs" label="admin.wizard.log.nav_label"}}
|
||||
{{nav-item route="adminWizardsManager" label="admin.wizard.manager.nav_label"}}
|
||||
{{nav-item route="adminWizardsPro" label="admin.wizard.pro.nav_label"}}
|
||||
{{/admin-nav}}
|
||||
|
||||
<div class="admin-container">
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
<div class="title-container">
|
||||
<h3 class="subscription-title">{{title}}</h3>
|
||||
|
||||
<div class="buttons">
|
||||
<span>
|
||||
{{#if updating}}
|
||||
{{loading-spinner size="small"}}
|
||||
{{else if updateIcon}}
|
||||
{{d-icon updateIcon}}
|
||||
{{/if}}
|
||||
</span>
|
||||
{{d-button
|
||||
icon="sync"
|
||||
action=(action "update")
|
||||
disabled=updating
|
||||
label="admin.wizard.pro.subscription.update"}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if subscribed}}
|
||||
<div class="detail-container">
|
||||
<div class="subscription-state {{stateClass}}">{{stateLabel}}</div>
|
||||
<div class="subscription-updated-at">
|
||||
{{{i18n
|
||||
'admin.wizard.pro.subscription.last_updated'
|
||||
updated_at=(format-date subscription.updated_at leaveAgo="true")
|
||||
}}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
|
@ -7,7 +7,7 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
& + .wizard-message + div {
|
||||
margin-top: 20px;
|
||||
|
@ -715,3 +715,61 @@
|
|||
width: 80px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.admin-wizards-pro {
|
||||
.admin-wizard-controls {
|
||||
h3, label {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
padding: .4em .5em;
|
||||
margin-left: .75em;
|
||||
background-color: $success;
|
||||
color: $secondary;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.loading-container {
|
||||
margin-right: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.custom-wizard-pro-subscription {
|
||||
.title-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: .5em;
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.buttons > span {
|
||||
margin-right: .5em;
|
||||
}
|
||||
}
|
||||
|
||||
.detail-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 1em;
|
||||
background-color: $primary-very-low;
|
||||
|
||||
.subscription-state {
|
||||
padding: .25em .5em;
|
||||
margin-right: .75em;
|
||||
|
||||
&.active {
|
||||
background-color: $success;
|
||||
color: $secondary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,11 +86,19 @@ en:
|
|||
no_file: Please choose a file to import
|
||||
file_size_error: The file size must be 512kb or less
|
||||
file_format_error: The file must be a .json file
|
||||
server_error: "Error: {{message}}"
|
||||
importing: Importing wizards...
|
||||
destroying: Destroying wizards...
|
||||
import_complete: Import complete
|
||||
destroy_complete: Destruction complete
|
||||
pro:
|
||||
documentation: Check out the PRO documentation
|
||||
authorize: "Authorize this forum to use your PRO subscription plan on %{server}."
|
||||
not_subscribed: "You've authorized, but are not currently subscribed to a PRO plan on %{server}."
|
||||
subscription_expiring: "Your subscription is active, but will expire in the next 48 hours."
|
||||
subscription_active: "Your subscription is active."
|
||||
subscription_inactive: "Your subscription is inactive on this forum. Read more in <a href='https://thepavilion.io/t/3652'>the documentation</a>."
|
||||
unauthorized: "You're unauthorized. If you have a subscription, it will become inactive in the next 48 hours."
|
||||
unauthorize_failed: Failed to unauthorize.
|
||||
|
||||
editor:
|
||||
show: "Show"
|
||||
|
@ -426,6 +434,23 @@ en:
|
|||
destroy: Destroy
|
||||
destroyed: destroyed
|
||||
|
||||
pro:
|
||||
nav_label: PRO
|
||||
title: Custom Wizard PRO
|
||||
authorize: Authorize
|
||||
authorized: Authorized
|
||||
unauthorize: cancel
|
||||
not_subscribed: You're not currently subscribed
|
||||
subscription:
|
||||
title:
|
||||
community: Community Subscription
|
||||
business: Business Subscription
|
||||
status:
|
||||
active: Active
|
||||
inactive: Inactive
|
||||
update: Update
|
||||
last_updated: Last updated {{updated_at}}
|
||||
|
||||
wizard_js:
|
||||
group:
|
||||
select: "Select a group"
|
||||
|
|
|
@ -43,5 +43,11 @@ Discourse::Application.routes.append do
|
|||
get 'admin/wizards/manager/export' => 'admin_manager#export'
|
||||
post 'admin/wizards/manager/import' => 'admin_manager#import'
|
||||
delete 'admin/wizards/manager/destroy' => 'admin_manager#destroy'
|
||||
|
||||
get 'admin/wizards/pro' => 'admin_pro#index'
|
||||
get 'admin/wizards/pro/authorize' => 'admin_pro#authorize'
|
||||
get 'admin/wizards/pro/authorize/callback' => 'admin_pro#authorize_callback'
|
||||
delete 'admin/wizards/pro/authorize' => 'admin_pro#destroy'
|
||||
post 'admin/wizards/pro/subscription' => 'admin_pro#update_subscription'
|
||||
end
|
||||
end
|
||||
|
|
46
controllers/custom_wizard/admin/pro.rb
Normale Datei
46
controllers/custom_wizard/admin/pro.rb
Normale Datei
|
@ -0,0 +1,46 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CustomWizard::AdminProController < CustomWizard::AdminController
|
||||
skip_before_action :check_xhr, :preload_json, :verify_authenticity_token, only: [:authorize, :authorize_callback]
|
||||
|
||||
def index
|
||||
render_serialized(CustomWizard::Pro.new, CustomWizard::ProSerializer, root: false)
|
||||
end
|
||||
|
||||
def authorize
|
||||
request_id = SecureRandom.hex(32)
|
||||
cookies[:user_api_request_id] = request_id
|
||||
redirect_to CustomWizard::ProAuthentication.generate_request(current_user.id, request_id).to_s
|
||||
end
|
||||
|
||||
def authorize_callback
|
||||
payload = params[:payload]
|
||||
request_id = cookies[:user_api_request_id]
|
||||
|
||||
CustomWizard::ProAuthentication.handle_response(request_id, payload)
|
||||
CustomWizard::ProSubscription.update
|
||||
|
||||
redirect_to '/admin/wizards/pro'
|
||||
end
|
||||
|
||||
def destroy
|
||||
if CustomWizard::ProAuthentication.destroy
|
||||
render json: success_json
|
||||
else
|
||||
render json: failed_json
|
||||
end
|
||||
end
|
||||
|
||||
def update_subscription
|
||||
if CustomWizard::ProSubscription.update
|
||||
render json: success_json.merge(
|
||||
subscription: CustomWizard::ProSubscriptionSerializer.new(
|
||||
CustomWizard::ProSubscription.new,
|
||||
root: false
|
||||
)
|
||||
)
|
||||
else
|
||||
render json: failed_json
|
||||
end
|
||||
end
|
||||
end
|
11
jobs/scheduled/update_pro_status.rb
Normale Datei
11
jobs/scheduled/update_pro_status.rb
Normale Datei
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Jobs
|
||||
class UpdateProSubscription < ::Jobs::Scheduled
|
||||
every 1.days
|
||||
|
||||
def execute(args)
|
||||
CustomWizard::ProSubscription.update
|
||||
end
|
||||
end
|
||||
end
|
21
lib/custom_wizard/pro.rb
Normale Datei
21
lib/custom_wizard/pro.rb
Normale Datei
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CustomWizard::Pro
|
||||
NAMESPACE ||= "#{CustomWizard::PLUGIN_NAME}_pro"
|
||||
|
||||
attr_reader :authentication,
|
||||
:subscription
|
||||
|
||||
def initialize
|
||||
@authentication = CustomWizard::ProAuthentication.new
|
||||
@subscription = CustomWizard::ProSubscription.new
|
||||
end
|
||||
|
||||
def authorized?
|
||||
@authentication.active?
|
||||
end
|
||||
|
||||
def subscribed?
|
||||
@subscription.active?
|
||||
end
|
||||
end
|
155
lib/custom_wizard/pro/authentication.rb
Normale Datei
155
lib/custom_wizard/pro/authentication.rb
Normale Datei
|
@ -0,0 +1,155 @@
|
|||
class CustomWizard::ProAuthentication
|
||||
include ActiveModel::Serialization
|
||||
|
||||
API_KEY ||= "api_key"
|
||||
API_CLIENT_ID ||= 'api_client_id'
|
||||
KEYS ||= "keys"
|
||||
|
||||
attr_reader :client_id,
|
||||
:auth_by,
|
||||
:auth_at,
|
||||
:api_key
|
||||
|
||||
def initialize
|
||||
api = get_api_key
|
||||
|
||||
@api_key = api.key
|
||||
@auth_at = api.auth_at
|
||||
@auth_by = api.auth_by
|
||||
@client_id = get_client_id || set_client_id
|
||||
end
|
||||
|
||||
def active?
|
||||
@api_key.present?
|
||||
end
|
||||
|
||||
def update(data)
|
||||
api_key = data[:key]
|
||||
user_id = data[:user_id]
|
||||
user = User.find(user_id)
|
||||
|
||||
if user&.admin
|
||||
set_api_key(api_key, user.id)
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
remove
|
||||
end
|
||||
|
||||
def self.destroy
|
||||
self.new.destroy
|
||||
end
|
||||
|
||||
def generate_keys(user_id, request_id)
|
||||
rsa = OpenSSL::PKey::RSA.generate(2048)
|
||||
nonce = SecureRandom.hex(32)
|
||||
set_keys(request_id, user_id, rsa, nonce)
|
||||
|
||||
OpenStruct.new(nonce: nonce, public_key: rsa.public_key)
|
||||
end
|
||||
|
||||
def decrypt_payload(request_id, payload)
|
||||
keys = get_keys(request_id)
|
||||
return false unless keys.present? && keys.pem
|
||||
delete_keys(request_id)
|
||||
|
||||
rsa = OpenSSL::PKey::RSA.new(keys.pem)
|
||||
decrypted_payload = rsa.private_decrypt(Base64.decode64(payload))
|
||||
return false unless decrypted_payload.present?
|
||||
|
||||
begin
|
||||
data = JSON.parse(decrypted_payload).symbolize_keys
|
||||
rescue JSON::ParserError
|
||||
return false
|
||||
end
|
||||
|
||||
return false unless data[:nonce] == keys.nonce
|
||||
data[:user_id] = keys.user_id
|
||||
|
||||
data
|
||||
end
|
||||
|
||||
def self.generate_request(user_id, request_id)
|
||||
authentication = self.new
|
||||
keys = authentication.generate_keys(user_id, request_id)
|
||||
|
||||
params = {
|
||||
public_key: keys.public_key,
|
||||
nonce: keys.nonce,
|
||||
client_id: authentication.client_id,
|
||||
auth_redirect: "#{Discourse.base_url}/admin/wizards/pro/authorize/callback",
|
||||
application_name: SiteSetting.title,
|
||||
scopes: CustomWizard::ProSubscription::SCOPE
|
||||
}
|
||||
|
||||
uri = URI.parse("https://#{CustomWizard::ProSubscription::SUBSCRIPTION_SERVER}/user-api-key/new")
|
||||
uri.query = URI.encode_www_form(params)
|
||||
uri.to_s
|
||||
end
|
||||
|
||||
def self.handle_response(request_id, payload)
|
||||
authentication = self.new
|
||||
|
||||
data = authentication.decrypt_payload(request_id, payload)
|
||||
return unless data.is_a?(Hash) && data[:key] && data[:user_id]
|
||||
|
||||
authentication.update(data)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def get_api_key
|
||||
raw = PluginStore.get(CustomWizard::Pro::NAMESPACE, API_KEY)
|
||||
OpenStruct.new(
|
||||
key: raw && raw['key'],
|
||||
auth_by: raw && raw['auth_by'],
|
||||
auth_at: raw && raw['auth_at']
|
||||
)
|
||||
end
|
||||
|
||||
def set_api_key(key, user_id)
|
||||
PluginStore.set(CustomWizard::Pro::NAMESPACE, API_KEY,
|
||||
key: key,
|
||||
auth_by: user_id,
|
||||
auth_at: Time.now
|
||||
)
|
||||
end
|
||||
|
||||
def remove
|
||||
PluginStore.remove(CustomWizard::Pro::NAMESPACE, API_KEY)
|
||||
end
|
||||
|
||||
def get_client_id
|
||||
PluginStore.get(CustomWizard::Pro::NAMESPACE, API_CLIENT_ID)
|
||||
end
|
||||
|
||||
def set_client_id
|
||||
client_id = SecureRandom.hex(32)
|
||||
PluginStore.set(CustomWizard::Pro::NAMESPACE, API_CLIENT_ID, client_id)
|
||||
client_id
|
||||
end
|
||||
|
||||
def set_keys(request_id, user_id, rsa, nonce)
|
||||
PluginStore.set(CustomWizard::Pro::NAMESPACE, "#{KEYS}_#{request_id}",
|
||||
user_id: user_id,
|
||||
pem: rsa.export,
|
||||
nonce: nonce
|
||||
)
|
||||
end
|
||||
|
||||
def get_keys(request_id)
|
||||
raw = PluginStore.get(CustomWizard::Pro::NAMESPACE, "#{KEYS}_#{request_id}")
|
||||
OpenStruct.new(
|
||||
user_id: raw && raw['user_id'],
|
||||
pem: raw && raw['pem'],
|
||||
nonce: raw && raw['nonce']
|
||||
)
|
||||
end
|
||||
|
||||
def delete_keys(request_id)
|
||||
PluginStore.remove(CustomWizard::Pro::NAMESPACE, "#{KEYS}_#{request_id}")
|
||||
end
|
||||
end
|
74
lib/custom_wizard/pro/subscription.rb
Normale Datei
74
lib/custom_wizard/pro/subscription.rb
Normale Datei
|
@ -0,0 +1,74 @@
|
|||
class CustomWizard::ProSubscription
|
||||
include ActiveModel::Serialization
|
||||
|
||||
SUBSCRIPTION_SERVER ||= "test.thepavilion.io"
|
||||
SUBSCRIPTION_TYPE ||= "stripe"
|
||||
SCOPE ||= "discourse-subscription-server:user_subscription"
|
||||
CLIENT_NAME ||= "custom-wizard"
|
||||
SUBSCRIPTION_KEY ||= "custom_wizard_pro_subscription"
|
||||
UPDATE_DAY_BUFFER ||= 2
|
||||
TYPES ||= %w(community business)
|
||||
|
||||
attr_reader :type,
|
||||
:updated_at
|
||||
|
||||
def initialize
|
||||
raw = get
|
||||
|
||||
if raw
|
||||
@type = raw['type']
|
||||
@updated_at = raw['updated_at']
|
||||
end
|
||||
end
|
||||
|
||||
def active?
|
||||
TYPES.include?(type) && updated_at.to_datetime > (Date.today - UPDATE_DAY_BUFFER.days).to_datetime
|
||||
end
|
||||
|
||||
def update(data)
|
||||
return false unless data && data.is_a?(Hash)
|
||||
subscriptions = data[:subscriptions]
|
||||
|
||||
if subscriptions.present?
|
||||
subscription = subscriptions.first
|
||||
type = subscription[:price_nickname]
|
||||
|
||||
set(type)
|
||||
end
|
||||
end
|
||||
|
||||
def self.update
|
||||
@subscribed = nil
|
||||
auth = CustomWizard::ProAuthentication.new
|
||||
subscription = self.new
|
||||
|
||||
if auth.active?
|
||||
response = Excon.get(
|
||||
"https://#{SUBSCRIPTION_SERVER}/subscription-server/user-subscriptions/#{SUBSCRIPTION_TYPE}/#{CLIENT_NAME}",
|
||||
headers: { "User-Api-Key" => auth.api_key }
|
||||
)
|
||||
|
||||
if response.status == 200
|
||||
begin
|
||||
data = JSON.parse(response.body).deep_symbolize_keys
|
||||
rescue JSON::ParserError
|
||||
return false
|
||||
end
|
||||
|
||||
return subscription.update(data)
|
||||
end
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set(type)
|
||||
PluginStore.set(CustomWizard::Pro::NAMESPACE, SUBSCRIPTION_KEY, type: type, updated_at: Time.now)
|
||||
end
|
||||
|
||||
def get
|
||||
PluginStore.get(CustomWizard::Pro::NAMESPACE, SUBSCRIPTION_KEY)
|
||||
end
|
||||
end
|
12
plugin.rb
12
plugin.rb
|
@ -62,11 +62,13 @@ after_initialize do
|
|||
../controllers/custom_wizard/admin/logs.rb
|
||||
../controllers/custom_wizard/admin/manager.rb
|
||||
../controllers/custom_wizard/admin/custom_fields.rb
|
||||
../controllers/custom_wizard/admin/pro.rb
|
||||
../controllers/custom_wizard/wizard.rb
|
||||
../controllers/custom_wizard/steps.rb
|
||||
../controllers/custom_wizard/realtime_validations.rb
|
||||
../jobs/refresh_api_access_token.rb
|
||||
../jobs/set_after_time_wizard.rb
|
||||
../jobs/regular/refresh_api_access_token.rb
|
||||
../jobs/regular/set_after_time_wizard.rb
|
||||
../jobs/scheduled/update_pro_status.rb
|
||||
../lib/custom_wizard/validators/template.rb
|
||||
../lib/custom_wizard/validators/update.rb
|
||||
../lib/custom_wizard/action_result.rb
|
||||
|
@ -85,6 +87,9 @@ after_initialize do
|
|||
../lib/custom_wizard/submission.rb
|
||||
../lib/custom_wizard/template.rb
|
||||
../lib/custom_wizard/wizard.rb
|
||||
../lib/custom_wizard/pro.rb
|
||||
../lib/custom_wizard/pro/subscription.rb
|
||||
../lib/custom_wizard/pro/authentication.rb
|
||||
../lib/custom_wizard/api/api.rb
|
||||
../lib/custom_wizard/api/authorization.rb
|
||||
../lib/custom_wizard/api/endpoint.rb
|
||||
|
@ -105,6 +110,9 @@ after_initialize do
|
|||
../serializers/custom_wizard/log_serializer.rb
|
||||
../serializers/custom_wizard/submission_serializer.rb
|
||||
../serializers/custom_wizard/realtime_validation/similar_topics_serializer.rb
|
||||
../serializers/custom_wizard/pro_serializer.rb
|
||||
../serializers/custom_wizard/pro/authentication_serializer.rb
|
||||
../serializers/custom_wizard/pro/subscription_serializer.rb
|
||||
../extensions/extra_locales_controller.rb
|
||||
../extensions/invites_controller.rb
|
||||
../extensions/users_controller.rb
|
||||
|
|
11
serializers/custom_wizard/pro/authentication_serializer.rb
Normale Datei
11
serializers/custom_wizard/pro/authentication_serializer.rb
Normale Datei
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
class CustomWizard::ProAuthenticationSerializer < ApplicationSerializer
|
||||
attributes :active,
|
||||
:client_id,
|
||||
:auth_by,
|
||||
:auth_at
|
||||
|
||||
def active
|
||||
object.active?
|
||||
end
|
||||
end
|
10
serializers/custom_wizard/pro/subscription_serializer.rb
Normale Datei
10
serializers/custom_wizard/pro/subscription_serializer.rb
Normale Datei
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
class CustomWizard::ProSubscriptionSerializer < ApplicationSerializer
|
||||
attributes :type,
|
||||
:active,
|
||||
:updated_at
|
||||
|
||||
def active
|
||||
object.active?
|
||||
end
|
||||
end
|
26
serializers/custom_wizard/pro_serializer.rb
Normale Datei
26
serializers/custom_wizard/pro_serializer.rb
Normale Datei
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
class CustomWizard::ProSerializer < ApplicationSerializer
|
||||
attributes :server,
|
||||
:authentication,
|
||||
:subscription
|
||||
|
||||
def server
|
||||
CustomWizard::ProSubscription::SUBSCRIPTION_SERVER
|
||||
end
|
||||
|
||||
def authentication
|
||||
if object.authentication
|
||||
CustomWizard::ProAuthenticationSerializer.new(object.authentication, root: false)
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def subscription
|
||||
if object.subscription
|
||||
CustomWizard::ProSubscriptionSerializer.new(object.subscription, root: false)
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
Laden …
In neuem Issue referenzieren