Merge branch 'develop' into fix/url-sanitizer

This commit is contained in:
Shivam Mishra
2025-08-21 17:20:15 +05:30
committed by GitHub
4 changed files with 229 additions and 16 deletions
+1 -1
View File
@@ -1 +1 @@
3.4.2
3.4.3
+58
View File
@@ -3,6 +3,7 @@ module Liquidable
included do
before_create :process_liquid_in_content
before_create :process_liquid_in_template_params
end
private
@@ -35,4 +36,61 @@ module Liquidable
# We don't want to process liquid in code blocks
content.gsub(/`(.*?)`/m, '{% raw %}`\\1`{% endraw %}')
end
def process_liquid_in_template_params
return unless template_params_present? && liquid_processable_template_params?
processed_params = process_liquid_in_hash(template_params_data['processed_params'])
# Update the additional_attributes with processed template_params
self.additional_attributes = additional_attributes.merge(
'template_params' => template_params_data.merge('processed_params' => processed_params)
)
rescue Liquid::Error
# If there is an error in the liquid syntax, we don't want to process it
end
def template_params_present?
additional_attributes&.dig('template_params', 'processed_params').present?
end
def liquid_processable_template_params?
message_type == 'outgoing' || message_type == 'template'
end
def template_params_data
additional_attributes['template_params']
end
def process_liquid_in_hash(hash)
return hash unless hash.is_a?(Hash)
hash.transform_values { |value| process_liquid_value(value) }
end
def process_liquid_value(value)
case value
when String
process_liquid_string(value)
when Hash
process_liquid_in_hash(value)
when Array
process_liquid_array(value)
else
value
end
end
def process_liquid_array(array)
array.map { |item| process_liquid_value(item) }
end
def process_liquid_string(string)
return string if string.blank?
template = Liquid::Template.parse(string)
template.render(message_drops)
rescue Liquid::Error
string
end
end
+15 -15
View File
@@ -2,7 +2,7 @@
# Description: Install and manage a Chatwoot installation.
# OS: Ubuntu 20.04 LTS, 22.04 LTS, 24.04 LTS
# Script Version: 3.4.2
# Script Version: 3.4.3
# Run this script as root
set -eu -o errexit -o pipefail -o noclobber -o nounset
@@ -19,7 +19,7 @@ fi
# option --output/-o requires 1 argument
LONGOPTS=console,debug,help,install,Install:,logs:,restart,ssl,upgrade,Upgrade:,webserver,version,web-only,worker-only,convert:
OPTIONS=cdhiI:l:rsuU:wvWK
CWCTL_VERSION="3.3.0"
CWCTL_VERSION="3.4.3"
pg_pass=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 15 ; echo '')
CHATWOOT_HUB_URL="https://hub.2.chatwoot.com/events"
@@ -430,7 +430,7 @@ function configure_systemd_services() {
if [ "$DEPLOYMENT_TYPE" == "web" ]; then
echo "Setting up web-only deployment"
# Stop and disable existing services if converting
if [ "$existing_full_deployment" = true ]; then
echo "Converting from full deployment to web-only"
@@ -449,14 +449,14 @@ function configure_systemd_services() {
cp /home/chatwoot/chatwoot/deployment/chatwoot-web.1.service /etc/systemd/system/chatwoot-web.1.service
cp /home/chatwoot/chatwoot/deployment/chatwoot-web.target /etc/systemd/system/chatwoot-web.target
systemctl daemon-reload
systemctl enable chatwoot-web.target
systemctl start chatwoot-web.target
elif [ "$DEPLOYMENT_TYPE" == "worker" ]; then
echo "Setting up worker-only deployment"
# Stop and disable existing services if converting
if [ "$existing_full_deployment" = true ]; then
echo "Converting from full deployment to worker-only"
@@ -475,14 +475,14 @@ function configure_systemd_services() {
cp /home/chatwoot/chatwoot/deployment/chatwoot-worker.1.service /etc/systemd/system/chatwoot-worker.1.service
cp /home/chatwoot/chatwoot/deployment/chatwoot-worker.target /etc/systemd/system/chatwoot-worker.target
systemctl daemon-reload
systemctl enable chatwoot-worker.target
systemctl start chatwoot-worker.target
else
echo "Setting up full deployment (web + worker)"
# Stop existing specialized deployments if converting back to full
if [ -f "/etc/systemd/system/chatwoot-web.target" ]; then
echo "Converting from web-only to full deployment"
@@ -494,7 +494,7 @@ function configure_systemd_services() {
systemctl stop chatwoot-worker.target || true
systemctl disable chatwoot-worker.target || true
fi
cp /home/chatwoot/chatwoot/deployment/chatwoot-web.1.service /etc/systemd/system/chatwoot-web.1.service
cp /home/chatwoot/chatwoot/deployment/chatwoot-worker.1.service /etc/systemd/system/chatwoot-worker.1.service
cp /home/chatwoot/chatwoot/deployment/chatwoot.target /etc/systemd/system/chatwoot.target
@@ -538,7 +538,7 @@ function setup_ssl() {
cd chatwoot
sed -i "s/http:\/\/0.0.0.0:3000/https:\/\/$domain_name/g" .env
EOF
# Restart the appropriate chatwoot target
if [ -f "/etc/systemd/system/chatwoot-web.target" ]; then
systemctl restart chatwoot-web.target
@@ -1005,7 +1005,7 @@ EOF
upgrade_redis
upgrade_node
get_pnpm
sudo -i -u chatwoot << EOF
# Navigate to the Chatwoot directory
@@ -1098,16 +1098,16 @@ function restart() {
##############################################################################
function convert_deployment() {
echo "Converting Chatwoot deployment to: $DEPLOYMENT_TYPE"
# Check if Chatwoot is installed
if [ ! -d "/home/chatwoot/chatwoot" ]; then
echo "Chatwoot installation not found. Use --install first."
exit 1
fi
# Run the systemd service configuration which handles conversion logic
configure_systemd_services
echo "Deployment converted successfully to: $DEPLOYMENT_TYPE"
}
+155
View File
@@ -69,4 +69,159 @@ shared_examples_for 'liqudable' do
end
end
end
context 'when liquid is present in template_params' do
let(:contact) do
create(:contact, name: 'john', email: 'john@example.com', phone_number: '+912883', custom_attributes: { customer_type: 'platinum' })
end
let(:conversation) { create(:conversation, id: 1, contact: contact, custom_attributes: { priority: 'high' }) }
context 'when message is outgoing with template_params' do
let(:message) { build(:message, conversation: conversation, message_type: 'outgoing') }
it 'replaces liquid variables in template_params body' do
message.additional_attributes = {
'template_params' => {
'name' => 'greet',
'category' => 'MARKETING',
'language' => 'en',
'processed_params' => {
'body' => {
'customer_name' => '{{contact.name}}',
'customer_email' => '{{contact.email}}'
}
}
}
}
message.save!
body_params = message.additional_attributes['template_params']['processed_params']['body']
expect(body_params['customer_name']).to eq 'John'
expect(body_params['customer_email']).to eq 'john@example.com'
end
it 'replaces liquid variables in nested template_params' do
message.additional_attributes = {
'template_params' => {
'name' => 'test_template',
'processed_params' => {
'header' => {
'media_url' => 'https://example.com/{{contact.name}}.jpg'
},
'body' => {
'customer_name' => '{{contact.name}}',
'priority' => '{{conversation.custom_attribute.priority}}'
},
'footer' => {
'company' => '{{account.name}}'
}
}
}
}
message.save!
processed = message.additional_attributes['template_params']['processed_params']
expect(processed['header']['media_url']).to eq 'https://example.com/John.jpg'
expect(processed['body']['customer_name']).to eq 'John'
expect(processed['body']['priority']).to eq 'high'
expect(processed['footer']['company']).to eq conversation.account.name
end
it 'handles arrays in template_params' do
message.additional_attributes = {
'template_params' => {
'name' => 'test_template',
'processed_params' => {
'buttons' => [
{ 'type' => 'url', 'parameter' => 'https://example.com/{{contact.name}}' },
{ 'type' => 'text', 'parameter' => 'Hello {{contact.name}}' }
]
}
}
}
message.save!
buttons = message.additional_attributes['template_params']['processed_params']['buttons']
expect(buttons[0]['parameter']).to eq 'https://example.com/John'
expect(buttons[1]['parameter']).to eq 'Hello John'
end
it 'handles custom attributes in template_params' do
message.additional_attributes = {
'template_params' => {
'name' => 'test_template',
'processed_params' => {
'body' => {
'customer_type' => '{{contact.custom_attribute.customer_type}}',
'priority' => '{{conversation.custom_attribute.priority}}'
}
}
}
}
message.save!
body_params = message.additional_attributes['template_params']['processed_params']['body']
expect(body_params['customer_type']).to eq 'platinum'
expect(body_params['priority']).to eq 'high'
end
it 'handles missing email with default filter in template_params' do
contact.update!(email: nil)
message.additional_attributes = {
'template_params' => {
'name' => 'test_template',
'processed_params' => {
'body' => {
'customer_email' => '{{ contact.email | default: "no-email@example.com" }}'
}
}
}
}
message.save!
body_params = message.additional_attributes['template_params']['processed_params']['body']
expect(body_params['customer_email']).to eq 'no-email@example.com'
end
it 'handles broken liquid syntax in template_params gracefully' do
message.additional_attributes = {
'template_params' => {
'name' => 'test_template',
'processed_params' => {
'body' => {
'broken_liquid' => '{{contact.name} {{invalid}}'
}
}
}
}
message.save!
body_params = message.additional_attributes['template_params']['processed_params']['body']
expect(body_params['broken_liquid']).to eq '{{contact.name} {{invalid}}'
end
it 'does not process template_params when message is incoming' do
incoming_message = build(:message, conversation: conversation, message_type: 'incoming')
incoming_message.additional_attributes = {
'template_params' => {
'name' => 'test_template',
'processed_params' => {
'body' => {
'customer_name' => '{{contact.name}}'
}
}
}
}
incoming_message.save!
body_params = incoming_message.additional_attributes['template_params']['processed_params']['body']
expect(body_params['customer_name']).to eq '{{contact.name}}'
end
it 'does not process template_params when not present' do
message.additional_attributes = { 'other_data' => 'test' }
expect { message.save! }.not_to raise_error
end
end
end
end