Thứ sáu, 22/02/2019 | 00:00 GMT+7

Cách thiết lập ứng dụng CakePHP với LAMP trên Ubuntu 18.04

CakePHP là một khung công tác web PHP phổ biến và giàu tính năng. Nó giải quyết nhiều vấn đề phổ biến trong phát triển web, chẳng hạn như tương tác với database , bảo vệ chống lại việc tiêm SQL và tạo mã chế độ xem. Nó tuân theo mô hình model-view-controller (MVC), phân tách các phần khác nhau của ứng dụng, cho phép các nhà phát triển làm việc song song trên các phần khác nhau của ứng dụng một cách hiệu quả. Nó cũng cung cấp tính năng bảo mật và xác thực tích hợp. Để tạo một ứng dụng database cơ bản là một quá trình liền mạch, điều này làm cho CakePHP hữu ích cho việc tạo mẫu. Tuy nhiên, bạn cũng có thể sử dụng CakePHP để tạo các ứng dụng web được phát triển đầy đủ để triển khai.

Trong hướng dẫn này, bạn sẽ triển khai ứng dụng web CakePHP mẫu cho môi trường production . Để làm điều này, bạn sẽ cài đặt một database và user mẫu, cấu hình Apache, kết nối ứng dụng của bạn với database và tắt chế độ gỡ lỗi. Bạn cũng sẽ sử dụng CakePHP của bake lệnh để tự động tạo ra mô hình bài viết.

Yêu cầu

Trước khi bắt đầu hướng dẫn này, bạn cần :

  • Server chạy Ubuntu 18.04 có quyền truy cập root và account sudo, không phải root, bạn có thể cài đặt điều này theo hướng dẫn cài đặt server ban đầu này .
  • LAMP được cài đặt theo Cách cài đặt ngăn xếp Linux, Apache, MySQL, PHP (LAMP) trên Ubuntu 18.04 . Tại thời điểm viết bài này, PHP 7.2 là version mới nhất.
  • Composer (một trình quản lý gói PHP) được cài đặt trên server của bạn. Để có hướng dẫn về cách thực hiện điều đó, hãy truy cập Cách cài đặt và sử dụng composer trên Ubuntu 18.04 . Bạn chỉ cần hoàn thành hai bước đầu tiên từ hướng dẫn đó.
  • Apache được bảo mật bằng Let's Encrypt. Để hoàn thành yêu cầu này, trước tiên bạn cần cài đặt server ảo theo Bước 5 của Cách cài đặt Apache trên Ubuntu 18.04 . Sau đó, bạn có thể làm theo Cách bảo mật Apache bằng Let's Encrypt trên Ubuntu 18.04 để bảo mật Apache bằng Let's Encrypt. Khi được hỏi, hãy bật chuyển hướng HTTPS bắt buộc.
  • Tên domain đã đăng ký đầy đủ. Hướng dẫn này sẽ sử dụng example.com xuyên suốt. Bạn có thể mua domain trên Namecheap , nhận một domain miễn phí trên Freenom hoặc sử dụng công ty đăng ký domain mà bạn chọn.
  • Cả hai bản ghi DNS sau được cài đặt cho server của bạn. Bạn có thể theo dõi phần giới thiệu này về DigitalOcean DNS để biết chi tiết về cách thêm chúng.
    • Bản ghi A với example.com trỏ đến địa chỉ IP công cộng của server của bạn.
    • Bản ghi A với www.example.com trỏ đến địa chỉ IP công cộng của server của bạn.

Bước 1 - Cài đặt phụ thuộc

Để chuẩn bị cho ứng dụng của bạn , bạn sẽ bắt đầu bằng cách cài đặt các phần mở rộng PHP mà CakePHP cần.

Bắt đầu bằng cách cập nhật bộ nhớ cache của trình quản lý gói:

  • sudo apt update

CakePHP yêu cầu các phần mở rộng PHP mbstring , intlsimplexml , bổ sung hỗ trợ cho các chuỗi multibyte, quốc tế hóa và xử lý XML. Bạn đã cài đặt mbstring như một phần của hướng dẫn yêu cầu của Composer . Bạn có thể cài đặt các thư viện còn lại bằng một lệnh:

  • sudo apt install php7.2-intl php7.2-xml -y

Lưu ý số version ở trên (7.2) sẽ thay đổi với các version PHP mới.

Bạn đã cài đặt các phụ thuộc cho CakePHP. Đến đây bạn đã sẵn sàng để cấu hình database MySQL của bạn để sử dụng trong production .

Bước 2 - Cài đặt database MySQL

Bây giờ, bạn sẽ tạo một database MySQL để lưu trữ thông tin về các bài viết trên blog của bạn. Bạn cũng cần tạo một user database mà ứng dụng của bạn sẽ sử dụng để truy cập database . Bạn sẽ sửa đổi các quyền database để đạt được sự phân tách quyền kiểm soát này. Do đó, các tác nhân xấu sẽ không thể gây ra sự cố trên hệ thống ngay cả với thông tin đăng nhập database , đây là một biện pháp phòng ngừa bảo mật quan trọng trong môi trường production .

Chạy shell MySQL của bạn:

  • sudo mysql -u root -p

Khi được hỏi, hãy nhập password bạn đã cài đặt trong quá trình cài đặt LAMP ban đầu.

Tiếp theo, tạo database :

  • CREATE DATABASE cakephp_blog;

Bạn sẽ thấy kết quả tương tự như:

Output
Query OK, 1 row affected (0.00 sec)

Ứng dụng CakePHP của bạn sẽ sử dụng database mới này để đọc và lưu trữ dữ liệu production .

Sau đó, hướng dẫn MySQL hoạt động trên database cakephp_blog mới:

  • USE cakephp_blog;

Bạn sẽ thấy kết quả tương tự như:

Output
Database changed

Đến đây bạn sẽ tạo một schemas bảng cho các bài viết trên blog của bạn trong database cakephp_blog . Chạy lệnh sau để cài đặt :

  • CREATE TABLE articles (
  • id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  • title VARCHAR(50),
  • body TEXT,
  • created DATETIME DEFAULT NULL,
  • modified DATETIME DEFAULT NULL
  • );

Bạn đã tạo một schemas với năm trường để mô tả các bài viết trên blog:

  • id : là định danh duy nhất của một bài viết, được cài đặt làm khóa chính.
  • title : là tiêu đề của một bài báo, được khai báo dưới dạng trường văn bản chứa tối đa 50 ký tự.
  • body : là nội dung của bài viết, được khai báo là trường TEXT .
  • created : là ngày và giờ tạo bản ghi.
  • modified : là ngày và giờ sửa đổi bản ghi.

Đầu ra sẽ tương tự như:

Output
Query OK, 0 rows affected (0.01 sec)

Bạn đã tạo một bảng để lưu trữ các bài báo trong database cakephp_blog . Tiếp theo, điền vào nó với các bài viết ví dụ bằng cách chạy lệnh sau:

  • INSERT INTO articles (title, body, created)
  • VALUES ('Sample title', 'This is the article body.', NOW());

Bạn đã thêm một bài viết ví dụ với một số dữ liệu mẫu cho tiêu đề và nội dung.

Bạn sẽ thấy kết quả sau:

Output
Query OK, 0 rows affected (0.01 sec)

Để kết nối ứng dụng CakePHP với database , bạn cần tạo user database mới và hạn chế các quyền của nó:

  • GRANT ALL PRIVILEGES ON cakephp_blog.* TO 'cake_user'@'localhost' IDENTIFIED BY 'password';

Lệnh này cấp tất cả các quyền cho tất cả các bảng trong database .

Hãy nhớ thay thế password bằng một password mạnh mà bạn chọn.

Để cập nhật database của bạn với những thay đổi bạn đã thực hiện, hãy reload bằng lệnh:

  • FLUSH PRIVILEGES;

Bạn vừa tạo một user database mới, cake_user và chỉ cấp cho user các quyền trên database cakephp_blog , do đó, thắt chặt bảo mật.

Thoát khỏi terminal MySQL bằng lệnh exit .

Bạn đã tạo một database mới với một schemas , điền vào nó với dữ liệu mẫu và tạo một user database thích hợp. Trong bước tiếp theo, bạn sẽ cài đặt chính ứng dụng CakePHP.

Bước 3 - Tạo Ứng dụng Blog

Trong phần này, bạn sẽ sử dụng Composer để cài đặt ứng dụng CakePHP mẫu. Sẽ rất thuận lợi khi sử dụng Composer vì nó cho phép bạn cài đặt CakePHP từ dòng lệnh của bạn và nó tự động cài đặt các quyền file nhất định và file cấu hình.

Đầu tiên, chuyển đến folder web server Apache:

  • cd /var/www/example.com/html

Apache sử dụng folder này để lưu trữ các file hiển thị cho thế giới bên ngoài. User root sở hữu folder này và vì vậy user không phải root của bạn, sammy , không thể ghi bất cứ thứ gì vào nó. Để sửa lỗi này, bạn sẽ thay đổi quyền của hệ thống file bằng lệnh:

  • sudo chown -R sammy .

Đến đây bạn sẽ tạo một ứng dụng CakePHP mới thông qua Composer:

  • composer create-project --prefer-dist cakephp/app cake-blog

Ở đây bạn đã gọi trình composer và hướng dẫn nó tạo một dự án mới với create-project . --prefer-dist cakephp/app yêu cầu composer sử dụng CakePHP làm mẫu với cake-blog là tên của ứng dụng mới.

Lưu ý lệnh này có thể mất một chút thời gian để hoàn thành.

Khi Composer yêu cầu bạn cài đặt quyền đối với folder , hãy trả lời bằng y .

Trong phần này, bạn đã tạo một dự án CakePHP mới bằng Composer. Trong bước tiếp theo, bạn sẽ cấu hình Apache để trỏ đến ứng dụng mới, điều này sẽ làm cho nó có thể xem được trong trình duyệt của bạn.

Bước 4 - Cấu hình Apache để trỏ đến ứng dụng của bạn

Bây giờ, bạn sẽ cấu hình Apache cho ứng dụng CakePHP mới của bạn , cũng như bật overrides .htaccess , đây là một yêu cầu của CakePHP. Điều này đòi hỏi phải chỉnh sửa các file cấu hình Apache.

Để định tuyến thực sự diễn ra, bạn phải hướng dẫn Apache sử dụng .htaccess . Đây là các file cấu hình sẽ nằm trong các folder con của ứng dụng (nếu cần) và sau đó Apache sử dụng các file đó để thay đổi cấu hình chung của nó cho phần được yêu cầu của ứng dụng. Trong số các việc khác, chúng sẽ chứa các luật viết lại URL mà bạn sẽ điều chỉnh ngay bây giờ.

Bắt đầu bằng cách mở file cấu hình chung Apache ( apache2.conf ) bằng editor của bạn:

  • sudo nano /etc/apache2/apache2.conf

Tìm khối mã sau:

/etc/apache2/apache2.conf
... <Directory /var/www/>         Options Indexes FollowSymLinks         AllowOverride None         Require all granted </Directory> ... 

Thay đổi AllowOverride từ None thành All , như sau:

/etc/apache2/apache2.conf
... <Directory /var/www/>         Options Indexes FollowSymLinks         AllowOverride All         Require all granted </Directory> ... 

Lưu và đóng file .

Tiếp theo, bạn sẽ hướng dẫn Apache trỏ đến folder webroot trong cài đặt CakePHP. Apache lưu trữ các file cấu hình của nó trên Ubuntu 18.04 trong /etc/apache2/sites-available . Các file này chi phối cách Apache xử lý các yêu cầu web.

Trong hướng dẫn yêu cầu Let's Encrypt, bạn đã bật chuyển hướng HTTPS; do đó chỉ cho phép truy cập HTTP S. Do đó, bạn sẽ chỉ chỉnh sửa file example.com -le-ssl.conf , file này cấu hình truy cập HTTP S.

Đầu tiên, hãy mở file cấu hình example.com -le-ssl.conf :

  • sudo nano /etc/apache2/sites-available/example.com-le-ssl.conf

Bạn chỉ cần thay đổi một dòng, dòng cài đặt DocumentRoot và cho Apache biết nơi phân phối nội dung cho trình duyệt. Tìm dòng sau trong file :

/etc/apache2/sites-available/example.com-le-ssl.conf
DocumentRoot /var/www/example.com/html 

Chỉnh sửa dòng này để trỏ đến cài đặt CakePHP, bằng cách thêm nội dung được đánh dấu sau:

/etc/apache2/sites-available/example.com-le-ssl.conf
DocumentRoot /var/www/example.com/html/cake-blog/webroot 

Lưu file và thoát khỏi editor .

Sau đó, khởi động lại Apache để phản ánh cấu hình mới:

  • sudo systemctl restart apache2

Đến đây bạn có thể truy cập https:// your_domain / trong trình duyệt của bạn .

CakePHP không thể kết nối với database

Bạn sẽ thấy trang thành công CakePHP mặc định. Bạn sẽ nhận thấy rằng có một khối cho biết ứng dụng của bạn không thể kết nối với database . Trong bước tiếp theo, bạn sẽ giải quyết vấn đề này bằng cách kết nối ứng dụng của bạn với database .

Đến đây bạn đã bật overrides .htaccess và trỏ Apache đến đúng folder webroot .

Bước 5 - Kết nối ứng dụng của bạn với database

Trong phần này, bạn sẽ kết nối database của bạn với ứng dụng để blog của bạn có thể truy cập các bài báo. Bạn sẽ chỉnh sửa file config/app.php mặc định của CakePHP để cài đặt kết nối với database của bạn .

Điều hướng đến folder ứng dụng:

  • cd /var/www/example.com/html/cake-blog

Mở file config/app.php bằng cách chạy lệnh sau:

  • sudo nano config/app.php

Tìm khối Datasources (trông giống như sau):

/var/www/example.com/html/cake-blog/config/app.php
...     'Datasources' => [         'default' => [             'className' => 'Cake\Database\Connection',             'driver' => 'Cake\Database\Driver\Mysql',             'persistent' => false,             'host' => 'localhost',             ...             //'port' => 'non_standard_port_number',             'username' => 'cake_user',             'password' => 'password',             'database' => 'cakephp_blog', ... 

Đối với 'username' thay thế my_app bằng tên user của user database của bạn (hướng dẫn này sử dụng: cake_user ), secret với password của user database của bạn và my_app thứ hai với tên database ( cakephp_blog trong hướng dẫn này).

Lưu và đóng file .

Làm mới ứng dụng trong trình duyệt của bạn và quan sát thông báo thành công trong phần Database . Nếu nó hiển thị lỗi, hãy kiểm tra lại file cấu hình của bạn theo các bước trước đó.

CakePHP có thể kết nối với database

Trong bước này, bạn đã kết nối ứng dụng CakePHP với database MySQL của bạn . Trong bước tiếp theo, bạn sẽ tạo các file mô hình, chế độ xem và bộ điều khiển sẽ tạo nên giao diện user để tương tác với các bài viết.

Bước 6 - Tạo Giao diện User Bài viết

Trong phần này, bạn sẽ tạo ra một giao diện điều sẵn sàng để sử dụng bằng cách chạy CakePHP bake lệnh, tạo ra các mô hình bài viết. Trong CakePHP, nướng tạo ra tất cả các mô hình, khung nhìn và bộ điều khiển được yêu cầu ở trạng thái cơ bản, sẵn sàng để phát triển thêm. Mọi ứng dụng database phải cho phép các hoạt động tạo, đọc, cập nhật và xóa (CRUD), điều này làm cho tính năng bake của CakePHP hữu ích để tự động tạo mã cho các hoạt động này. Trong vòng vài phút, bạn sẽ có được một nguyên mẫu đầy đủ của ứng dụng, sẵn sàng để nhập, lưu trữ và chỉnh sửa dữ liệu.

Mô hình, chế độ xem và bộ điều khiển liên quan đến mẫu MVC . Role của họ là:

  • Mô hình đại diện cho cấu trúc dữ liệu.
  • Chế độ xem trình bày dữ liệu theo cách thân thiện với user .
  • Bộ điều khiển hoạt động theo yêu cầu của user và đóng role trung gian giữa chế độ xem và mô hình.

CakePHP lưu trữ file thực thi CLI của nó dưới bin/cake . Trong khi nó chủ yếu được sử dụng để nướng, nó cung cấp một loạt các lệnh khác, chẳng hạn như các lệnh để xóa các bộ nhớ đệm khác nhau.

Lệnh bake sẽ kiểm tra database của bạn và tạo ra các mô hình dựa trên các định nghĩa bảng mà nó tìm thấy. Bắt đầu bằng cách chạy lệnh sau:

  • ./bin/cake bake all

Bằng cách chuyển lệnh all , bạn đang hướng dẫn CakePHP tạo các mô hình, bộ điều khiển và khung nhìn cùng một lúc.

Đầu ra của bạn sẽ giống như sau:

Output
Bake All --------------------------------------------------------------- Possible model names based on your database: - articles Run `cake bake all [name]` to generate skeleton files.

Nó đã phát hiện đúng định nghĩa articles từ database của bạn và đang cung cấp để tạo file cho mô hình đó.

Nướng nó bằng lệnh:

  • ./bin/cake bake all articles

Đầu ra của bạn sẽ giống như sau:

Output
Bake All --------------------------------------------------------------- One moment while associations are detected. Baking table class for Articles... Creating file /var/www/example.com/html/cake-blog/src/Model/Table/ArticlesTable.php Wrote `/var/www/example.com/html/cake-blog/src/Model/Table/ArticlesTable.php` Deleted `/var/www/example.com/html/cake-blog/src/Model/Table/empty` Baking entity class for Article... Creating file /var/www/example.com/html/cake-blog/src/Model/Entity/Article.php Wrote `/var/www/example.com/html/cake-blog/src/Model/Entity/Article.php` Deleted `/var/www/example.com/html/cake-blog/src/Model/Entity/empty` Baking test fixture for Articles... Creating file /var/www/example.com/html/cake-blog/tests/Fixture/ArticlesFixture.php Wrote `/var/www/example.com/html/cake-blog/tests/Fixture/ArticlesFixture.php` Deleted `/var/www/example.com/html/cake-blog/tests/Fixture/empty` Bake is detecting possible fixtures... Baking test case for App\Model\Table\ArticlesTable ... Creating file /var/www/example.com/html/cake-blog/tests/TestCase/Model/Table/ArticlesTableTest.php Wrote `/var/www/example.com/html/cake-blog/tests/TestCase/Model/Table/ArticlesTableTest.php` Baking controller class for Articles... Creating file /var/www/example.com/html/cake-blog/src/Controller/ArticlesController.php Wrote `/var/www/example.com/html/cake-blog/src/Controller/ArticlesController.php` Bake is detecting possible fixtures... ... Baking `add` view template file... Creating file /var/www/example.com/html/cake-blog/src/Template/Articles/add.ctp Wrote `/var/www/example.com/html/cake-blog/src/Template/Articles/add.ctp` Baking `edit` view template file... Creating file /var/www/example.com/html/cake-blog/src/Template/Articles/edit.ctp Wrote `/var/www/example.com/html/cake-blog/src/Template/Articles/edit.ctp` Bake All complete.

Trong kết quả kết quả , bạn sẽ thấy CakePHP đã ghi lại tất cả các bước đã thực hiện để tạo bảng soạn sẵn chức năng cho database articles .

Bây giờ, chuyển đến phần sau trong trình duyệt của bạn:

https://your_domain/articles 

Bạn sẽ thấy danh sách các bài báo hiện có trong database , bao gồm một hàng có tiêu đề Mẫu . Lệnh bake đã tạo ra giao diện này cho phép bạn tạo, xóa và chỉnh sửa các bài báo. Như vậy, nó cung cấp một điểm khởi đầu vững chắc để phát triển hơn nữa. Bạn có thể thử thêm một bài viết mới bằng cách nhấp vào liên kết Bài viết mới trong thanh bên.

Giao diện  user  bài viết đã tạo

Trong phần này, bạn tạo ra mô hình, xem, và các file điều khiển với CakePHP của bake lệnh. Đến đây bạn có thể tạo, xóa, xem và chỉnh sửa các bài viết của bạn , với tất cả các thay đổi được lưu ngay lập tức vào database .

Trong bước tiếp theo, bạn sẽ tắt chế độ gỡ lỗi.

Bước 7 - Tắt chế độ gỡ lỗi trong CakePHP

Trong phần này, bạn sẽ tắt chế độ gỡ lỗi trong CakePHP. Điều này rất quan trọng vì trong chế độ gỡ lỗi, ứng dụng hiển thị thông tin gỡ lỗi chi tiết, đây là một rủi ro bảo mật. Bạn sẽ hoàn thành bước này sau khi hoàn thành việc phát triển ứng dụng của bạn .

Mở file config/app.php bằng editor yêu thích của bạn:

  • sudo nano config/app.php

Gần đầu file sẽ có một dòng cho chế độ 'debug' . Khi bạn mở file 'debug' chế độ 'debug' sẽ được đặt thành true . Thay đổi điều này thành false như sau:

config / app.php
... 'debug' => filter_var(env('DEBUG', false), FILTER_VALIDATE_BOOLEAN), ... 

Khi bạn đã tắt chế độ gỡ lỗi, trang chủ, nằm trong src/Templates/Pages/home.ctp , sẽ hiển thị lỗi.

Lỗi chế độ gỡ lỗi

Lưu ý: Nếu bạn chưa thay đổi tuyến mặc định hoặc thay thế nội dung của home.ctp , trang chủ ứng dụng của bạn sẽ hiển thị lỗi. Điều này là do trang chủ mặc định đóng role như một console trạng thái trong quá trình phát triển, nhưng không hoạt động với chế độ gỡ lỗi bị tắt.

Bạn đã tắt chế độ gỡ lỗi. Bất kỳ lỗi và ngoại lệ nào xảy ra từ bây giờ, cùng với dấu vết ngăn xếp của chúng, sẽ không được hiển thị cho user cuối, thắt chặt bảo mật cho ứng dụng của bạn.

Tuy nhiên, sau khi tắt chế độ gỡ lỗi, home.ctp của bạn sẽ hiển thị lỗi. Nếu bạn đã hoàn thành bước này chỉ cho mục đích của hướng dẫn này, bây giờ bạn có thể chuyển hướng trang chủ của bạn đến giao diện danh sách bài viết trong khi vẫn tắt chế độ gỡ lỗi. Bạn sẽ đạt được điều này bằng cách chỉnh sửa nội dung của home.ctp .

Mở home.ctp để chỉnh sửa:

  • sudo nano src/Template/Pages/home.ctp

Thay thế nội dung của nó bằng những thứ sau:

src / Template / Pages / home.ctp
<meta http-equiv="refresh" content="0; url=./Articles" /> <p><a href="./Articles">Click here if you are not redirected</a></p> 

HTML này chuyển hướng đến bộ Articles khiển Articles . Nếu chuyển hướng tự động không thành công, cũng có một liên kết để user theo dõi.

Trong bước này, bạn đã tắt chế độ gỡ lỗi vì mục đích bảo mật và sửa lỗi của trang chủ bằng cách chuyển hướng user đến giao diện danh sách bài đăng trên blog mà bộ Articles khiển Articles cung cấp.

Kết luận

Đến đây bạn đã cài đặt thành công ứng dụng CakePHP trên LAMP trên Ubuntu 18.04. Với CakePHP, bạn có thể tạo database với bao nhiêu bảng tùy thích và nó sẽ tạo ra một editor web trực tiếp cho dữ liệu.

Sách dạy nấu ăn CakePHP cung cấp tài liệu chi tiết về mọi khía cạnh của CakePHP. Bước tiếp theo cho ứng dụng của bạn có thể bao gồm triển khai xác thực user để mọi user có thể tạo các bài viết của riêng họ.


Tags:

Các tin liên quan

Cách đặt quota hệ thống tệp trên Ubuntu 18.04
2019-02-21
Cách thực hiện kiểm tra liên tục các vai trò không thể phục hồi bằng Molecule và Travis CI trên Ubuntu 18.04
2019-02-01
Cách đảm bảo chất lượng mã bằng SonarQube trên Ubuntu 18.04
2019-01-11
Cách cài đặt và bảo mật Memcached trên Ubuntu 18.04
2019-01-04
Cách cài đặt Elasticsearch, Logstash và Kibana (Elastic Stack) trên Ubuntu 16.04
2018-11-20
Cách cài đặt Elasticsearch, Logstash và Kibana (Elastic Stack) trên Ubuntu 16.04
2018-11-20
Cách cài đặt Elasticsearch, Logstash và Kibana (Elastic Stack) trên Ubuntu 18.04
2018-11-06
Cách triển khai ứng dụng Symfony 4 để sản xuất với LEMP trên Ubuntu 18.04
2018-10-18
Cách cài đặt và sử dụng Composer trên Ubuntu 18.04
2018-10-16
Làm thế nào để kiểm tra các vai trò không thể phục hồi với Molecule trên Ubuntu 18.04
2018-10-02