Menu

Thursday, September 16, 2021

[Database] ฐานข้อมูลจังหวัด อำเภอ ตำบล และรหัสไปรษณีย์ของไทย
[Database] Province, District (Amphur), Sub District (Tambon) and Postal Code (Zip Code) database of Thailand

Original Version

ฐานข้อมูลจังหวัด อำเภอ ตำบล และรหัสไปรษณีย์ของไทย ข้อมูลเดิมอ้างอิงจาก

https://raw.githubusercontent.com/Cerberus/Thailand-Address/master/thailand-address.sql

http://thai-db-download.blogspot.com/2015/02/devscriptcase-sql.html

ดาวน์โหลดได้ที่

https://raw.githubusercontent.com/arzeroid/code2now/master/sql/zipcode/thailand_with_zipcode%20(dirty).sql

หมายเหตุ ฐานข้อมูลนี้มีทั้งข้อมูลเก่าและใหม่ของตำบลและอำเภอรวมกันอยู่ โปรดตรวจสอบอีกครั้งก่อนใช้งาน

Cleansing Version

ฐานข้อมูลจังหวัด อำเภอ ตำบล และรหัสไปรษณีย์ของไทยที่มีการ clean data เอาข้อมูลเก่าออกให้เหลือแต่ข้อมูลปัจจุบัน

ดาวน์โหลดได้ที่

https://raw.githubusercontent.com/arzeroid/code2now/master/sql/zipcode/thailand_with_zipcode%20(clean).sql

Tuesday, August 24, 2021

[GIT] วิธีการ Clone หรือ Push Repository โดยใช้ Personal Access Token
[GIT] How to Clone or Push to a Repository using Personal Access Token

โดยปกติแล้ว เราจะใช้วิธี authenticate ดังต่อไปนี้ เพื่อใช้งาน Git Repository 
นอกเหนือจากวิธีข้างต้นแล้ว เรายังมีวิธี authenticate ด้วย Personal Access Token ในการเข้าใช้ Git Repository ด้วย 

สำหรับวิธีการสร้าง Personal Access Token สามารถอ่านได้ที่ Personal access tokens

Monday, August 16, 2021

[PHP - Laravel] วิธีการสร้าง Class Diagram ที่มี Caller Graph จาก Source Code ที่เป็น Laravel
[PHP - Laravel] How to Generate Class Diagram with Caller Graph from Laravel Source Code

เราจะใช้ Doxygen ร่วมกับ Graphviz ในการสร้าง Class diagram ที่มี Caller Graph 

Doxygen เป็นเครื่องมือที่ใช้สร้างเอกสารจาก source code ที่มีอยู่ ภาษาโปรแกรมที่ support เช่น PHP, Java, Python เป็นต้น

Graphviz เป็น open source software ที่ใช้สร้างและแสดงผลกราฟรูปแบบต่างๆ

Doxygen จะเรียกใช้ dot ของ Graphviz สำหรับสร้างเอกสารในส่วนของ caller graph

การติดตั้ง Doxygen และ Graphviz

  1. ดาวน์โหลด Doxygen จาก https://www.doxygen.nl/download.html และติดตั้งตามปกติ
  2. ดาวน์โหลด Graphviz จาก https://graphviz.org/download/ และติดตั้งตามปกติ
  3. เพิ่ม C:\Program Files\Graphviz\bin เข้าไปที่ System path

Thursday, July 8, 2021

[Docker - MySQL] วิธีการนำเข้าข้อมูลจากไฟล์ไปยังฐานข้อมูล MySQL ใน Docker Container
[Docker - MySQL] How to Import SQL File to MySQL Database in Docker Container

เราจะใช้คำสั่งดังนี้

sudo docker exec -it [container_id_or_name] mysql -u[mysql_username] -p[mysql_password] --default-character-set=utf8 [database_name] < [sql_file]
โดยที่
  • container_id_or_name เป็น ID หรือชื่อของ container
  • mysql_username เป็นชื่อผู้ใช้งานของ MySQL
  • mysql_password เป็นรหัสของผู้ใช้งานนั้น
  • database_name เป็นชื่อฐานข้อมูลที่ต้องการนำเข้าข้อมูล
  • sql_file เป็นชื่อไฟล์ SQL script สำหรับนำเข้าข้อมูล
อ่านเพิ่มเติมได้ที่ https://blog.code2now.com/2020/04/database-mysql-backup-restore-mysql.html

Monday, July 5, 2021

[GIT] การตั้งค่า Deploy Key ใน Gitlab (อัพเดต 20210705)
[GIT] How to Set Up Deploy Key in Gitlab (Updated 20210705)

การตั้งค่า deploy key เป็นการตั้งค่า SSH key เฉพาะโปรเจคและใช้เป็น key สำหรับการ deploy เท่านั้น โดยปกติแล้ว key นี้จะอนุญาตให้ clone หรือ pull code จาก repository เท่านั้น ไม่อนุญาตให้ทำการ push code ที่แก้ไขขึ้นมายัง repository

สมมติว่า เราสร้าง SSH key ในเครื่อง server ของเราเรียบร้อยแล้ว (ข้อมูลเพิ่มเติม: การสร้าง SSH Key ใหม่)
  1. Login เข้า Gitlab
  2. เข้าไปที่โปรเจคของเรา
  3. เลือกเมนู Settings
  4. เลือกเมนู Repository
  5. เลื่อนลงไปยังส่วนของ Deploy Keys แล้วกดปุ่ม Expand
  6. กรอกข้อมูล Title เพื่อระบุว่า key นี้เป็นของเครื่องใด
  7. คัดลอกข้อมูลภายใน Public key ของ SSH แล้ววางลงในช่อง Key
  8. ไม่ต้องเลือกช่อง Write access allowed
  9. กดปุ่ม Add key
เพียงเท่านี้ เราก็สามารถใช้ Clone with SSH และ pull code จาก Gitlab ด้วย SSH key ของ server เราได้แล้ว โดยไม่จำเป็นต้องใส่ username และ password

Monday, June 28, 2021

[Java - Spring Boot] Exception Handler ใน Spring Boot
[Java - Spring Boot] Exception Handler in Spring Boot

สำหรับ Exception Handler เราจะสร้าง class ที่ใช้ @ControllerAdvice ดังนี้
package com.example.exception;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import java.util.LinkedHashMap;

@ControllerAdvice
public class CustomExceptionHandler   {

    @ExceptionHandler(value = {ExceptionWrapper.class})(value = {exception_class.class})
    public ResponseEntity<Object> exceptionHandler(exception_class ex) {
    	// Handle specific exception then return ResponseEntity
        return new ResponseEntity(ex,HttpStatus.BAD_REQUEST);
    }
}
      
โดยที่ exception_class เป็นชื่อของ Exception Class ที่ต้อง handle 

คำอธิบายเพิ่มเติม
  • @ControllerAdvice - อนุญาตให้เรา apply เทคนิคใน class ข้างใต้มันกับทุกๆ controller ของเรา (อ่านเพิ่มเติมที่ ControllerAdvice)
  • @ExceptionHandler - ใช้เพื่อระบุว่า method ข้างใต้มันเป็น Exception handling methods (อ่านเพิ่มเติมที่ ExceptionHandler)

[Java - Spring Boot] วิธีการสร้าง JSON response สำหรับทุก controller ใน Spring Boot
[Java - Spring Boot] How to Wrap JSON Response for All Controllers in Spring Boot

หลังจากเรากำหนด @RestController ที่เหนือ class ของ controller ทำให้ object ที่ return จาก controller นั้นจะถูกแปลงไปเป็น JSON response ให้โดยอัตโนมัติ

อย่างไรก็ตาม ถ้าหากเราต้องการสร้าง template ครอบ object ที่ return จาก controller ทุกๆ controller ก่อนที่จะส่งเป็น JSON response ออกไป เรามีขั้นตอนดังนี้

  1. สร้าง Class สำหรับ JSON response template
    package com.example.model.wrapper;
    
    import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
    import com.fasterxml.jackson.annotation.JsonInclude;
    import com.fasterxml.jackson.annotation.JsonProperty;
    import org.springframework.http.HttpStatus;
    
    @JsonIgnoreProperties(ignoreUnknown = true)
    @JsonInclude(JsonInclude.Include.NON_NULL)
    public class ResultWrapper<T> {
        @JsonProperty("code")
        private Integer code;
        @JsonProperty("status")
        private HttpStatus status;
        @JsonProperty("message")
        private String message;
        @JsonProperty("data")
        private T data = null;
    
    
        public Result(Integer code, HttpStatus status, String message) {
            this.code = code;
            this.status = status;
            this.message = message;
        }
    
        public ResultWrapper(Integer code, HttpStatus status, T data) {
            this.code = code;
            this.status = status;
            this.data = data;
        }
    }
    

Monday, June 14, 2021

[Laravel - Composer] วิธีการตั้งค่า Authentication token ของ Github ใน Composer
[Laravel - Composer] How to Set Github Authentication Token in Composer

หลังจากรัน composer install เพื่อติดตั้ง package ที่จำเป็นต้องใช้งาน แล้วได้ข้อความตามข้างล่างนี้

Head to https://github.com/settings/tokens/new?scopes=repo&description=Composer+on+MACHINE-NAME+2021-02-17+0532
to retrieve a token. It will be stored in "C:/Users/puthipong/AppData/Roaming/Composer/auth.json" for future use by Composer.
Token (hidden): GitHub API limit (0 calls/hr) is exhausted, could not fetch https://api.github.com/repos/test/l5-repository/contents/composer.json?ref=ce164fa72b884b4af488ff2749a171346f229c66. Create a GitHub OAuth token to go over the API rate limit. You can also wait until ? for the rate limit to reset.

สิ่งที่เราต้องทำมีดังนี้
  1. เข้าไปที่ URL ตามข้อความข้างต้น
    https://github.com/settings/tokens/new?scopes=repo&description=Composer+on+MACHINE-NAME+2021-02-17+0532
  2. กดสร้าง token 
  3. รันคำสั่งข้างล่างนี้
    composer config --global github-oauth.github.com <token>
หมายเหตุ สำหรับข้อมูลเพิ่มเติม อ่านได้ที่ https://getcomposer.org/doc/articles/authentication-for-private-packages.md#github-oauth

Thursday, March 18, 2021

[NodeJS] วิธีการนำเข้าไฟล์ JSON ไปยังตัวแปรและการส่งออกค่าตัวแปรไปยังไฟล์ JSON (การอ่านและเขียนไฟล์ JSON)
[NodeJS] How to Import JSON File to Variable and Export Variable to JSON File (Reading and Writing JSON file to/from Variable)

สมมติว่า เรามีไฟล์ JSON อยู่ที่ ./resources/symbols.json และเราจะใช้ตัวแปรชื่อ symbols ในการเก็บค่าที่อ่านได้จากไฟล์ JSON  
เราสามารถอ่านไฟล์ JSON แล้วเก็บค่าเข้าตัวแปรได้ดังนี้
const symbols = require('./resources/symbols.json');
ในทางกลับกัน ถ้าเราต้องการเขียนค่าของตัวแปรออกมาเป็นไฟล์ JSON เราจะใช้วิธีดังนี้
const fs = require('fs');
const jsonData = JSON.stringify(symbols, null, 4); // third parameter defines white-space insertion for pretty-printing
fs.writeFileSync('./resources/symbols.json', jsonData);

Wednesday, February 17, 2021

[Laravel - Composer] วิธีการใช้ Custom Package พร้อมกับการกำหนด Version ของ Package นั้นในไฟล์ composer.json
[Laravel - Package] How to Use Your Custom Package with Package Version in Composer File

ไฟล์ composer.json เป็นแหล่งรวมรายการ package ที่ต้องใช้ใน Laravel project

บางครั้งเราอาจจะต้อง fork package บางตัวมาจาก repository อื่น เพื่อนำมาปรับใช้กับ project ของเรา

หลังจากที่เราปรับแก้ package แล้ว เรามีวิธีตั้งค่าไฟล์ composer เพื่อเรียกใช้ package จาก branch บน repository ของเราดังนี้
  1. เพิ่มชื่อ package ของเราในส่วน require หรือ require-dev ดังนี้
    "require": {
        ...
        "{package_name}": "dev-{branch_name}",
        ...
    }
    เช่น
    "require": {
        ...
        "monolog/monolog": "dev-fix-concurrent-mkdir",
        ...
    }
  2. เพิ่ม repository ของเราในส่วน repositories ดังนี้
    "repositories": {
        ...
        {
          "type": "vcs",
          "url": "{my_repository_url}"
        },
        ...
    }
    เช่น
    "repositories": {
        ...
        {
          "type": "vcs",
          "url": "https://github.com/my_repo/monolog.git"
        },
        ...
    }

Tuesday, February 9, 2021

[Bash Script] ข้อควรระวัง: ควรใช้ Full Path ของ Command ใน Bash Script
[Bash Script] Beware: Should Use Full Path of Command in Bash Script

เคสนี้เกิดขึ้นหลังจากที่เราเขียน bash script แล้วนำไปรันจริง ปรากฏว่า script นั้นสามารถรันบนเครื่อง UAT ได้ อย่างไม่มีปัญหาอะไร แต่เมื่อนำ script นั้นไปรันบนเครื่อง Production กลับไม่มีอะไรเกิดขึ้น 

สาเหตุที่พบ คือ การสั่งรัน command ใน script ที่เราเขียนนั้น เราเรียก command นั้นเลย โดยไม่ได้ใส่เป็น full path ของ command เอาไว้ มีความเป็นไปได้ว่า script อาจจะไม่สามารถหาที่อยู่ของ command นั้นได้

วิธีแก้ไข คือ เปลี่ยน command ที่ถูกเรียกใช้ใน script เป็น full path ทั้งหมด จึงทำให้ script นี้สามารถรันบนเครื่อง Production ได้ตามปกติ

คำแนะนำ เพื่อหลีกเลี่ยงจากเคสข้างบน เราแนะนำให้ใช้ full path ของ command ทั้งหมด ถ้าหากไม่ทราบ full path อาจจะใช้ which command ช่วย (สามารถอ่าน)

[Bash Script] วิธีการรันคำสั่งที่เก็บอยู่ในตัวแปร
[Bash Script] How to Run Command that Store in Variable

จากโพสต์ [Bash Script] วิธีการประกาศและใช้ตัวแปรใน Bash Script ที่เราสามารถเก็บค่าผลลัพธ์จากการรันคำสั่งอื่นๆใส่ในตัวแปรได้

สมมติว่า เรามีตัวแปร ชื่อ command ที่เก็บค่าดังนี้

command=$(which echo)

ในที่นี้ command จะมีค่าเป็น /bin/echo

ถ้าหากเราต้องการนำค่าของ command ไปเป็นคำสั่งในการรันต่อ เราจะใช้คำสั่งดังนี้

$command "echo_string"

เราจะเห็นได้ว่า command จะมี $ นำหน้า เพื่อแทนที่ตัวแปรนั้นด้วยค่าของมัน แล้วตามด้วย parameter ที่จะส่งเข้าไปยังคำสั่งนั้นๆ ซึ่งบรรทัดข้างบนจะมีค่าเท่ากับ

/bin/echo "echo_string"

Tuesday, February 2, 2021

[GIT] วิธีการเพิ่ม Remote URL ของ GIT Repository
[GIT] How to Add New Remote URL of GIT Repository

 การเปลี่ยน Remote URL ของ GIT Repository จำเป็นต้องใช้ 2 คำสั่งดังนี้

  • คำสั่งสำหรับตรวจสอบ GIT remote URL ปัจจุบัน
    git remote -v
  • คำสั่งสำหรับตั้งค่า Remote URL ของ GIT Repository ใหม่
    git remote add <new_remote_name> <new_git_remote_url>
ตัวอย่างการใช้งาน เช่น
$ git remote -v
origin  git@github.com:username/project.git (fetch)
origin  git@github.com:username/project.git (push)
$ git remote add test git@github.com:username/project2.git
$ git remote -v 
origin  git@github.com:username/project.git (fetch)
origin  git@github.com:username/project.git (push)
test git@github.com:username/project2.git (fetch)
test git@github.com:username/project2.git (push)

ข้อมูลเพิ่มเติม สามารถอ่านได้ที่ Adding a remote

Monday, February 1, 2021

[NodeJS - NestJS] วิธีการตั้งค่า Metadata ที่ Class แทน Handler เพื่อใช้ตรวจสอบสิทธิ์การใช้งาน
[NodeJS - NestJS] How to Set Metadata to Class instead of Handler for Authorization

เราสามารถใช้ Guards ในการตรวจสอบสิทธิ์การใช้งานของผู้ใช้

จากตัวอย่างใน Setting roles per handler เป็นการเช็คสิทธิ์แยกตาม handler ใน controller โดยเราจะตั้งค่า permissions ผ่านทาง @SetMetadata เหนือ handler นั้นๆดังนี้

@SetMetadata('permissions', ['create_user'])
async create(@Body() createUserDto: CreateUserDto) {
    this.userService.create(createUserDto);
}

และมีวิธีเรียกใช้ค่า permissions ใน Guard ดังนี้ 

const permissions = this.reflector.get<string[]>('permissions', context.getHandler());

อย่างไรก็ตาม เราสามารถตั้งค่า roles สำหรับ controller นั้นๆแทน handler ได้ ถ้าหากทุก handler ใน controller นั้นๆ ใช้สิทธิ์เดียวกันทั้งหมด โดยเราจะตั้งค่า permissions ผ่านทาง @SetMetadata เหนือ class ของ controller นั้นๆดังนี้ 

@SetMetadata('permissions', ['create_user'])
export class Controller {
    async create(@Body() createUserDto: CreateUserDto) {
        this.userService.create(createUserDto);
    }
}

และมีวิธีเรียกใช้ค่า permissions ใน Guard ดังนี้ 

const permissions = this.reflector.get<string[]>('permissions', context.getClass()); 

หมายเหตุ เราจะสังเกตได้ว่า parameter ตัวที่ 2 ของ this.reflector.get() มีความแตกต่างกัน ถ้าเราต้องการดึงค่า Metadata จาก handler เราจะใช้ context.getHandler() แต่ถ้าเราต้องการดึงค่า Metadata จาก class เราจะใช้ context.getClass() แทน

Friday, January 29, 2021

[OpenEDX] วิธีแก้ไขข้อผิดพลาด "DoesNotExist: Site matching query does not exist."
[OpenEDX] How to Fix "DoesNotExist: Site matching query does not exist."

กรณีที่เรายังไม่ได้สร้าง Site ที่ชื่อตรงกับ domain ของเรา แล้วเราไปลบ Site ที่มี id เป็น 1 ออกจาก database เวลาเรียกหน้าเว็บจะเกิด error ดังนี้
DoesNotExist: Site matching query does not exist.
เนื่องจากชื่อ domain ของเราจะถูกดึงมาจาก Request header แล้วนำไปเทียบกับ Site ใน database

เมื่อระบบหา Site ที่ตรงกันไม่ได้ ระบบจึงใช้ค่า SITE_ID=1 ใน /edx/app/edxapp/edx-platform/lms/envs/common.py เป็นค่าตั้งต้น

หลังจากนั้น ระบบจะนำค่า SITE_ID ไปหา Site ที่มี id ตรงกัน

เมื่อระบบไม่สามารถหา Site ได้ จึงได้ค่าเป็น None และทำให้เกิดการแจ้งข้อผิดพลาดข้างต้น

วิธีแก้ไข คือ แก้ไข SITE_ID ใน /edx/app/edxapp/edx-platform/lms/envs/common.py เป็น ID ของ Site ที่ต้องการให้เป็นค่าตั้งต้น ถ้าหากยังไม่มี Site เลย ให้ทำการสร้าง Site ก่อน

Monday, January 25, 2021

[PHP - Composer] วิธีการรัน Composer แบบไม่จำกัดหน่วยความจำและไม่ต้องแก้ไขไฟล์ php.ini
[PHP - Composer] How to Run Composer with Unlimited Memory and Do Not Update PHP INI File

เมื่อเรารัน composer install หรือ composer require เราอาจจะเจอกับ error ข้างล่างนี้ ทำให้เราไม่สามารถติดตั้ง package ที่เราต้องการได้

Fatal error: Allowed memory size of 1610612736 bytes exhausted (tried to allocate 4096 bytes) in phar:///usr/local/bin/composer/src/Composer/DependencyResolver/Solver.php on line 223

อีกทั้งการแก้ไขขนาด memory ในไฟล์ php.ini ก็ดูเกินความจำเป็นไป

วิธีการรัน composer โดยไม่จำกัดขนาด memory สามารถรันได้ด้วยคำสั่งดังนี้

COMPOSER_MEMORY_LIMIT=-1 composer require {package_name}

สำหรับข้อมูลเพิ่มเติม อ่านได้ที่ Memory limit errors

Thursday, January 21, 2021

[NodeJS - NestJS] ข้อควรระวัง: Class transformer ไม่ตัด properties ส่วนเกินออกจาก Object ปลายทาง
[NodeJS - NestJS] Caution: Class transformer not stripping additional properties

Class transformer เป็นส่วนประกอบนึงของ NestJS ที่ใช้ในการแปลง JSON object ใดๆเป็น class instance ที่เราต้องการ และยังถูกใช้ในการแปลง request ที่ส่งเข้ามาให้เป็น class ที่เราต้องการโดยอัตโนมัติอีกด้วย

สมมติว่า เรามี function ใน controller ดังนี้
@Post()
async createRole (@Body() req: CreateRoleReqDto): Promise <Role> {
    let role = await this.roleService.createRole(req);
    return role;
}
และ class CreateRoleReqDto สำหรับ request ของเราเป็นดังนี้
export class CreateRoleReqDto{
    name: string;
    description?: string;
    permissionIds?: number[];
}
เมื่อเราส่ง request body ดังนี้
{
    "name":"test3",
    "description":"def",    
    "permissionIds": [1,2]
}
request body ของเราจะถูกแปลงเป็น instance ของ CreateRoleReqDto โดยอัตโนมัติ 

Friday, January 15, 2021

[System - Linux] วิธีการ Monitor การใช้งาน CPU หน่วยความจำและพื้นที่ว่างของฮาร์ดดิสก์แบบ Real Time
[System - Linux] How to Monitor CPU, Memory and Disk Space in Real Time

การ Monitor CPU และ RAM

เราจะใช้ htop 

เมื่อเราติดตั้ง htop เรียบร้อยแล้ว เราสามารถ monitor CPU และ RAM โดยรันคำสั่งดังนี้

htop

การ Monitor พื้นที่ว่างของ Harddisk 

เราจะใช้คำสั่งดังนี้

watch --interval=60 df --human-readable /dev/sda1

โดยที่ /dev/sda1 เป็น partition ที่ต้องการ monitor


หมายเหตุ เราสามารถดูรายการ partition ทั้งหมดได้ โดยใช้คำสั่งดังนี้

sudo lsblk

โดยชื่อ partition จะต้องใส่ /dev/ นำหน้า เช่น /dev/xvda1 ตามชื่อในรูป

[System - Linux] วิธีการตรวจสอบพื้นที่ของดิสก์ ขนาดของแฟ้มข้อมูลและไฟล์
[System - Linux] How to Check Disk Space, Folder Size and File Size

วิธีการตรวจสอบพื้นที่ของดิสก์ เราจะใช้คำสั่งดังนี้
df -h

วิธีการตรวจสอบขนาดของแฟ้มข้อมูล เราจะใช้คำสั่งดังนี้
du -h

วิธีการตรวจสอบขนาดของไฟล์ เราจะใช้คำสั่งดังนี้
ls -la

[System - Linux] วิธีการตรวจสอบข้อมูล Log ของ Cron บน Linux Server แต่ละประเภท
[System - Linux] How to Find Cron Logs on Each Type of Linux Server

Ubuntu และ Debian

ข้อมูล log ของ cron จะถูกรวมอยู่ใน /var/log/syslog 

วิธีการตรวจสอบ log ให้ใช้คำสั่งดังนี้

grep CRON /var/log/syslog

CentOS and Redhat

ข้อมูล log ของ cron จะแยกไว้ที่ /var/log/cron 

วิธีการตรวจสอบ log ให้ใช้คำสั่งดังนี้

tail /var/log/cron