Merge branch 'develop' into CW-1823

This commit is contained in:
Muhsin Keloth
2023-05-31 12:54:36 +05:30
committed by GitHub
26 changed files with 302 additions and 72 deletions
@@ -1,11 +1,13 @@
class Api::V1::Accounts::Contacts::ContactInboxesController < Api::V1::Accounts::Contacts::BaseController
include HmacConcern
before_action :ensure_inbox, only: [:create]
def create
@contact_inbox = ContactInboxBuilder.new(
contact: @contact,
inbox: @inbox,
source_id: params[:source_id]
source_id: params[:source_id],
hmac_verified: hmac_verified?
).perform
end
@@ -1,6 +1,7 @@
class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseController
include Events::Types
include DateRangeHelper
include HmacConcern
before_action :conversation, except: [:index, :meta, :search, :create, :filter]
before_action :inbox, :contact, :contact_inbox, only: [:create]
@@ -104,9 +105,6 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
end
def set_conversation_status
# TODO: temporary fallback for the old bot status in conversation, we will remove after couple of releases
# commenting this out to see if there are any errors, if not we can remove this in subsequent releases
# status = params[:status] == 'bot' ? 'pending' : params[:status]
@conversation.status = params[:status]
@conversation.snoozed_until = parse_date_time(params[:snoozed_until].to_s) if params[:snoozed_until]
end
@@ -152,7 +150,8 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
ContactInboxBuilder.new(
contact: @contact,
inbox: @inbox,
source_id: params[:source_id]
source_id: params[:source_id],
hmac_verified: hmac_verified?
).perform
end
+5
View File
@@ -0,0 +1,5 @@
module HmacConcern
def hmac_verified?
ActiveModel::Type::Boolean.new.cast(params[:hmac_verified]).present?
end
end
@@ -50,6 +50,13 @@ class SuperAdmin::AccountsController < SuperAdmin::ApplicationController
# rubocop:enable Rails/I18nLocaleTexts
end
def reset_cache
requested_resource.reset_cache_keys
# rubocop:disable Rails/I18nLocaleTexts
redirect_back(fallback_location: [namespace, requested_resource], notice: 'Cache keys cleared')
# rubocop:enable Rails/I18nLocaleTexts
end
def destroy
account = Account.find(params[:id])
@@ -10,15 +10,22 @@
"TITLE": "Manage Audit Logs",
"DESC": "Audit Logs are trails for events and actions in a Chatwoot System.",
"TABLE_HEADER": [
"User",
"Action",
"IP Address",
"Time"
"Activity",
"Time",
"IP Address"
]
},
"API": {
"SUCCESS_MESSAGE": "AuditLogs retrieved successfully",
"ERROR_MESSAGE": "Could not connect to Woot Server, Please try again later"
},
"ACTION": {
"ADD": "created",
"EDIT": "updated",
"DELETE": "deleted",
"SIGN_IN": "signed in",
"SIGN_OUT": "signed out",
"SYSTEM": "System"
}
}
}
@@ -1,8 +1,8 @@
<template>
<div class="column content-box">
<div class="column content-box audit-log--settings">
<!-- List Audit Logs -->
<div class="row">
<div class="small-8 columns with-right-space ">
<div>
<div>
<p
v-if="!uiFlags.fetchingList && !records.length"
class="no-items-error-message"
@@ -16,8 +16,13 @@
<table
v-if="!uiFlags.fetchingList && records.length"
class="woot-table"
class="woot-table width-100"
>
<colgroup>
<col class="column-activity" />
<col />
<col />
</colgroup>
<thead>
<!-- Header -->
<th
@@ -29,12 +34,8 @@
</thead>
<tbody>
<tr v-for="auditLogItem in records" :key="auditLogItem.id">
<td class="wrap-break-words">{{ auditLogItem.username }}</td>
<td class="wrap-break-words">
{{ auditLogItem.auditable_type }}.{{ auditLogItem.action }}
</td>
<td class="remote-address">
{{ auditLogItem.remote_address }}
{{ generateLogText(auditLogItem) }}
</td>
<td class="wrap-break-words">
{{
@@ -44,6 +45,9 @@
)
}}
</td>
<td class="remote-address">
{{ auditLogItem.remote_address }}
</td>
</tr>
</tbody>
</table>
@@ -81,13 +85,48 @@ export default {
records: 'auditlogs/getAuditLogs',
uiFlags: 'auditlogs/getUIFlags',
meta: 'auditlogs/getMeta',
agentList: 'agents/getAgents',
}),
},
mounted() {
// Fetch API Call
this.$store.dispatch('auditlogs/fetch', { page: 1 });
this.$store.dispatch('agents/get');
},
methods: {
getAgentName(email) {
if (email === null) {
return this.$t('AUDIT_LOGS.ACTION.SYSTEM');
}
const agentName = this.agentList.find(agent => agent.email === email)
?.name;
// If agent does not exist(removed/deleted), return email from audit log
return agentName || email;
},
generateLogText(auditLogItem) {
const username = this.getAgentName(auditLogItem.username);
const auditableType = auditLogItem.auditable_type.toLowerCase();
const action = auditLogItem.action.toLowerCase();
const logActions = {
create: this.$t('AUDIT_LOGS.ACTION.ADD'),
destroy: this.$t('AUDIT_LOGS.ACTION.DELETE'),
update: this.$t('AUDIT_LOGS.ACTION.EDIT'),
sign_in: this.$t('AUDIT_LOGS.ACTION.SIGN_IN'),
sign_out: this.$t('AUDIT_LOGS.ACTION.SIGN_OUT'),
};
// detect if the action is custom user action, which involves
// only the user, such as signing in, signing out etc.
// if it is, then do not show the auditable type
const userActions = this.getUserActions(action);
return `${username} ${logActions[action] || action} ${
userActions ? '' : auditableType
}`;
},
getUserActions(action) {
return ['sign_in', 'sign_out'].includes(action);
},
onPageChange(page) {
window.history.pushState({}, null, `${this.$route.path}?page=${page}`);
try {
@@ -101,12 +140,24 @@ export default {
},
};
</script>
<style scoped>
.remote-address {
width: 14rem;
}
.wrap-break-words {
word-break: break-all;
white-space: normal;
<style lang="scss" scoped>
.audit-log--settings {
display: flex;
justify-content: space-between;
flex-direction: column;
.remote-address {
width: 14rem;
}
.wrap-break-words {
word-break: break-all;
white-space: normal;
}
.column-activity {
width: 60%;
}
}
</style>
@@ -42,11 +42,10 @@
</div>
<p
v-if="message.showAvatar || hasRecordedResponse"
v-dompurify-html="agentName"
class="agent-name"
:class="$dm('text-slate-700', 'dark:text-slate-200')"
>
{{ agentName }}
</p>
/>
</div>
</div>
@@ -3,10 +3,8 @@ class Channels::Whatsapp::TemplatesSyncSchedulerJob < ApplicationJob
def perform
Channel::Whatsapp.where('message_templates_last_updated <= ? OR message_templates_last_updated IS NULL',
15.minutes.ago).find_in_batches do |channels_batch|
channels_batch.each do |channel|
Channels::Whatsapp::TemplatesSyncJob.perform_later(channel)
end
3.hours.ago).limit(Limits::BULK_EXTERNAL_HTTP_CALLS_LIMIT).all.each do |channel|
Channels::Whatsapp::TemplatesSyncJob.perform_later(channel)
end
end
end
+21 -9
View File
@@ -4,29 +4,41 @@ module CacheKeys
include CacheKeysHelper
include Events::Types
included do
class_attribute :cacheable_models
self.cacheable_models = [Label, Inbox, Team]
end
def cache_keys
{
label: fetch_value_for_key(id, Label.name.underscore),
inbox: fetch_value_for_key(id, Inbox.name.underscore),
team: fetch_value_for_key(id, Team.name.underscore)
}
keys = {}
self.class.cacheable_models.each do |model|
keys[model.name.underscore.to_sym] = fetch_value_for_key(id, model.name.underscore)
end
keys
end
def invalidate_cache_key_for(key)
prefixed_cache_key = get_prefixed_cache_key(id, key)
Redis::Alfred.del(prefixed_cache_key)
dispatch_cache_udpate_event
Redis::Alfred.delete(prefixed_cache_key)
dispatch_cache_update_event
end
def update_cache_key(key)
prefixed_cache_key = get_prefixed_cache_key(id, key)
Redis::Alfred.set(prefixed_cache_key, Time.now.utc.to_i)
dispatch_cache_udpate_event
dispatch_cache_update_event
end
def reset_cache_keys
self.class.cacheable_models.each do |model|
invalidate_cache_key_for(model.name.underscore)
end
end
private
def dispatch_cache_udpate_event
def dispatch_cache_update_event
Rails.configuration.dispatcher.dispatch(ACCOUNT_CACHE_INVALIDATED, Time.zone.now, cache_keys: cache_keys, account: self)
end
end
@@ -23,7 +23,8 @@ class Whatsapp::Providers::Whatsapp360DialogService < Whatsapp::Providers::BaseS
def sync_templates
response = HTTParty.get("#{api_base_path}/configs/templates", headers: api_headers)
whatsapp_channel.update(message_templates: response['waba_templates'], message_templates_last_updated: Time.now.utc) if response.success?
whatsapp_channel[:message_templates] = response['waba_templates'] if response.success?
whatsapp_channel.update(message_templates_last_updated: Time.now.utc)
end
def validate_provider_config?
@@ -24,8 +24,8 @@ class Whatsapp::Providers::WhatsappCloudService < Whatsapp::Providers::BaseServi
def sync_templates
templates = fetch_whatsapp_templates("#{business_account_path}/message_templates?access_token=#{whatsapp_channel.provider_config['api_key']}")
whatsapp_channel.update(message_templates: templates, message_templates_last_updated: Time.now.utc) if templates.present?
whatsapp_channel[:message_templates] = templates if templates.present?
whatsapp_channel.update(message_templates_last_updated: Time.now.utc)
end
def fetch_whatsapp_templates(url)
@@ -35,7 +35,9 @@
<div class="flex flex-col items-start justify-between w-full md:flex-row md:items-center pt-2">
<div class="flex items-center space-x-2">
<% if @article.author&.avatar_url&.present? %>
<img src="<%= @article.author.avatar_url %>" alt="<%= @article.author.display_name %>" class="w-12 h-12 border rounded-full pr-1">
<div class="pr-1">
<img src="<%= @article.author.avatar_url %>" alt="<%= @article.author.display_name %>" class="w-12 h-12 border rounded-full">
</div>
<% end %>
<div>
<h5 class="text-base font-medium text-slate-900 mb-2"><%= @article.author.available_name %></h5>
@@ -0,0 +1,9 @@
<section class="main-content__body">
<hr/>
<%= form_for([:reset_cache, namespace, page.resource], method: :post, html: { class: "form" }) do |f| %>
<div class="form-actions">
<p>This will clear the IndexedDB cache keys from redis. <br>The next load will fetch the data from backend.</p>
<%= f.submit 'Reset Frontend Cache' %>
</div>
<% end %>
</section>
@@ -87,3 +87,5 @@ as well as a link to its edit page.
</section>
<%= render partial: "seed_data", locals: {page: page} %>
<%= render partial: "reset_cache", locals: {page: page} %>