-
Notifications
You must be signed in to change notification settings - Fork 100
Expand file tree
/
Copy pathodoo_installer.sh
More file actions
1459 lines (1224 loc) · 54.1 KB
/
odoo_installer.sh
File metadata and controls
1459 lines (1224 loc) · 54.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/bin/bash
# Enhanced Odoo Installation Script for Ubuntu 22.04 - Complete Version with domain, Nginx, and SSL features
# Version: 2.1-COMPLETE
# Author: Mahmoud Abel Latif, https://mah007.net
# Description: Interactive Odoo installation with domain configuration, official Nginx, and SSL certificates
# Script configuration
SCRIPT_VERSION="2.1-COMPLETE"
SCRIPT_NAME="Enhanced Odoo Installer with Domain & SSL Support"
LOG_FILE="/tmp/odoo_install_$(date +%Y%m%d_%H%M%S).log"
CONFIG_FILE="/tmp/odoo_install_config.conf"
# Color definitions
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
WHITE='\033[1;37m'
NC='\033[0m' # No Color
BOLD='\033[1m'
# Progress tracking
TOTAL_STEPS=8
CURRENT_STEP=0
STEP_PROGRESS=0
# Installation configuration
OE_USER="odoo"
OE_BRANCH=""
INSTALL_WKHTMLTOPDF="True"
IS_ENTERPRISE="True"
WKHTML_X64="https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-2/wkhtmltox_0.12.6.1-2.jammy_amd64.deb"
WKHTML_X32="https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-2/wkhtmltox_0.12.6.1-2.jammy_amd64.deb"
# New variables for domain and SSL management
DOMAIN_NAME=""
HAS_DOMAIN="false"
INSTALL_NGINX="false"
SSL_TYPE="" # "self-signed" or "letsencrypt"
SERVER_IP=""
# Trap for cleanup on exit
trap cleanup_on_exit EXIT INT TERM
#==============================================================================
# UTILITY FUNCTIONS
#==============================================================================
# Logging function
log_message() {
local level="$1"
local message="$2"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$timestamp] [$level] $message" >> "$LOG_FILE"
case "$level" in
"ERROR")
echo -e "${RED}[ERROR]${NC} $message" >&2
;;
"WARNING")
echo -e "${YELLOW}[WARNING]${NC} $message"
;;
"INFO")
echo -e "${GREEN}[INFO]${NC} $message"
;;
"DEBUG")
echo -e "${CYAN}[DEBUG]${NC} $message"
;;
esac
}
# Progress bar function
show_progress_bar() {
local current="$1"
local total="$2"
local width=50
local percentage=$((current * 100 / total))
local completed=$((current * width / total))
local remaining=$((width - completed))
printf "\r${BLUE}["
printf "%*s" $completed | tr ' ' '='
printf "%*s" $remaining | tr ' ' '-'
printf "] %d%% (%d/%d)${NC}" $percentage $current $total
}
# Simple execute function without spinner for critical operations
execute_simple() {
local command="$1"
local description="$2"
log_message "DEBUG" "Executing: $command"
echo -e "${CYAN}$description...${NC}"
if eval "$command" >> "$LOG_FILE" 2>&1; then
echo -e "${GREEN}✓${NC} $description"
log_message "INFO" "Successfully completed: $description"
return 0
else
local exit_code=$?
echo -e "${RED}✗${NC} $description"
log_message "ERROR" "Failed to execute: $description (Exit code: $exit_code)"
return $exit_code
fi
}
# Enhanced billboard display
display_billboard() {
local message="$1"
local width=80
local padding=$(( (width - ${#message}) / 2 ))
echo
echo -e "${PURPLE}${BOLD}$(printf '%*s' $width | tr ' ' '=')"
echo -e "$(printf '%*s' $padding)${WHITE}$message${PURPLE}"
echo -e "$(printf '%*s' $width | tr ' ' '=')${NC}"
echo
}
# Step header
show_step_header() {
local step_num="$1"
local step_name="$2"
local step_desc="$3"
CURRENT_STEP=$step_num
echo
echo -e "${BOLD}${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${BOLD}${WHITE}Step $step_num/$TOTAL_STEPS: $step_name${NC}"
echo -e "${CYAN}$step_desc${NC}"
echo -e "${BOLD}${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
show_progress_bar $step_num $TOTAL_STEPS
echo
log_message "INFO" "Starting Step $step_num: $step_name"
}
# Cleanup function
cleanup_on_exit() {
local exit_code=$?
if [ $exit_code -ne 0 ]; then
echo
echo -e "${RED}${BOLD}Installation interrupted or failed!${NC}"
echo -e "${YELLOW}Check the log file for details: $LOG_FILE${NC}"
echo -e "${YELLOW}You can resume the installation by running this script again.${NC}"
fi
}
# Get server IP address
get_server_ip() {
# Try multiple methods to get the server IP
SERVER_IP=$(curl -s ifconfig.me 2>/dev/null || curl -s ipinfo.io/ip 2>/dev/null || curl -s icanhazip.com 2>/dev/null)
if [ -z "$SERVER_IP" ]; then
# Fallback to local IP
SERVER_IP=$(hostname -I | awk '{print $1}')
fi
if [ -z "$SERVER_IP" ]; then
SERVER_IP="Unable to detect"
log_message "WARNING" "Could not detect server IP address"
else
log_message "INFO" "Detected server IP: $SERVER_IP"
fi
}
#==============================================================================
# DOMAIN AND SSL CONFIGURATION
#==============================================================================
# Domain configuration
configure_domain() {
clear
display_billboard "Domain Configuration"
echo -e "${BOLD}${WHITE}Domain Setup for Odoo Installation${NC}"
echo
echo -e "${CYAN}This step configures your domain settings for Odoo.${NC}"
echo -e "${CYAN}If you have a domain, we can set up SSL certificates automatically.${NC}"
echo
# Get server IP
get_server_ip
echo -e "${YELLOW}Your server IP address: ${BOLD}$SERVER_IP${NC}"
echo
while true; do
echo -e -n "${BOLD}${WHITE}Do you have a domain name pointing to this server? [y/N]: ${NC}"
read -r has_domain_input
case "$has_domain_input" in
[Yy]|[Yy][Ee][Ss])
HAS_DOMAIN="true"
break
;;
[Nn]|[Nn][Oo]|"")
HAS_DOMAIN="false"
break
;;
*)
echo -e "${RED}Please answer yes (y) or no (n).${NC}"
;;
esac
done
if [ "$HAS_DOMAIN" = "true" ]; then
while true; do
echo -e -n "${BOLD}${WHITE}Enter your domain name (e.g., odoo.mycompany.com): ${NC}"
read -r domain_input
if [ -n "$domain_input" ]; then
# Basic domain validation
if [[ "$domain_input" =~ ^[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*$ ]]; then
DOMAIN_NAME="$domain_input"
echo -e "${GREEN}Domain set to: $DOMAIN_NAME${NC}"
# Verify domain DNS
verify_domain_dns
break
else
echo -e "${RED}Invalid domain format. Please enter a valid domain name.${NC}"
fi
else
echo -e "${RED}Domain name cannot be empty.${NC}"
fi
done
else
echo -e "${YELLOW}No domain configured. Will use IP address access only.${NC}"
DOMAIN_NAME="$SERVER_IP"
fi
log_message "INFO" "Domain configuration: HAS_DOMAIN=$HAS_DOMAIN, DOMAIN_NAME=$DOMAIN_NAME"
}
# Verify domain DNS configuration
verify_domain_dns() {
echo -e "${CYAN}Verifying domain DNS configuration...${NC}"
# Check if domain resolves to this server
local domain_ip=$(dig +short "$DOMAIN_NAME" 2>/dev/null | tail -n1)
if [ -n "$domain_ip" ]; then
if [ "$domain_ip" = "$SERVER_IP" ]; then
echo -e "${GREEN}✓ Domain DNS is correctly configured${NC}"
log_message "INFO" "Domain DNS verification successful: $DOMAIN_NAME -> $domain_ip"
else
echo -e "${YELLOW}⚠ Warning: Domain points to $domain_ip but server IP is $SERVER_IP${NC}"
echo -e "${YELLOW} SSL certificate generation may fail if DNS is not properly configured.${NC}"
log_message "WARNING" "Domain DNS mismatch: $DOMAIN_NAME -> $domain_ip (expected: $SERVER_IP)"
echo -e -n "${BOLD}${WHITE}Continue anyway? [y/N]: ${NC}"
read -r continue_anyway
if [[ ! "$continue_anyway" =~ ^[Yy]$ ]]; then
echo -e "${RED}Please configure your domain DNS to point to $SERVER_IP and try again.${NC}"
exit 1
fi
fi
else
echo -e "${YELLOW}⚠ Warning: Could not resolve domain $DOMAIN_NAME${NC}"
echo -e "${YELLOW} Please ensure your domain DNS is properly configured.${NC}"
log_message "WARNING" "Could not resolve domain: $DOMAIN_NAME"
echo -e -n "${BOLD}${WHITE}Continue anyway? [y/N]: ${NC}"
read -r continue_anyway
if [[ ! "$continue_anyway" =~ ^[Yy]$ ]]; then
echo -e "${RED}Please configure your domain DNS and try again.${NC}"
exit 1
fi
fi
}
# Nginx configuration
configure_nginx() {
clear
display_billboard "Web Server Configuration"
echo -e "${BOLD}${WHITE}Nginx Reverse Proxy Setup${NC}"
echo
echo -e "${CYAN}Nginx will act as a reverse proxy for Odoo, providing:${NC}"
echo -e " ${GREEN}•${NC} SSL/TLS encryption"
echo -e " ${GREEN}•${NC} Better performance and caching"
echo -e " ${GREEN}•${NC} Load balancing capabilities"
echo -e " ${GREEN}•${NC} Security enhancements"
echo
while true; do
echo -e -n "${BOLD}${WHITE}Do you want to install and configure Nginx? [Y/n]: ${NC}"
read -r install_nginx_input
case "$install_nginx_input" in
[Yy]|[Yy][Ee][Ss]|"")
INSTALL_NGINX="true"
break
;;
[Nn]|[Nn][Oo])
INSTALL_NGINX="false"
echo -e "${YELLOW}Nginx installation skipped. Odoo will be accessible directly on port 8069.${NC}"
break
;;
*)
echo -e "${RED}Please answer yes (y) or no (n).${NC}"
;;
esac
done
if [ "$INSTALL_NGINX" = "true" ]; then
configure_ssl
fi
log_message "INFO" "Nginx installation preference: $INSTALL_NGINX"
}
# SSL configuration
configure_ssl() {
echo
echo -e "${BOLD}${WHITE}SSL Certificate Configuration${NC}"
echo
if [ "$HAS_DOMAIN" = "true" ]; then
echo -e "${CYAN}Choose SSL certificate type:${NC}"
echo -e " ${YELLOW}1)${NC} Let's Encrypt (Free, automatic renewal) ${GREEN}[Recommended]${NC}"
echo -e " ${YELLOW}2)${NC} Self-signed certificate (For testing/internal use)"
echo
while true; do
echo -e -n "${BOLD}${WHITE}Enter your choice [1-2]: ${NC}"
read -r ssl_choice
case "$ssl_choice" in
1)
SSL_TYPE="letsencrypt"
echo -e "${GREEN}Selected: Let's Encrypt SSL certificate${NC}"
break
;;
2)
SSL_TYPE="self-signed"
echo -e "${YELLOW}Selected: Self-signed SSL certificate${NC}"
break
;;
*)
echo -e "${RED}Invalid choice. Please select 1 or 2.${NC}"
;;
esac
done
else
SSL_TYPE="self-signed"
echo -e "${YELLOW}No domain configured. Will use self-signed SSL certificate.${NC}"
fi
log_message "INFO" "SSL certificate type: $SSL_TYPE"
}
#==============================================================================
# VALIDATION FUNCTIONS
#==============================================================================
# Check if running as root
check_root() {
if [ "$EUID" -ne 0 ]; then
log_message "ERROR" "This script must be run as root (use sudo)"
exit 1
fi
}
# Check system requirements
check_system_requirements() {
local errors=0
# Check Ubuntu version
if ! lsb_release -d | grep -q "Ubuntu 22.04"; then
log_message "WARNING" "This script is optimized for Ubuntu 22.04. Current version: $(lsb_release -d | cut -f2)"
fi
# Check available disk space (minimum 10GB)
local available_space=$(df / | awk 'NR==2 {print $4}')
local required_space=10485760 # 10GB in KB
if [ "$available_space" -lt "$required_space" ]; then
log_message "ERROR" "Insufficient disk space. Required: 10GB, Available: $((available_space/1024/1024))GB"
errors=$((errors + 1))
fi
# Check memory (minimum 2GB)
local total_memory=$(free -m | awk 'NR==2{print $2}')
if [ "$total_memory" -lt 2048 ]; then
log_message "WARNING" "Low memory detected. Recommended: 2GB+, Available: ${total_memory}MB"
fi
# Check internet connectivity
if ! ping -c 1 google.com &> /dev/null; then
log_message "ERROR" "No internet connection detected"
errors=$((errors + 1))
fi
return $errors
}
# Validate Odoo version selection
validate_odoo_version() {
case "$OE_BRANCH" in
"14.0"|"15.0"|"16.0"|"17.0"|"18.0")
return 0
;;
*)
log_message "ERROR" "Invalid Odoo version: $OE_BRANCH"
return 1
;;
esac
}
#==============================================================================
# INTERACTIVE FUNCTIONS
#==============================================================================
# Odoo version selection
select_odoo_version() {
while true; do
clear
display_billboard "Odoo Version Selection"
echo -e "${BOLD}${WHITE}Please select the Odoo version to install:${NC}"
echo
echo -e " ${YELLOW}1)${NC} Odoo 14.0 ${CYAN}(LTS - Long Term Support)${NC}"
echo -e " ${YELLOW}2)${NC} Odoo 15.0 ${CYAN}(Stable)${NC}"
echo -e " ${YELLOW}3)${NC} Odoo 16.0 ${CYAN}(Stable)${NC}"
echo -e " ${YELLOW}4)${NC} Odoo 17.0 ${CYAN}(Latest Stable)${NC}"
echo -e " ${YELLOW}5)${NC} Odoo 18.0 ${CYAN}(Latest - May have issues)${NC}"
echo -e " ${YELLOW}6)${NC} Back to Main Menu"
echo
echo -e -n "${BOLD}${WHITE}Enter your choice [1-6]: ${NC}"
read -r choice
case "$choice" in
1) OE_BRANCH="14.0"; break;;
2) OE_BRANCH="15.0"; break;;
3) OE_BRANCH="16.0"; break;;
4) OE_BRANCH="17.0"; break;;
5) OE_BRANCH="18.0"; break;;
6) return 1;;
*)
echo -e "${RED}Invalid choice. Please select 1-6.${NC}"
sleep 2
;;
esac
done
echo -e "${GREEN}Selected Odoo version: $OE_BRANCH${NC}"
log_message "INFO" "User selected Odoo version: $OE_BRANCH"
return 0
}
# Installation confirmation
confirm_installation() {
clear
display_billboard "Installation Confirmation"
echo -e "${BOLD}${WHITE}Installation Summary:${NC}"
echo -e " ${CYAN}Odoo Version:${NC} $OE_BRANCH"
echo -e " ${CYAN}System User:${NC} $OE_USER"
echo -e " ${CYAN}Domain:${NC} ${DOMAIN_NAME:-"IP-based access"}"
echo -e " ${CYAN}Install Nginx:${NC} $INSTALL_NGINX"
if [ "$INSTALL_NGINX" = "true" ]; then
echo -e " ${CYAN}SSL Certificate:${NC} $SSL_TYPE"
fi
echo -e " ${CYAN}Install wkhtmltopdf:${NC} $INSTALL_WKHTMLTOPDF"
echo -e " ${CYAN}Enterprise Features:${NC} $IS_ENTERPRISE"
echo -e " ${CYAN}Log File:${NC} $LOG_FILE"
echo
echo -e "${YELLOW}${BOLD}WARNING:${NC} This installation will:"
echo -e " • Modify system packages and configurations"
echo -e " • Create system users and directories"
echo -e " • Install and configure PostgreSQL"
echo -e " • Download and install Odoo from source"
if [ "$INSTALL_NGINX" = "true" ]; then
echo -e " • Install and configure Nginx with SSL"
fi
echo
while true; do
echo -e -n "${BOLD}${WHITE}Do you want to proceed with the installation? [y/N]: ${NC}"
read -r confirm
case "$confirm" in
[Yy]|[Yy][Ee][Ss])
return 0
;;
[Nn]|[Nn][Oo]|"")
echo -e "${YELLOW}Installation cancelled by user.${NC}"
return 1
;;
*)
echo -e "${RED}Please answer yes (y) or no (n).${NC}"
;;
esac
done
}
# Continue with installation functions...
#==============================================================================
# INSTALLATION FUNCTIONS
#==============================================================================
# Step 1: Pre-flight checks
step_preflight_checks() {
show_step_header 1 "Pre-flight Checks" "Validating system requirements and configuration"
# Check if running as root
check_root
# System requirements check
if ! check_system_requirements; then
log_message "ERROR" "System requirements check failed"
exit 1
fi
# Validate Odoo version
if ! validate_odoo_version; then
log_message "ERROR" "Invalid Odoo version configuration"
exit 1
fi
log_message "INFO" "Pre-flight checks completed successfully"
}
# Step 2: System preparation
step_system_preparation() {
show_step_header 2 "System Preparation" "Creating users and updating system packages"
# Create Odoo user and group
execute_simple "groupadd -f $OE_USER" "Creating Odoo group"
execute_simple "useradd --create-home -d /home/$OE_USER --shell /bin/bash -g $OE_USER $OE_USER 2>/dev/null || true" "Creating Odoo user"
execute_simple "usermod -aG sudo $OE_USER" "Adding Odoo user to sudo group"
# Update system packages
execute_simple "apt-get update" "Updating package lists"
execute_simple "apt-get upgrade -y" "Upgrading system packages"
execute_simple "apt install -y zip gdebi net-tools curl wget gnupg2 software-properties-common" "Installing basic tools"
# Configure localization
execute_simple "export LC_ALL=en_US.UTF-8 && export LC_CTYPE=en_US.UTF-8" "Setting locale variables"
execute_simple "dpkg-reconfigure -f noninteractive locales" "Configuring locales"
log_message "INFO" "System preparation completed successfully"
}
# Step 3: Database setup
step_database_setup() {
show_step_header 3 "Database Setup" "Installing and configuring PostgreSQL database"
# Add PostgreSQL repository
execute_simple "sh -c 'echo \"deb [arch=amd64] http://apt.postgresql.org/pub/repos/apt jammy-pgdg main\" > /etc/apt/sources.list.d/pgdg.list'" "Adding PostgreSQL repository"
# Add PostgreSQL signing key with error handling
if ! execute_simple "wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -" "Adding PostgreSQL signing key"; then
log_message "WARNING" "Failed to add PostgreSQL key via apt-key, trying alternative method"
execute_simple "wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /etc/apt/trusted.gpg.d/postgresql.gpg > /dev/null" "Adding PostgreSQL signing key (alternative method)"
fi
# Update package lists
execute_simple "apt-get update" "Updating package lists with PostgreSQL repository"
# Install PostgreSQL
execute_simple "apt-get install -y postgresql-16 postgresql-server-dev-16" "Installing PostgreSQL 16"
# Create PostgreSQL user for Odoo
execute_simple "su - postgres -c \"createuser -s $OE_USER\" 2>/dev/null || true" "Creating PostgreSQL user for Odoo"
# Verify PostgreSQL installation
if ! systemctl is-active --quiet postgresql; then
log_message "ERROR" "PostgreSQL service is not running"
execute_simple "systemctl start postgresql" "Starting PostgreSQL service"
execute_simple "systemctl enable postgresql" "Enabling PostgreSQL service"
fi
log_message "INFO" "Database setup completed successfully"
}
# Step 4: Dependencies installation
step_dependencies_installation() {
show_step_header 4 "Dependencies Installation" "Installing Python packages and system libraries"
# Install system dependencies with better error handling
local system_packages=(
"git" "python3-pip" "build-essential" "wget" "python3-dev"
"python3-venv" "python3-wheel" "libfreetype6-dev" "libxml2-dev"
"libzip-dev" "libldap2-dev" "libsasl2-dev" "python3-setuptools"
"node-less" "libjpeg-dev" "zlib1g-dev" "libpq-dev" "libtiff5-dev"
"libjpeg8-dev" "libopenjp2-7-dev" "liblcms2-dev" "libwebp-dev"
"libharfbuzz-dev" "libfribidi-dev" "libxcb1-dev" "libwww-perl"
"gsfonts" "libcairo2-dev" "python3-cairo"
)
echo -e "${CYAN}Installing system packages (some may fail, this is normal)...${NC}"
local failed_packages=()
for package in "${system_packages[@]}"; do
if ! dpkg -l | grep -q "^ii $package "; then
if execute_simple "apt-get install -y $package" "Installing $package"; then
echo -e "${GREEN}✓${NC} $package installed successfully"
else
echo -e "${YELLOW}⚠${NC} $package installation failed (will continue)"
failed_packages+=("$package")
log_message "WARNING" "Failed to install $package, continuing with installation"
fi
else
echo -e "${GREEN}✓${NC} $package is already installed"
log_message "INFO" "$package is already installed"
fi
done
if [ ${#failed_packages[@]} -gt 0 ]; then
echo -e "${YELLOW}Failed packages: ${failed_packages[*]}${NC}"
log_message "WARNING" "Some packages failed to install: ${failed_packages[*]}"
fi
# Install Python libraries with error handling
echo -e "${CYAN}Installing Python libraries...${NC}"
if ! execute_simple "pip3 install gdata psycogreen" "Installing Python data libraries"; then
log_message "WARNING" "Failed to install Python data libraries, continuing"
fi
if ! execute_simple "pip3 install suds" "Installing SUDS library"; then
log_message "WARNING" "Failed to install SUDS library, continuing"
fi
if ! execute_simple "pip3 install rtPyCairo" "Installing Cairo Python bindings"; then
log_message "WARNING" "Failed to install Cairo Python bindings, continuing"
fi
# Install Node.js and npm packages
execute_simple "apt-get install -y ca-certificates curl gnupg" "Installing Node.js prerequisites"
execute_simple "mkdir -p /etc/apt/keyrings" "Creating keyrings directory"
if ! execute_simple "curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg" "Adding Node.js repository key"; then
log_message "ERROR" "Failed to add Node.js repository key"
return 1
fi
execute_simple "echo 'deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main' | tee /etc/apt/sources.list.d/nodesource.list" "Adding Node.js repository"
execute_simple "apt-get update" "Updating package lists with Node.js repository"
execute_simple "apt-get install -y nodejs" "Installing Node.js"
# Create symbolic link for node
execute_simple "ln -sf /usr/bin/nodejs /usr/bin/node" "Creating Node.js symbolic link"
# Install npm packages for Enterprise features
if [ "$IS_ENTERPRISE" = "True" ]; then
execute_simple "npm install -g less" "Installing Less CSS preprocessor"
execute_simple "npm install -g less-plugin-clean-css" "Installing Less clean CSS plugin"
execute_simple "npm install -g rtlcss" "Installing RTL CSS processor"
fi
log_message "INFO" "Dependencies installation completed successfully"
}
# Step 5: Wkhtmltopdf installation
step_wkhtmltopdf_installation() {
show_step_header 5 "Wkhtmltopdf Installation" "Installing PDF generation library"
if [ "$INSTALL_WKHTMLTOPDF" = "True" ]; then
# Determine architecture
if [ "$(getconf LONG_BIT)" == "64" ]; then
local wkhtml_url="$WKHTML_X64"
else
local wkhtml_url="$WKHTML_X32"
fi
local wkhtml_file=$(basename "$wkhtml_url")
# Download wkhtmltopdf
if ! execute_simple "wget -O /tmp/$wkhtml_file $wkhtml_url" "Downloading wkhtmltopdf"; then
log_message "ERROR" "Failed to download wkhtmltopdf"
return 1
fi
# Install wkhtmltopdf
execute_simple "gdebi --non-interactive /tmp/$wkhtml_file" "Installing wkhtmltopdf"
# Cleanup
execute_simple "rm -f /tmp/$wkhtml_file" "Cleaning up wkhtmltopdf installer"
# Verify installation
if command -v wkhtmltopdf &> /dev/null; then
log_message "INFO" "Wkhtmltopdf installed successfully: $(wkhtmltopdf --version | head -n1)"
else
log_message "ERROR" "Wkhtmltopdf installation verification failed"
return 1
fi
else
log_message "INFO" "Wkhtmltopdf installation skipped by user configuration"
fi
log_message "INFO" "Wkhtmltopdf installation completed"
}
# Step 6: Odoo installation
step_odoo_installation() {
show_step_header 6 "Odoo Installation" "Downloading and configuring Odoo source code"
# Install additional Python packages
local python_packages=(
"python3-dev" "python3-asn1crypto" "python3-babel" "python3-bs4"
"python3-cffi-backend" "python3-cryptography" "python3-dateutil"
"python3-docutils" "python3-feedparser" "python3-funcsigs"
"python3-gevent" "python3-greenlet" "python3-html2text"
"python3-html5lib" "python3-jinja2" "python3-lxml" "python3-mako"
"python3-markupsafe" "python3-mock" "python3-ofxparse"
"python3-openssl" "python3-passlib" "python3-pbr" "python3-pil"
"python3-psutil" "python3-psycopg2" "python3-pydot" "python3-pygments"
"python3-pypdf2" "python3-renderpm" "python3-reportlab"
"python3-reportlab-accel" "python3-roman" "python3-serial"
"python3-stdnum" "python3-suds" "python3-tz" "python3-usb"
"python3-werkzeug" "python3-xlsxwriter" "python3-yaml"
)
echo -e "${CYAN}Installing Python packages for Odoo...${NC}"
for package in "${python_packages[@]}"; do
if ! dpkg -l | grep -q "^ii $package "; then
if ! execute_simple "apt-get install -y $package" "Installing $package"; then
log_message "WARNING" "Failed to install $package, continuing"
fi
fi
done
# Install Python packages via pip
execute_simple "easy_install greenlet" "Installing greenlet"
execute_simple "easy_install gevent" "Installing gevent"
# Create Odoo directories
execute_simple "mkdir -p /odoo" "Creating Odoo directory"
execute_simple "mkdir -p /etc/odoo" "Creating Odoo configuration directory"
execute_simple "mkdir -p /var/log/odoo" "Creating Odoo log directory"
# Create log file
execute_simple "touch /var/log/odoo/odoo-server.log" "Creating Odoo log file"
# Set proper ownership
execute_simple "chown -R $OE_USER:$OE_USER /var/log/odoo" "Setting ownership for log directory"
execute_simple "chown -R $OE_USER:$OE_USER /etc/odoo" "Setting ownership for configuration directory"
# Clone Odoo repository
cd /odoo || exit 1
if ! execute_simple "git clone --depth 1 --branch $OE_BRANCH https://www.github.com/odoo/odoo" "Cloning Odoo repository"; then
log_message "ERROR" "Failed to clone Odoo repository"
return 1
fi
# Clone OdooEE repository
# cd /odoo || exit 1
# if ! execute_simple "git clone --depth 1 --branch $OE_BRANCH https://mah007:[email protected]/odoo/enterprise" "Cloning OdooEE repository"; then
# log_message "ERROR" "Failed to clone Odoo repository"
# return 1
# fi
# Set ownership for Odoo directory
execute_simple "chown -R $OE_USER:$OE_USER /odoo" "Setting ownership for Odoo directory"
# Install Odoo Python requirements
execute_simple "su - $OE_USER -s /bin/bash -c \"pip3 install -r https://raw.githubusercontent.com/odoo/odoo/$OE_BRANCH/requirements.txt --user\"" "Installing Odoo Python requirements"
execute_simple "su - $OE_USER -s /bin/bash -c \"pip3 install phonenumbers --user\"" "Installing phonenumbers library"
log_message "INFO" "Odoo installation completed successfully"
}
#==============================================================================
# NGINX INSTALLATION AND CONFIGURATION
#==============================================================================
# Install official Nginx (latest version)
install_official_nginx() {
echo -e "${CYAN}Installing official Nginx (latest version)...${NC}"
# Remove any existing Nginx installation
execute_simple "apt-get remove -y nginx nginx-common nginx-core" "Removing existing Nginx packages"
execute_simple "apt-get autoremove -y" "Cleaning up unused packages"
# Add official Nginx repository
execute_simple "curl -fsSL https://nginx.org/keys/nginx_signing.key | apt-key add -" "Adding Nginx signing key"
execute_simple "echo 'deb https://nginx.org/packages/ubuntu/ jammy nginx' > /etc/apt/sources.list.d/nginx.list" "Adding Nginx repository"
execute_simple "echo 'deb-src https://nginx.org/packages/ubuntu/ jammy nginx' >> /etc/apt/sources.list.d/nginx.list" "Adding Nginx source repository"
# Set repository priority
cat > /etc/apt/preferences.d/99nginx << EOF
Package: *
Pin: origin nginx.org
Pin: release o=nginx
Pin-Priority: 900
EOF
# Update and install Nginx
execute_simple "apt-get update" "Updating package lists with Nginx repository"
execute_simple "apt-get install -y nginx" "Installing official Nginx"
# Verify Nginx installation
local nginx_version=$(nginx -v 2>&1 | cut -d' ' -f3 | cut -d'/' -f2)
log_message "INFO" "Nginx installed successfully: version $nginx_version"
# Enable and start Nginx
execute_simple "systemctl enable nginx" "Enabling Nginx service"
execute_simple "systemctl start nginx" "Starting Nginx service"
# Verify Nginx is running
if systemctl is-active --quiet nginx; then
log_message "INFO" "Nginx service is running"
else
log_message "ERROR" "Nginx service failed to start"
return 1
fi
}
# Generate self-signed SSL certificate
generate_self_signed_ssl() {
echo -e "${CYAN}Generating self-signed SSL certificate...${NC}"
# Create SSL directory
execute_simple "mkdir -p /etc/ssl/nginx" "Creating SSL directory"
# Generate private key and certificate
execute_simple "openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/ssl/nginx/server.key \
-out /etc/ssl/nginx/server.crt \
-subj \"/C=US/ST=State/L=City/O=Organization/OU=OrgUnit/CN=$DOMAIN_NAME\"" "Generating SSL certificate"
# Set proper permissions
execute_simple "chmod 600 /etc/ssl/nginx/server.key" "Setting SSL key permissions"
execute_simple "chmod 644 /etc/ssl/nginx/server.crt" "Setting SSL certificate permissions"
log_message "INFO" "Self-signed SSL certificate generated successfully"
}
# Install and configure Let's Encrypt
install_letsencrypt_ssl() {
echo -e "${CYAN}Installing Let's Encrypt SSL certificate...${NC}"
# Install snapd if not present
if ! command -v snap &> /dev/null; then
execute_simple "apt-get install -y snapd" "Installing snapd"
execute_simple "systemctl enable --now snapd.socket" "Enabling snapd"
execute_simple "ln -s /var/lib/snapd/snap /snap" "Creating snap symlink"
fi
# Remove any existing certbot packages
execute_simple "apt-get remove -y certbot" "Removing existing certbot packages"
# Install certbot via snap
execute_simple "snap install --classic certbot" "Installing Certbot via snap"
execute_simple "ln -sf /snap/bin/certbot /usr/bin/certbot" "Creating Certbot symlink"
# Create temporary Nginx configuration for domain verification
create_temporary_nginx_config
# Reload Nginx with temporary config
execute_simple "nginx -t" "Testing Nginx configuration"
execute_simple "systemctl reload nginx" "Reloading Nginx"
# Get SSL certificate
if execute_simple "certbot --nginx -d $DOMAIN_NAME --non-interactive --agree-tos --email admin@$DOMAIN_NAME" "Obtaining Let's Encrypt certificate"; then
log_message "INFO" "Let's Encrypt SSL certificate obtained successfully"
# Test automatic renewal
execute_simple "certbot renew --dry-run" "Testing automatic renewal"
else
log_message "ERROR" "Failed to obtain Let's Encrypt certificate, falling back to self-signed"
SSL_TYPE="self-signed"
generate_self_signed_ssl
fi
}
# Create temporary Nginx configuration for Let's Encrypt verification
create_temporary_nginx_config() {
cat > /etc/nginx/conf.d/temp_odoo.conf << EOF
server {
listen 80;
server_name $DOMAIN_NAME;
location / {
return 200 'Temporary configuration for SSL setup';
add_header Content-Type text/plain;
}
}
EOF
log_message "INFO" "Created temporary Nginx configuration"
}
# Create Nginx configuration for Odoo
create_nginx_odoo_config() {
echo -e "${CYAN}Creating Nginx configuration for Odoo...${NC}"
# Remove temporary configuration
execute_simple "rm -f /etc/nginx/conf.d/temp_odoo.conf" "Removing temporary configuration"
# Determine SSL certificate paths
local ssl_cert_path
local ssl_key_path
if [ "$SSL_TYPE" = "letsencrypt" ]; then
ssl_cert_path="/etc/letsencrypt/live/$DOMAIN_NAME/fullchain.pem"
ssl_key_path="/etc/letsencrypt/live/$DOMAIN_NAME/privkey.pem"
else
ssl_cert_path="/etc/ssl/nginx/server.crt"
ssl_key_path="/etc/ssl/nginx/server.key"
fi
# Create Nginx configuration based on template
cat > /etc/nginx/conf.d/odoo.conf << EOF
#odoo server
upstream odoo {
server 127.0.0.1:8069;
}
upstream odoochat {
server 127.0.0.1:8072;
}
map \$http_upgrade \$connection_upgrade {
default upgrade;
'' close;
}
# http -> https
server {
listen 80;
server_name $DOMAIN_NAME;
rewrite ^(.*) https://\$host\$1 permanent;
}
server {
listen 443 ssl;
server_name $DOMAIN_NAME;
proxy_read_timeout 720s;
proxy_connect_timeout 720s;
proxy_send_timeout 720s;
# SSL parameters
ssl_certificate $ssl_cert_path;
ssl_certificate_key $ssl_key_path;
ssl_session_timeout 30m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# log
access_log /var/log/nginx/odoo.access.log;
error_log /var/log/nginx/odoo.error.log;
# Redirect websocket requests to odoo gevent port
location /websocket {
proxy_pass http://odoochat;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection \$connection_upgrade;
proxy_set_header X-Forwarded-Host \$http_host;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_set_header X-Real-IP \$remote_addr;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
proxy_cookie_flags session_id samesite=lax secure;
}
# Redirect requests to odoo backend server
location / {
# Add Headers for odoo proxy mode
proxy_set_header X-Forwarded-Host \$http_host;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_set_header X-Real-IP \$remote_addr;
proxy_redirect off;
proxy_pass http://odoo;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
proxy_cookie_flags session_id samesite=lax secure;
}
# common gzip
gzip_types text/css text/scss text/plain text/xml application/xml application/json application/javascript;
gzip on;
}
EOF
# Test Nginx configuration
if execute_simple "nginx -t" "Testing Nginx configuration"; then
execute_simple "systemctl reload nginx" "Reloading Nginx with Odoo configuration"
log_message "INFO" "Nginx configuration created and loaded successfully"
else
log_message "ERROR" "Nginx configuration test failed"
return 1
fi
}
#==============================================================================
# DYNAMIC ODOO CONFIGURATION
#==============================================================================
# Generate dynamic Odoo configuration
generate_odoo_config() {
echo -e "${CYAN}Generating dynamic Odoo configuration...${NC}"
# Create basic configuration file structure
cat > /etc/odoo/odoo.conf << EOF
[options]
; Basic Odoo configuration
db_host = False