Senin, 09 September 2024

Kastemisasi tampilan terminal linux menjadi keren




Install Oh My Posh
langkah pertama anda dapat menginstal Oh My Posh dan Unzip agar dapat mengekstraknya, ketikkan perintah di terminal.

sudo wget https://github.com/JanDeDobbeleer/oh-my-posh/releases/latest/download/posh-linux-amd64 -O /usr/local/bin/oh-my-posh
sudo chmod +x /usr/local/bin/oh-my-posh
sudo apt install unzip

Selanjutnya Anda perlu mendownload temanya
mkdir ~/.poshthemes
wget https://github.com/JanDeDobbeleer/oh-my-posh/releases/latest/download/themes.zip -O ~/.poshthemes/themes.zip
unzip ~/.poshthemes/themes.zip -d ~/.poshthemes
chmod u+rw ~/.poshthemes/*.json
rm ~/.poshthemes/themes.zip
Instal Nerd Font
Anda perlu menggunakan Nerd Font untuk menampilkan semua ikon khusus yang bagus. Anda dapat mendownloadnya langsung dari sini. Simpan saja di komputer Anda, unzip dan instal.
Kemudian, Anda perlu mengatur font ini dari pengaturan terminal.

Aktivasi Bashrc
Sekarang Anda perlu menambahkan beberapa kode ke file bashrc Anda; tema mana yang ingin Anda gunakan di Oh My Posh akan ditunjukkan. Itu adalah langkah penting untuk penyesuaian terminal.

Buka file bashrc Anda dengan menjalankan perintah di bawah ini:
nano ~/.bashrc

Dan tambahkan dua baris berikut di bawahnya:
# Oh My Posh Theme Config
eval "$(oh-my-posh --init --shell bash --config '~/.poshthemes/atomic.omp.json')"

Sekarang tekan ctrl + o dan enter untuk menyimpan file dan ctrl + x untuk menutup file anda.
Terakhir anda perlu mengaktifkan tema dengan menjalankan perintah di bawah ini:
source ~/.bashrc

Senin, 02 September 2024

Membuat restful api sederhana dengan framework actix web dan database mysql

 Sebelum kita mulai koding alangkah baiknya kita baca basmallah dalam hati.

Yuk kita intip langkah - langkah apa saja yang perlu disiapkan dalam membuat restful api ini.

- Pertama kalian harus install rust di dalam komputer masing - masing buka linknya di sini https://www.rust-lang.org/tools/install

- Kedua setelah selesai penginstalan kita mulai pembuatan projeknya ya, ketikkan perintah di dalam terminal. " cargo new nama projek"

- Ketiga siapkan database mysql dengan nama blog_db dan  buat tabel baru di dalam database tersebut misalnya posts. Isi tabel tersebut ada id, title dan content

- Keempat , langkah ini barulah kita buatkan script atau kodenya. Di file main.rs isi kodenya seperti ini ya:

mod handler;
mod model;
mod schema;




use actix_web::middleware::Logger;
use actix_web::{web, App, HttpServer};
use sqlx::mysql::{MySqlPool, MySqlPoolOptions};
use dotenv::dotenv;

pub struct AppState {
    db: MySqlPool,
}


#[actix_web::main]
async fn main() -> std::io::Result<()>{
    if std::env::var_os("RUST_LOG").is_none() {
        std::env::set_var("RUST_LOG", "actix_web=info")
    }
    dotenv().ok();
    env_logger::init();

    let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");
    let pool = match MySqlPoolOptions::new()
            .max_connections(10)
            .connect(&database_url)
            .await
    {
        Ok(pool) => {
            println!("✅Connection to the database is successful!");
            pool
        },
        Err(err) => {
            println!("🔥 Failed to connect to the database: {:?}", err);
            std::process::exit(1);
        }  
    };

    println!("🚀 Server started successfully");

    HttpServer::new(move || {
        App::new()
        .app_data(web::Data::new(AppState { db : pool.clone()}))
        .wrap(Logger::default())
        .service(handler::get_post)
        .service(handler::create_post)
        .service(handler::get_single_post)
        .service(handler::edit_post)
        .service(handler::delete_post)
       
    })
    .bind(("127.0.0.1", 5050))?
            .run()
            .await

}

selain file main.rs ada beberapa file yang perlu dipersiapkan diantaranya: handler.rs, model.rs dan schema.rs. 

Di dalam file handler.rs memiliki tugas untuk menjalankan segala service yang dibutuhkan dalam mengolah data untuk kodenya bisa dilihat di bawah ini.

use actix_web::{delete, get, patch, post, web, HttpResponse, Responder};
use serde_json::json;
use crate::{
    model::{Post, PostResponse}, schema::{CreatePostSchema, FilterOptions}, AppState
};


fn filter_db_record(post: &Post) -> PostResponse {
    PostResponse {
        id: post.id.to_owned(),
        title: post.title.to_owned(),
        content: post.content.to_owned(),
    }
}

#[get("/api/posts")]
pub async fn get_post(opt: web::Query<FilterOptions>,config: web::Data<AppState>)-> impl Responder{
    let limit = opt.limit.unwrap_or(10);
    let offset = (opt.page.unwrap_or(1) - 1) * limit;
    let posts = sqlx::query_as!(Post, r#"SELECT id, title, content FROM posts ORDER BY id DESC LIMIT ? OFFSET ?"#, limit as i32, offset as i32)
        .fetch_all(&config.db)
        .await
        .unwrap();

    let post_resp = posts
                            .into_iter()
                            .map(|post| filter_db_record(&post))
                            .collect::<Vec<PostResponse>>();
    let response = serde_json::json!({
        "status" : "success",
        "posts" : post_resp
    });

    HttpResponse::Ok().json(response)
}

#[post("/api/posts")]
pub async fn create_post(body: web::Json<CreatePostSchema>, config: web::Data<AppState>) -> impl Responder{
    sqlx::query!(r#"INSERT INTO posts(title, content) VALUES(?, ?)"#, body.title, body.content)
        .execute(&config.db)
        .await;

    let query_result = sqlx::query_as!(Post ,r#"SELECT id, title, content FROM posts WHERE title =?"#, body.title)
        .fetch_one(&config.db)
        .await;

    match query_result {
        Ok(value) => {
            let post_response = serde_json::json!({"status": "success", "data": serde_json::json!({"post": filter_db_record(&value)})});

            return HttpResponse::Ok().json(post_response);
        },
        Err(err) => {
            return HttpResponse::InternalServerError().json(serde_json::json!({
                "status": "error",
                "message": format!("{:?}", err)
            }));
        }
    }
}

#[get("/api/posts/{id}")]
pub async fn get_single_post(path: web::Path<i32>, config: web::Data<AppState>) -> impl Responder{
    let post_id = path.into_inner().to_string();
    let query_result = sqlx::query_as!(Post, r#"SELECT id, title, content FROM posts WHERE id=?"#, post_id)
                    .fetch_one(&config.db)
                    .await;

    match query_result {
        Ok(post) => {
            let post_response = serde_json::json!({"status": "success", "data": serde_json::json!({"post": filter_db_record(&post)})});

            return HttpResponse::Ok().json(post_response)
        },
        Err(err) => {
            return HttpResponse::InternalServerError()
                    .json(serde_json::json!({"status": "error", "message": format!("{:?}", err)}));
        }
    }
}

#[patch("/api/posts/{id}")]
pub async fn edit_post(path: web::Path<i32>, body: web::Json<CreatePostSchema>, config: web::Data<AppState>)-> impl Responder{
    let post_id = path.into_inner().to_string();

    let update_result = sqlx::query(r#"UPDATE posts SET title = ?, content = ? WHERE id= ?"#)
    .bind(body.title.to_owned())
    .bind(body.content.to_owned())
    .bind(post_id.to_owned())
    .execute(&config.db)
    .await;
   
    match  update_result{
        Ok(value) => {
            if value.rows_affected() == 0 {
                let message = format!("Post with ID: {} not found", post_id);
                return HttpResponse::NotFound().json(json!({"status": "fail", "message": message}));
            }
        },
        Err(err) => {
            let message = format!("Internal server error: {}", err);
            return HttpResponse::InternalServerError().json(json!({"status": "error", "message": message}));
        }
    }

    let _result = sqlx::query_as!(Post, r#"SELECT id, title, content FROM posts WHERE id=?"#, post_id.to_owned())
            .fetch_one(&config.db)
            .await;
    match _result {
        Ok(row) => {
            let note_response = serde_json::json!({"status": "success","data": serde_json::json!({
                "note": filter_db_record(&row)
            })});

            HttpResponse::Ok().json(note_response)
        }
        Err(e) => HttpResponse::InternalServerError()
            .json(serde_json::json!({"status": "error","message": format!("{:?}", e)})),
    }
}

#[delete("/api/posts/{id}")]
pub async fn delete_post(path: web::Path<i32>, config: web::Data<AppState>) -> impl Responder{
    let post_id = path.into_inner().to_string();
    let query_result = sqlx::query!(r#"DELETE FROM posts WHERE id = ?"#, post_id )
                .execute(&config.db).await;

    match  query_result {
        Ok(result) => {
            if result.rows_affected() == 0 {
                let message = format!("Post with ID: {} not found", post_id);
                return HttpResponse::NotFound().json(json!({"status": "fail", "message": message}));
            } else {
                return HttpResponse::Ok().json(json!({"status": "success", "message": "Delete record successfully"}));
            }
        },
        Err(err) => {
            return HttpResponse::InternalServerError().json(json!({"status": "error", "message": format!("Internal server error {:?}", err)}));
        }
    }
}

Selanjutnya di dalam file model berisi field yang dibutuhkan di dalam tabel posts:

use serde::{Deserialize, Serialize};

#[derive(Debug, sqlx::FromRow, Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct  Post {
    pub id: i32,
    pub title: Option<String>,
    pub content: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct  PostResponse {
    pub id: i32,
    pub title: Option<String>,
    pub content: Option<String>,
}

Kemudian file schema.rs memiliki beberapa parameter atau inputan query yang digunakan di setiap endpointnya

use serde::{Deserialize, Serialize};

#[derive(Deserialize, Debug)]
pub struct FilterOptions {
    pub page: Option<usize>,
    pub limit: Option<usize>,
}

#[derive(Deserialize, Debug)]
pub struct ParamOptions {
    pub id: String,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct CreatePostSchema{
    pub title: Option<String>,
    pub content: Option<String>,
}

ada 2 file yang gak boleh kelewat ya .env dan cargo.toml . masing - masing dari file tersebut memiliki tugas yang berbeda - beda. Jika file .env bertugas untuk konfigurasi koneksi database mysql sedangkan cargo.toml berisi modul - modul program yang  yang dibutuhkan dalam pembuatan projek tersebut.

Settingan dari koneksi database mysql di dalam file .env sangat mudah sekali yaitu:

MYSQL_USER= root
MYSQL_DATABASE=blog_db
MYSQL_ROOT_PASSWORD=password
MYSQL_PASSWORD=password
DATABASE_URL=mysql://${MYSQL_USER}:${MYSQL_ROOT_PASSWORD}@localhost:3306/${MYSQL_DATABASE}

Tambahkan modul atau library di file cargo.toml ya

[dependencies]
sqlx = { version = "0.8", features = ["mysql", "runtime-async-std"]}
actix-web = "4.9.0"
mysql = "25.0.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
chrono = { version = "0.4", features = ["serde"] }
derive_more = "0.99"
env_logger = "0.11"
isahc = "1.7"
log="0.4"
dotenv = "0.15.0"

Jika semua kode sudah dikerjakan dengan benar di dalam projek anda maka anda bisa melakukan uji coba api lewat curl saja.
1. Method Post : Create new a record
    curl -H 'Content-Type: application/json' \
      -d '{ "title": "lorem ipsum", "content" : "lorem ipsum dolar sit ae"}' \
      -X POST \
      http://127.0.0.1:5050/api/posts
2. Method Get : singgle record
    curl -H 'Content-Type: application/json' \
      -X GET  http://127.0.0.1:5050/api/posts/6
3. Method Get : All records
    curl -H 'Content-Type: application/json' \
    -X GET http://127.0.0.1:5050/api/posts
4. Method Patch : update record by id
    curl -H 'Content-Type: application/json' \
      -d '{ "title": "Hello coders", "content" : "Hello coders, how are you?"}' \
      -X PATCH \
      http://127.0.0.1:5050/api/posts/6
5. Method Delete : delete record
    curl -H 'Content-Type: application/json' \
      -X DELETE http://127.0.0.1:5050/api/posts/6

Begitulah tahapan demi tahapan dalam pembuatan simpel restful api menggunakan bahasa pemrograman rust alias framework actix web. Semoga pembelajaran ini bisa bermanfaat di kemudian hari. Selamat mencobanya ya....heeppiii coding brooo.


Kastemisasi tampilan terminal linux menjadi keren

Install Oh My Posh langkah pertama anda dapat menginstal Oh My Posh dan Unzip agar dapat mengekstraknya, ketikkan perintah di terminal. sudo...